4

This is my first time asking something on this platform, so if form or anything else is off, please feel free to point out things about how I could refine it and ask more concisely in the future.

I'm trying to develop an orthosis, which uses 4 load cells (full-bridge type) and 4 HX711 load cell amplifiers to inform patients via an app when they exceed their prescribed weight limit.

I have test code to calibrate the load cells and the code for the project itself. On the same Arduino, with the same components connected the test code, which only instantiates 4 HX711 objects and reads them after calibration, works fine. The full-blown project code uses exactly the same lines as well as the same hardware setup, but after the 2nd HX711 is instantiated I get no further Serial output and for all I can tell without a debugging jig, it looks like the Arduino stops executing code. If I try running the project code with only two HX711 objects (leaving the others physically connected, but commenting out the respective lines) the code works just fine.

So far I tried measuring RAM consumption, the Arduino should have 2048 bytes available, of which my project code seems to use 1483 bytes. My sensor test code uses 276 bytes, which means that technically I shouldn't be able to run out of memory. I also tried using another Arduino as a power source for the HX711 modules, just in case that the entire setup is asking for too much from a single Arduino - had no luck with that either. I would be really thankful for any pointers in the right direction since I can't figure it out on my own.

I'm using the following library for my HX711 modules: https://github.com/bogde/HX711

Project code (stops working after line 126):

#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>
#include "DHT.h"
#include "RTClib.h"
#include "HX711.h"

#define DHTPIN (uint8_t) 8 #define DHTTYPE DHT11 #define LED (uint8_t) 5 #define BUZZER (uint8_t) 9 #define BAUD 9600 #define NUM_CHARS (byte) 32 #define CHIP_SELECT (uint8_t) 10 #define SENSOR1_DATA (uint8_t) A0 #define SENSOR2_DATA (uint8_t) A2 #define SENSOR3_DATA (uint8_t) A4 #define SENSOR4_DATA (uint8_t) 7 #define SENSOR1_SCK (uint8_t) A1 #define SENSOR2_SCK (uint8_t) A3 #define SENSOR3_SCK (uint8_t) A5 #define SENSOR4_SCK (uint8_t) 6 #define TEST_DURATION 3000 #define NOT_CONNECTED 0 #define LIVE_VIEW 1 #define SETTINGS 2

// Constants const int CHAR_SIZE = sizeof(char);

// Hardware interface settings SoftwareSerial btSerial(2, 3); RTC_DS1307 rtc; DHT dht(DHTPIN, DHTTYPE);

HX711 SENSOR1; HX711 SENSOR2; HX711 SENSOR3; HX711 SENSOR4;

// Hardware status & settings boolean ledEnabled = true; boolean isLedOn = false; boolean buzzerEnabled = true; boolean isBuzzerOn = false; boolean error = false;

float threshold1 = 0.0f; float threshold2 = 0.0f; float threshold3 = 0.0f; float threshold4 = 0.0f;

// Hardware testing boolean ledTestinProgress = false; unsigned long ledTestStart = 0; boolean buzzerTestinProgress = false; unsigned long buzzerTestStart = 0;

// Modes int8_t mode = 0;

// Bluetooth char data[NUM_CHARS]; boolean newData = false;

void receive(); void processMessage(); void testLED(); void testBuzzer(); void loadSettings(); void saveSettings();

extern void* __bss_end; extern void* __brkval;

int get_free_memory() { int free_memory;

if((int)__brkval == 0) free_memory = ((int)&free_memory) - ((int)&__bss_end); else free_memory = ((int)&free_memory) - ((int)__brkval);

return free_memory; }

void setup() { pinMode(LED, OUTPUT); pinMode(BUZZER, OUTPUT); Serial.begin(BAUD); rtc.begin(); if (!SD.begin(CHIP_SELECT)) { Serial.println(F("ERROR: SD init failed.")); error = true;
} else { loadSettings();

    Serial.println(F(&quot;SD initialized.&quot;));
    Serial.println(F(&quot;Settings: &quot;));
    Serial.print(F(&quot;LED: &quot;));
    Serial.println(ledEnabled);
    Serial.print(F(&quot;Buzzer: &quot;));
    Serial.println(buzzerEnabled);
    Serial.print(F(&quot;Threshold 1: &quot;));
    Serial.println(threshold1);
    Serial.print(F(&quot;Threshold 2: &quot;));
    Serial.println(threshold2);
    Serial.print(F(&quot;Threshold 3: &quot;));
    Serial.println(threshold3);
    Serial.print(F(&quot;Threshold 4: &quot;));
    Serial.println(threshold4);

}

dht.begin();

SENSOR1.begin(SENSOR1_DATA, SENSOR1_SCK);
SENSOR1.set_scale(-63.f);
SENSOR1.tare();
Serial.println(F(&quot;Sensor 1 initialized.&quot;)); 

SENSOR2.begin(SENSOR2_DATA, SENSOR2_SCK);
SENSOR2.set_scale(-58.f);
SENSOR2.tare();
Serial.println(F(&quot;Sensor 2 initialized.&quot;));

SENSOR3.begin(SENSOR3_DATA, SENSOR3_SCK);
SENSOR3.set_scale(-50.f);
SENSOR3.tare();
Serial.println(F(&quot;Sensor 3 initialized.&quot;));

SENSOR4.begin(SENSOR4_DATA, SENSOR4_SCK);
SENSOR4.set_scale(-46.f);
SENSOR4.tare();
Serial.println(F(&quot;Sensor 4 initialized.&quot;));      

btSerial.begin(BAUD);

}

void loop() {
if (!error) { receive(); processMessage();

    DateTime now = rtc.now();
    float sensor1 = SENSOR1.get_units() / 1000;
    float sensor2 = SENSOR2.get_units() / 1000;
    float sensor3 = SENSOR3.get_units() / 1000;
    float sensor4 = SENSOR4.get_units() / 1000;

    Serial.print(F(&quot;Sensor 1: &quot;));
    Serial.println(sensor1);
    Serial.print(F(&quot;Sensor 2: &quot;));
    Serial.println(sensor2);
    Serial.print(F(&quot;Sensor 3: &quot;));
    Serial.println(sensor3);
    Serial.print(F(&quot;Sensor 4: &quot;));
    Serial.println(sensor4);

    if (isnan(sensor1) || isnan(sensor2) || isnan(sensor3) || isnan(sensor4)) return;

    if (!buzzerTestinProgress &amp;&amp; !ledTestinProgress) {
        if (threshold1 &gt; 0 &amp;&amp; sensor1 &gt; threshold1 ||
            threshold2 &gt; 0 &amp;&amp; sensor2 &gt; threshold2 ||
            threshold3 &gt; 0 &amp;&amp; sensor3 &gt; threshold3 ||
            threshold4 &gt; 0 &amp;&amp; sensor4 &gt; threshold4) {
            if (buzzerEnabled) 
            {
                tone(BUZZER, 1000);
                isBuzzerOn = true;
            }
            if (ledEnabled) {
                digitalWrite(LED, HIGH);
                isLedOn = true;
            }
        } else {
            if (isBuzzerOn) {
                noTone(BUZZER);
                isBuzzerOn = false;
            }

            if (isLedOn) {
                digitalWrite(LED, LOW);
                isLedOn = false;
            }
        }
    }

    if (mode == LIVE_VIEW) {
        btSerial.print(F(&quot;&lt;&quot;));
        btSerial.print(F(&quot;M;&quot;));
        btSerial.print(now.unixtime());
        btSerial.print(F(&quot;;&quot;));
        if (threshold1 &gt; -1.0f) btSerial.print(sensor1);
        else btSerial.print(-1.0f);
        btSerial.print(F(&quot;;&quot;));
        if (threshold2 &gt; -1.0f) btSerial.print(sensor2);
        else btSerial.print(-1.0f);
        btSerial.print(F(&quot;;&quot;));
        if (threshold3 &gt; -1.0f) btSerial.print(sensor3);
        else btSerial.print(-1.0f);
        btSerial.print(F(&quot;;&quot;));
        if (threshold4 &gt; -1.0f) btSerial.print(sensor4);
        else btSerial.print(-1.0f);
        btSerial.println(F(&quot;&gt;&quot;));
    }

    if (ledTestinProgress &amp;&amp; ((millis() - ledTestStart &gt;= TEST_DURATION))) {
        ledTestStart = 0;
        ledTestinProgress = false;
        digitalWrite(LED, LOW);
    }

    if (buzzerTestinProgress &amp;&amp; ((millis() - buzzerTestStart &gt;= TEST_DURATION))) {
        buzzerTestStart = 0;
        buzzerTestinProgress = false;
        noTone(BUZZER);
    }

    delay(250);
} else {
    tone(BUZZER, 1000);
    digitalWrite(LED, HIGH);
    delay(1000);
    noTone(BUZZER);
    digitalWrite(LED, LOW);
    delay(1000);
}

Serial.print(F(&quot;Free RAM: &quot;));
Serial.println(get_free_memory());

}

// Receive a Bluetooth message from the companion app // Valid messages are encapsulated within '<' and '>' to ensure that the whole message is received. void receive() { static boolean receiving = false; static byte index = 0; char startMarker = '<'; char endMarker = '>'; char rc;

while (btSerial.available() &gt; 0 &amp;&amp; !newData) {
    rc = btSerial.read();
    delay(10);

    if (receiving) {
        if (rc != endMarker) {
            data[index] = rc;
            index++;

            if (index &gt;= NUM_CHARS) {
                index = NUM_CHARS - 1;
            }
        } else {
            data[index] = '\0';
            receiving = false;
            index = 0;
            newData = true;
        }
    } else if (rc == startMarker) {
        receiving = true;
    }
}

}

// Processes a received message based on length and content void processMessage() { if (newData) { int dataLength = strlen(data);

    Serial.println(data);

    if (dataLength == 1) {
        switch (data[0]) {
            case 'l': mode = LIVE_VIEW;
            break;
            case 's': {
                mode = SETTINGS;
                btSerial.print(F(&quot;&lt;S;&quot;));
                btSerial.print(ledEnabled);
                btSerial.print(F(&quot;;&quot;));
                btSerial.print(buzzerEnabled);
                btSerial.print(F(&quot;;&quot;));
                btSerial.print(threshold1);
                btSerial.print(F(&quot;;&quot;));
                btSerial.print(threshold2);
                btSerial.print(F(&quot;;&quot;));
                btSerial.print(threshold3);
                btSerial.print(F(&quot;;&quot;));
                btSerial.print(threshold4);
                btSerial.println(F(&quot;&gt;&quot;));
            }
            break;
            case 'H': btSerial.println(F(&quot;&lt;W&gt;&quot;));
        }
    } else if (dataLength == 2) {
        if (strcmp(data, &quot;LT&quot;) == 0) testLED();
        if (strcmp(data, &quot;BT&quot;) == 0) testBuzzer();
    } else {
        char* token = strtok(data, &quot;;&quot;);
        int count = 0;

        if (strcmp(token, &quot;S&quot;) == 0) {
            while (token != NULL) {
                token = strtok(NULL, &quot;;&quot;);
                if (count == 0) ledEnabled = atoi(token);
                if (count == 1) buzzerEnabled = atoi(token);
                if (count == 2) threshold1 = atof(token);
                if (count == 3) threshold2 = atof(token);
                if (count == 4) threshold3 = atof(token);
                if (count == 5) threshold4 = atof(token);
                count++;
            }

            saveSettings();
        }
    }

    newData = false;
}

}

// Initiate an LED test void testLED() { ledTestStart = millis(); ledTestinProgress = true; digitalWrite(LED, HIGH); }

// Initiate a buzzer test void testBuzzer() { buzzerTestStart = millis(); buzzerTestinProgress = true; tone(BUZZER, 1000); }

// Load settings from SD card void loadSettings() { File settingsFile = SD.open("settings.txt", FILE_READ);

if (settingsFile) {

    char settings[10];
    int index = 0;

    while (settingsFile.available()) {
        settings[index] = settingsFile.read();
        index++;
    }

    settingsFile.close();
    settings[index] = '\0';

    int count = 0;

    char* token = strtok(settings, &quot;;&quot;);
    ledEnabled = atoi(token);

    while (token != NULL) {
        token = strtok(NULL, &quot;;&quot;);
        if (count == 0) buzzerEnabled = atoi(token);
        if (count == 1) threshold1 = atof(token);
        if (count == 2) threshold2 = atof(token);
        if (count == 3) threshold3 = atof(token);
        if (count == 4) threshold4 = atof(token);
        count++;
    }
} else {
    Serial.println(F(&quot;ERROR: Reading settings failed.&quot;));
    error = true;
}

}

// Save settings to SD card void saveSettings() { SD.remove("settings.txt");

File settingsFile = SD.open(&quot;settings.txt&quot;, FILE_WRITE);
if (settingsFile) {
    settingsFile.print(ledEnabled);
    settingsFile.print(&quot;;&quot;);
    settingsFile.print(buzzerEnabled);
    settingsFile.print(&quot;;&quot;);
    settingsFile.print(threshold1);
    settingsFile.print(&quot;;&quot;);
    settingsFile.print(threshold2);
    settingsFile.print(&quot;;&quot;);
    settingsFile.print(threshold3);
    settingsFile.print(&quot;;&quot;);
    settingsFile.print(threshold4);
    settingsFile.println(&quot;;&quot;);

    settingsFile.close();
} else {
    Serial.println(F(&quot;ERROR: Saving settings failed.&quot;));
    error = true;
}

}

Sensor test code (working flawlessly with the same hardware setup):

#include "HX711.h"

#define SENSOR1_DATA (uint8_t) A0 #define SENSOR2_DATA (uint8_t) A2 #define SENSOR3_DATA (uint8_t) A4 #define SENSOR4_DATA (uint8_t) 7 #define SENSOR1_SCK (uint8_t) A1 #define SENSOR2_SCK (uint8_t) A3 #define SENSOR3_SCK (uint8_t) A5 #define SENSOR4_SCK (uint8_t) 6

extern void* __bss_end; extern void* __brkval;

HX711 SENSOR1; HX711 SENSOR2; HX711 SENSOR3; HX711 SENSOR4;

int get_free_memory() { int free_memory;

if((int) __brkval == 0) free_memory = ((int)&free_memory) - ((int)&__bss_end); else free_memory = ((int)&free_memory) - ((int)__brkval);

return free_memory; }

void setup() { Serial.begin(9600); Serial.println(F("Sensor Test"));

SENSOR1.begin(SENSOR1_DATA, SENSOR1_SCK);
SENSOR1.set_scale(-63.f);
SENSOR1.tare();
Serial.println(F(&quot;Sensor 1 initialized.&quot;)); 

SENSOR2.begin(SENSOR2_DATA, SENSOR2_SCK);
SENSOR2.set_scale(-58.f);
SENSOR2.tare();
Serial.println(F(&quot;Sensor 2 initialized.&quot;));

SENSOR3.begin(SENSOR3_DATA, SENSOR3_SCK);
SENSOR3.set_scale(-50.f);
SENSOR3.tare();
Serial.println(F(&quot;Sensor 3 initialized.&quot;));

SENSOR4.begin(SENSOR4_DATA, SENSOR4_SCK);
SENSOR4.set_scale(-46.f);
SENSOR4.tare();
Serial.println(F(&quot;Sensor 4 initialized.&quot;));            

}

void loop() { Serial.print(F("S1: ")); Serial.print(SENSOR1.get_units(), 1); Serial.print(F(" | S2: ")); Serial.print(SENSOR2.get_units(), 1); Serial.print(F(" | S3: ")); Serial.print(SENSOR3.get_units(), 1); Serial.print(F(" | S4: ")); Serial.println(SENSOR4.get_units(), 1); Serial.print(F("Free RAM: ")); Serial.println(get_free_memory());
}

Wiring diagram (Fritzing): Wiring diagram

Changelog:

  • Code is now embedded in the question.
pyrob2142
  • 41
  • 3
  • 1
    If the code is important to the question, it should be brought into the question. That said, it's already better than most first-questions here. – timemage May 22 '21 at 17:57
  • 1
    comment out lines 118-121 ... see if it crashes in a different place – jsotola May 22 '21 at 18:02
  • 1
    @jsotola Interestingly enough it still crashes at the same line, which is unexpected. I figured it would crash after Sensor 3 now, basically shifting the problem back. – pyrob2142 May 22 '21 at 18:26
  • 1
    try changing all four set_scale to -63 – jsotola May 22 '21 at 18:47
  • 1
    maybe something is wrong with sensor 3 ... move the four lines of sensor3 code to before the sensor1 code – jsotola May 22 '21 at 18:51
  • 1
    @jsotola changing the calibration values did nothing, but I tried a few permutations of initializing the sensors.

    1 - 2 - 4 - 3: 1, 2, 4 init 3 not showing

    3 - 1- 2 - 4: No init output at all

    4 - 2 - 1 - 3: 4, 2, 1 init 3 not showing

    I think you may be right about the 3rd sensor, but this begs the question: Why is it behaving itself in the test code and just acting up in the project code itself? I will need to investigate this weird behaviour.

    – pyrob2142 May 22 '21 at 19:07
  • 1
    I wonder if you are not running out of stack / heap RAM. You might try a Arduino platform with a processor which has more (RAM) memory (before pulling all your hair out). Do you need to measure weight fast? You may want to try to only construct 2 instances of the scale measurement class at a time. Construct 2, measure 2, destroy them the repeat connecting to the other 2 sensors. Then start all over again. Every class instantiation uses the same program code. But you use up a block of RAM to hold data from each instantiation. Welcome to C++ programming. – st2000 May 22 '21 at 19:13
  • 1

    Why is it behaving itself in the test code and just acting up in the project code itself?

    ...so, what is the difference between the "test code" and the "project code"?

    – st2000 May 22 '21 at 19:14
  • After some digging I found that the offensive line is SENSOR3.tare();. The Arduino won't get past that one.

    The app is expecting the data as close to real-time as possible and we already have some latency thanks to the Bluetooth connection. I was also suspecting a memory shortage, but if those memory test codes floating around every Arduino forum are somewhat accurate, I should be in the clear.

    – pyrob2142 May 22 '21 at 19:22
  • 2
    swap sensor 1 and sensor 3 pin definitions – jsotola May 22 '21 at 19:26
  • 2
    Now sensor 3 is working and 1 is acting up, again stuck at the tare() call. Sounds like you are thinking about a hardware problem? – pyrob2142 May 22 '21 at 19:32
  • 3
    now swap only either the DATA or SCK pins ... sounds like A4 or A5 is being interfered with ... maybe one of the libraries uses it ....... or try just reversing the pins for sensor 3 – jsotola May 22 '21 at 19:39
  • 3
    A4 & A5 are the same pins as SCL/SDA on the Uno R3. The RTC and sensor are hooked up directly to the same pins. – Dave Newton May 22 '21 at 19:47
  • 1
    You guys are geniuses, I would have never figured that out! Sensor 3 is now using A5 and 4 instead is working perfectly.

    Thank you so much for your time and help!

    – pyrob2142 May 22 '21 at 19:53
  • Maybe a bit late, but I notice you use a DHT11 (humidity and temperature sensor) library, initialize a DHT11 sensor on pin 8 but never read or use the sensor in your code. I think it's better to remove it from the code. – StarCat Dec 06 '21 at 13:09

2 Answers2

1

You'll need to use a library that support multiple HX711's. The HX711-multi library by compugician has worked great for me.

The main difference this library introduces is the ability to sample multiple HX711 units simultaneously. It is optimized for intoducing minimal (effectively zero) overhead.

tokism
  • 111
  • 1
0

If I have seen this correctly, you are currently only measuring the memory requirement in the loop(). To find out how much memory your code is using you should measure the memory consumption in any function which is memory hungry. You should call this line in all functions that are likely to put a heavy load on the heap.

freeRAM = min(freeRAM, get_free_memory());

freeRAM is a global defined int with an initial value of 2048 (or what the RAM size is). It is not enough to measure the memory usage only in loop(), because every function uses the heap during a call and frees the memory afterwards if variables not static.

Georg
  • 51
  • 4
  • I don't know why someone voted this down, I was about to suggest the same thing. In particular saveSettings which instantiates a File object may use a lot of RAM. You don't have a lot in reserve. – Nick Gammon Dec 01 '22 at 05:50