bogde / HX711

An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital Converter (ADC) for Weight Scales.
MIT License
896 stars 538 forks source link

NTP code won't work with HX711 #63

Closed sp9wun closed 7 years ago

sp9wun commented 7 years ago

I found something strange. I tried to add hx711 to my wemos mini board and I discovered that adding simple line:

#include <HX711.h>

results that sending udp (ntp) packet procedure don't work - I have no idea why. Here's sample code:

#define USE_HX711

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>

#ifdef USE_HX711
#include <HX711.h>
#endif

// Scale Settings
const int SCALE_DOUT_PIN = D4;
const int SCALE_SCK_PIN = D3;

// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";

int timeZone = 1;     // Central European Time
//const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)

#ifdef USE_HX711
HX711 scale(SCALE_DOUT_PIN, SCALE_SCK_PIN);
#endif

WiFiUDP Udp;
ESP8266WiFiMulti wifiMulti;

unsigned int localPort = 8888;  // local port to listen for UDP packets

long factor = 50900.00;
float weight = 0;

time_t getNtpTime();
void digitalClockDisplay();
void sendNTPpacket(IPAddress &address);

void setup()
{
  Serial.begin(115200);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);

#ifdef USE_HX711
  scale.set_scale(factor);
  scale.tare();
#endif

  wifiMulti.addAP("dlink", "");

  int tries = 60;     // max czas oczekiwania na połączenie z WiFi
  int n;

  Serial.println("Connecting Wifi...");
  while (wifiMulti.run() != WL_CONNECTED && tries > 0) {
    delay(1000);
    Serial.print(".");
    tries--;
  }
  Serial.println("");
  Serial.println("Connected");
  Serial.print("SSID: "); 
  Serial.println(WiFi.SSID()); 
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  delay(2000);

  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()
{
#ifdef USE_HX711
  weight = scale.get_units(5);
#endif

  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();
      if(timeStatus() == timeNeedsSync)
        Serial.print("not synced: ");
    }
  } else {
    Serial.print(String(weight, 3));
    Serial.println("kg");
  }
}

void digitalClockDisplay()
{
#ifdef USE_HX711
  weight = scale.get_units(5);
#endif

  char curtime[40];

  // digital clock display of the time
  sprintf(curtime,"Time: %02d:%02d:%02d %02d.%02d.%d",(int)hour(),(int)minute(),(int)second(),(int)day(),(int)month(),(int)year());
  Serial.print(curtime); 
  Serial.println(); 
  Serial.println(String(weight, 3));
}

/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

Results:

Connecting Wifi... ... Connected SSID: dlink IP address: 192.168.3.117 Starting UDP Local port: 8888 waiting for sync Transmit NTP Request us.pool.ntp.org: 108.61.56.35 No NTP Response :-( 0.001kg 0.001kg

And after commenting:

// #define USE_HX711

Connecting Wifi... ... Connected SSID: dlink IP address: 192.168.3.117 Starting UDP Local port: 8888 waiting for sync Transmit NTP Request us.pool.ntp.org: 45.79.1.70 Receive NTP Response Time: 10:13:43 20.02.2017 0.000 Time: 10:13:44 20.02.2017 0.000

electrokean commented 7 years ago

Actually, with that define you aren't just including the HX711 header file, but also making several calls to the library, in particular a call to scale.get_units(5) called at the start of each loop.

I don't really know anything about the Wemos Mini (other than it being based on ESP8266), but I've got a feeling the busy-wait loop in HX711::read() may be the issue.

  1. Do you have a recent version of HX711.cpp which includes a call to yield() in the is_ready() busy wait loop at the start of HX711::read() ?

  2. Can you confirm whether including the the HX711 header file without the other HX711 calls also has an issue?

  3. What if you remove the call to scale.get_units() at the start of loop, and only call it from within digitalClockDisplay() ?

sp9wun commented 7 years ago

Thank you for reply! I'm afraid that problem is in HX711.h file - even if I'll remove everything between #ifdef #endif and add line with '#include HX711.h' only - issue still exists.

sp9wun commented 7 years ago

Forgott to answer your another question - I'm using latest version of HX711.cpp with yield() function:

    while (!is_ready()) {
        // Will do nothing on Arduino but prevent resets of ESP8266 (Watchdog Issue)
        yield();
    }

If I remove the call to scale.get_units() at the start of loop I don't see value of weight because timeStatus=timeNotSet. So because of it I added scale.get_units() at the beginning of loop to see results from HX711 module.

sp9wun commented 7 years ago

I made additional test - I tried to do the same on atmega 328 (on board: Arduino Duemilanove with Arduino Wiznet Ethernet shield) - and it seems to work without problem.

dhmsjs commented 7 years ago

Don't know if this will fix your problem or not, but still you might want to move the object instantiation

#ifdef USE_HX711
HX711 scale(SCALE_DOUT_PIN, SCALE_SCK_PIN);
#endif

into setup(), or remove the pin definitions from it, and instead set them up with a begin() call in setup(). The sequence of initialization of global objects is not well defined by the compiler so you can't control when exactly during the hardware initialization sequence that global object is created. This is the reason why there is a begin() method provided -- so that you can control when the initialization occurs.

electrokean commented 7 years ago

@sp9wun sorry, I missed the display of the weight in loop() when time is not set. Maybe you need to reduce the frequency in displaying the weight as it interferes with the networking. I suggest you change you code in loop() to only get and display the weight once or twice per second.

@dhmsjs he would still have to instantiate the object outside setup() to keep the global scope, but should certainly use a begin() call from setup to avoid the issue you mention.

dhmsjs commented 7 years ago

@electrokean that's what I'm thinking too. Since by default the HX711 takes 100mSec to read and @sp9wun is averaging 5 readings on each call to read(), each reading locks up the system for almost 1/2 sec which might be messing up the UDP packet reception.

sp9wun commented 7 years ago

Thank you for help but I think that there is some misunderstanding - I wrote at the beginning that problem is in #include <HX711.h> Here is code without #include <HX711.h> and this code is working very well:

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>

// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";

int timeZone = 1;     // Central European Time
//const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)

WiFiUDP Udp;
ESP8266WiFiMulti wifiMulti;

unsigned int localPort = 8888;  // local port to listen for UDP packets

long factor = 50900.00;
float weight = 0;

time_t getNtpTime();
void digitalClockDisplay();
void sendNTPpacket(IPAddress &address);

void setup()
{
  Serial.begin(115200);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);

  wifiMulti.addAP("dlink", "");

  int tries = 60;     // max czas oczekiwania na połączenie z WiFi
  int n;

  Serial.println("Connecting Wifi...");
  while (wifiMulti.run() != WL_CONNECTED && tries > 0) {
    delay(1000);
    Serial.print(".");
    tries--;
  }
  Serial.println("");
  Serial.println("Connected");
  Serial.print("SSID: "); 
  Serial.println(WiFi.SSID()); 
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  delay(2000);

  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();
      if(timeStatus() == timeNeedsSync)
        Serial.print("not synced: ");
    }
  } else {
    Serial.print(String(weight, 3));
    Serial.println("kg");
  }
}

void digitalClockDisplay()
{
  char curtime[40];

  // digital clock display of the time
  sprintf(curtime,"Time: %02d:%02d:%02d %02d.%02d.%d",(int)hour(),(int)minute(),(int)second(),(int)day(),(int)month(),(int)year());
  Serial.print(curtime); 
  Serial.println(); 
  Serial.print(String(weight, 3));
  Serial.println(" kg"); 
}

/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

And here is exactly the same code but with #include <HX711.h> line. And there is no call to HX711 library at all.

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>

#include <HX711.h>

// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";

int timeZone = 1;     // Central European Time
//const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)

WiFiUDP Udp;
ESP8266WiFiMulti wifiMulti;

unsigned int localPort = 8888;  // local port to listen for UDP packets

long factor = 50900.00;
float weight = 0;

time_t getNtpTime();
void digitalClockDisplay();
void sendNTPpacket(IPAddress &address);

void setup()
{
  Serial.begin(115200);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);

  wifiMulti.addAP("dlink", "");

  int tries = 60;     // max czas oczekiwania na połączenie z WiFi
  int n;

  Serial.println("Connecting Wifi...");
  while (wifiMulti.run() != WL_CONNECTED && tries > 0) {
    delay(1000);
    Serial.print(".");
    tries--;
  }
  Serial.println("");
  Serial.println("Connected");
  Serial.print("SSID: "); 
  Serial.println(WiFi.SSID()); 
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  delay(2000);

  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();
      if(timeStatus() == timeNeedsSync)
        Serial.print("not synced: ");
    }
  } else {
    Serial.print(String(weight, 3));
    Serial.println("kg");
  }
}

void digitalClockDisplay()
{
  char curtime[40];

  // digital clock display of the time
  sprintf(curtime,"Time: %02d:%02d:%02d %02d.%02d.%d",(int)hour(),(int)minute(),(int)second(),(int)day(),(int)month(),(int)year());
  Serial.print(curtime); 
  Serial.println(); 
  Serial.print(String(weight, 3));
  Serial.println(" kg"); 
}

/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

And results with working code:

Connecting Wifi...
..
Connected
SSID: dlink
IP address:
192.168.3.117
Starting UDP
Local port: 8888
waiting for sync
Transmit NTP Request
us.pool.ntp.org: 138.236.128.36
Receive NTP Response
Time: 07:45:17 23.02.2017
0.000 kg
Time: 07:45:18 23.02.2017
0.000 kg

And result after adding #include <HX711.h>

Connecting Wifi...
..
Connected
SSID: dlink
IP address:
192.168.3.117
Starting UDP
Local port: 8888
waiting for sync
Transmit NTP Request
us.pool.ntp.org: 173.49.198.27
No NTP Response :-(
0.000kg
0.000kg
electrokean commented 7 years ago

There is almost nothing in the include file other than a class definition. If this breaks your code, then either you've discovered a weird compiler bug (and the Arduino/ESP8266 compiler is a bit weird) - or your code is probably going to break very easily anyway.

From your output is looks like in the example including the HX711.h header you are also trying to talk to a different NTP server, and it times out after 1.5s and then possibly doesn't try again. So using a different NTP server adds another variable other than just the include. How many times did you try this?

You also don't make any further calls to getNtpTime from loop(), and I recall reading somewhere that if you don't access now() to read the current time, then the setSyncProvider()/setSyncInterval() options will never get triggered. Maybe you could try to display now() when timeStatus is timeNotSet (or compare it to prevDisplay or something like that), and see if that makes any difference.

Caveat - I have near zero actual experience with ESP8266 or TimeLib.

bogde commented 7 years ago

hi @sp9wun , did you solved this? do you need any help?

sp9wun commented 7 years ago

Nope - I made workaround. I connected HX711 to Arduino (Duemilanove) with ethernet shield. Second board is ESP8266 which is connecting with arduino using TCP/IP protocol and reading current weight.

bogde commented 7 years ago

thanks for the update. i'm going to close this now, but feel free to re-open as needed. cheers!