esp8266 / Arduino

ESP8266 core for Arduino
GNU Lesser General Public License v2.1
15.98k stars 13.34k forks source link

SoftwareSerial: wdt reset #1426

Closed supersjimmie closed 5 years ago

supersjimmie commented 8 years ago

I use both SoftwareSerial and WiFiClient. During normal operations, it sometimes works for many hours but other times it gives a wdt reset. This looks like it has a relation between SoftwareSerial and WiFiClient.

If I use mySerial.enableRX(false) before starting the function with the WiFiClient, it seems to keep working, otherwise it looks like to crash on different places.

ets Jan 8 2013,rst cause:4, boot mode:(3,6) wdt reset This was during client.print() operations, the first client.print with the data succeeded, then just after that, only when sending an empty line with client.print("\r\n") it gave the wdt reset.

But earlier, it was just during receiving data on the SoftwareSerial: `ets Jan 8 2013,rst cause:4, boot mode:(1,6)``

And a couple of other times also during normal receiving on the serial: ets Jan 8 2013,rst cause:4, boot mode:(3,7)

So many different boot modes, they all seem only to occur if I don't use the mySerial.enableRX(false).

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

supersjimmie commented 8 years ago

Even when I change the receiving part:

  1. Removed the obsolete line #include <ESP8266WiFi.h>,
  2. Changed Serial.write() to char c =, to make it do as less as possible. It still crashes after a while. EDIT: Even when I do'n even read from mySerial but just flush()!
plerup commented 8 years ago

Then we're back at what's happening when there is a burst of interrupts. The time to send 500 characters at 115200 is definitely shorter than the wdt timeout, but the question is how the interrupts are delivered. I have seen, as stated in the comments in the source, that interrupts are made on both flanks regardless of what setup in gpio_pin_intr_state_set.

When I have the time I will try to trace what happens in the interrupt routine when running this code.

plerup commented 8 years ago

I doesn't matter if you read or not, the interrupts will be called anyway

supersjimmie commented 8 years ago

It looks like it first hangs somehow and then the wdt kicks in after it's timeout. I see a row of chars come in, then it freezes and after about 4-5 sec the wdt reset occurs. So why does something freeze?

And you're right, even with only wdtFeed and yield, doing nothing with mySerial, it crashes.

supersjimmie commented 8 years ago

Ok, next information now I'm able to test faster.

With esp8266/arduino 2.0.0 stable and softwareserial 2.0 and 2.1.1 it keeps working. Same with esp8266/arduino GIT version.

So esp8266/arduino Stable or GIT makes no difference after all. (I think that I thought so because I also had different softwareserial versions).

But with softwareserial 2.2 it crashes within seconds on 2.0.0 and latest GIT. (no difference between original 2.2 and juancgalvez version)

Must be something that changed from 2.1.1 to 2.2.

juancgalvez commented 8 years ago

Resets in my case are very random. I worked properly for 26 hours. After that it did a reset and didn't boot properly again. It needed 2 hours of manual resets until it worked again and has been up for more than 12 hours. Pretty weird.

Even if I press reset button it works for a couple of seconds and freezes again or works for properly hours (completely random).

plerup commented 8 years ago

@supersjimmie Great job debugging this! Thanks.

Now I've reproduced the error myself.

What changed in 2.2 was that correct handling of multiple ports was added. In this solution however, a loop was introduced. It's a finite one but it seems like it causes this problem. After I optimized it, the crash doesn't seem occur even if I constantly feed the port at 115200.

A new version is in the works. It will also include the suggestions from @juancgalvez

supersjimmie commented 8 years ago

You're welcome @plerup I hope it's gonna be solved so everybody can use this great piece of software without issues. Let me know if you have something that I can test in my test-setup. @juancgalvez are we sure you have the same issue or could there be something else? (perhaps show your code and problem too?)

supersjimmie commented 8 years ago

If you're changing things, perhaps also look at this warning:

SoftwareSerial.cpp:185:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

    if (next != m_inPos) {
juancgalvez commented 8 years ago

@plerup. Great you found something to review and fix..

@supersjimmie. My program is pretty simple. The purpose is to read, every 10 seconds, the power consumption from another device (PieceFair PZEM-004). To do so I have to transmits via async serial, at 9600 bps, 7 bytes asking for 4 values (voltage, current, power and accumulated power). The device replies with 7 bytes for each reading. In total 28 bytes transmitted and 28 bytes received. One 7 bytes transmission one 7 bytes receive, done 4 times. Once I get the 4 values the information it is transmitted via UDP to a Linux machine. I was using MQTT, but thinking the issue could be there, I replaced it with UDP. Once the issue is fixed I am going to move back to MQTT.

plerup commented 8 years ago

New version 2.2.1 is now available. Doesn't crash in my tests...

supersjimmie commented 8 years ago

Downloaded, will test...

juancgalvez commented 8 years ago

I will test too. Thank you for the fixes.

supersjimmie commented 8 years ago

When doing a stresstest (sending 600x char(85) and pause for 500ms), it crashes after 12-200 sec.

supersjimmie commented 8 years ago

Strange, when I use more "real life" data it seems to work better. If I use something like this at the sender part:

  mySerial.println(F("1-3:0.2.8(40)"));
  mySerial.println(F("0-0:1.0.0(151207214053W)"));
  mySerial.println(F("0-0:96.1.1(4530303034303031353738303135353134)"));
  mySerial.println(F("1-0:1.8.1(000998.160*kWh)"));
<more>

With a total of about 600 chars, it seems to keep running. The only different I can think about is that I do 600x mySerial.write(85) in the stress test because char 85 is a nice alternating bit pattern like '1010101'...

juancgalvez commented 8 years ago

If println does a continuous transmission without a delays then that could be a problem. A delay(0) before line 120 could solve the problem. Something like:

   delay(0);
   // Disable interrupts in order to get a clean transmit
   cli();
supersjimmie commented 8 years ago

I don't know if there is any short delay when using println, but there probably is if that keeps running. De added delay(0) still results in a wdt reset when I keep sending 600x char 85 with a .5 sec pause.

juancgalvez commented 8 years ago

@plerup I think your idea about several software serial is interesting, but the implementation (not really the implementation but the hardware limitations) won't work. Lets assume there are incoming characters at two pins at the same time and at the same serial speed (assume 9600 bps). Your ISR handles both pins changes (if the occur at the same time) but, by the time rxRead finished receiving a byte from the first pin, time has passed and nothing has been received from the second and data would have lost. Your code would work if data is received at different times.

tuxedo0801 commented 8 years ago

@juancgalvez Sounds correct. software serial lacks a receiving buffer, by design. So just one soft serial possible, or risk data loss.

juancgalvez commented 8 years ago

@supersjimmie. Are you transmitting or receiving those characters using software serial when the reset occurs?

supersjimmie commented 8 years ago

I am transmitting them from an arduino nano with softwareserial and receiving then on an esp8266 with softwareserial. The wdt resets occur on the receiving esp. (the arduino is for emulating my actual situation, a smartmeter sending power/gas-usage to an esp)

juancgalvez commented 8 years ago

Ok. Then the delay(0) modification is not necessary but won't hurt.

supersjimmie commented 8 years ago

I am thinking about going back to SoftwareSerial 2.1.1, because that version looks stable and I don't need multi-port support.

Major change from 2.2 to 2.2.1 is added cli() and sei() to rxRead(). Would it be of any use if I add those to the 2.1.1 version too? Or could that potentially harm if any other esp (like wifi perhaps?) related function needs interrupts?

igrr commented 8 years ago

Note that you should never disable interrupts for periods longer than 20 microseconds (that's what SDK release notes said).

plerup commented 8 years ago

The main problem here is that if to long time is spent in the interrupt routine it can get out of sync in some circumstances. This was to some extent mitigated in 2.2.1 by optimizing the loop. The sei cli calls were probably not such a good idea considering @igrr comment.

I've been examining the flow more in detail now using an oscilloscope and it looks like the main problem is that there is always a second interrupt coming in for each byte. This seem to occur when GPIO interrupts are enabled again after the stop bit even though there is no flank at this time.

scope

In the code this is detected as an invalid interrupt as long as it occurs before the next start bit but when long burst of data is coming in at high speed there is a risk of the interrupt routine coming out of sync and being called constantly and thereby triggering the WDT

So the main question is why does the second interrupt occur or how can it be avoided.

drmpf commented 8 years ago

In the AVR world this could be avoided by clearing the flag that was set for this interrupt before re-enabling interrupts, but not sure what the ESP equivalent is.

plerup commented 8 years ago

That is done here as well

supersjimmie commented 8 years ago

Just did a simple test, bursting char 85 without any pause. SoftwareSerial 2.1.1 original keeps running, when I add cli() and sei() it wdt crashes. So that confirms: better not use cli() and sei().

plerup commented 8 years ago

New version 2.2.2 now available. Doesn't crash for me when sending '85' constantly at 115200

supersjimmie commented 8 years ago

Thanks, will run it here too.... Running for 1000 sec now... Second test has been running (85)'s at 115k2 for over 3 hours without a crash.

odilonafonso commented 8 years ago

Hi @plerup Sorry by delay.. You said 16 days ago:

@odilonafonso The SoftwareSerial you are referring to is the AVR version. If you are using the staging release of esp8266/arduino you will automatically get the correct version

But I can not found a SoftwareSerial.h into my ESP8266 environment tree. Maybe I did something wrong when I installed?

Yes, I found SoftwareSerial on AVR environment:

/opt/arduino-1.6.7/hardware/arduino/avr/libraries/SoftwareSerial

And my ESP8266 path is:

/opt/arduino-1.6.7/hardware/esp8266com

I tried to mount this environment with the staging version... ;(

Where exactly should be located SoftwareSerial library and how can I correct this issue ? Thank you again !!

odilonafonso commented 8 years ago

Hi @plerup,

I found a SoftwareSerial library on:

https://github.com/plerup/espsoftwareserial (your version)

I don't know what ESP8266 staging release you talked about.

By the way, please tell me where to put your SoftwareSerial version in order that the Arduino IDE can work without confusion with the AVR version. How can the IDE knows what version to use ? which is the search order that the pre-compiler runs before the compiler to find the include files? Thank you very much !!

plerup commented 8 years ago

@odilonafonso If you do a clean install of Arduino and then add esp8266/Arduino as described on the gitHub page this will work out of the box. Depending on what board you choose the correct SoftwareSerial will be used.

plerup commented 8 years ago

New version 2.2.3 is now available. This is my commit message:

Improved interrupt handling

The interrupt register is now cleared when leaving the interrupt routine. This is important as the register gets set even when the interrupts are disabled. This fixes the previous problem with a "dummy" interrupt which could lead to wdt and other problems

igrr commented 8 years ago

@plerup should we update the download link in this repository?

plerup commented 8 years ago

@igrr I plan to make one more release were I hope it will be possible to use attachInterrupt together with SoftwareSerial.

plerup commented 8 years ago

Version 3.0.0 is now available. It uses the standard GPIO interrupt management with attachInterrupt.

https://github.com/plerup/espsoftwareserial/commit/20f93975747c4e054d8212dcc24a0a82e8e43a0b

NickWaterton commented 8 years ago

I have also been struggling with this problem for some time. I am reading serial data from a maxbotix sonar rangefinder, which sends R0140/r (where 0140 is the range in cm or in or mm depending on version) 10 or 20 times per second. I'm using Arduino 1.6.7 with the latest github esp version. This is driving NeoPixels, via MQTT, and without SoftwareSerial (or minimal use), will run for 3-4 days without problems (I can use PWM for rangefinding on some versions of maxbotix rangefinders). It might run longer, but I've been developing the software. This is a very complex program, but it runs fine without Software serial.

With SoftwareSerial, it runs for between 2 hrs and 14 hrs before a hardware WDT reset. It's been driving me crazy trying to figure out if it's a loop, or just too many interrupts - but the timing on the interrupts should not give a WDT reset...

I was using your original code (version 2.2 I believe), but modified to allow use of attachinterrupt in other code.

I think I'm doing the same thing as you in your latest version, but my ISR routines are part of the class, I notice that your's aren't - does that make a difference? I've added your latest changes to my code, in the hope that it will run longer between WDT resets. I think the only real difference between your code and mine is that I have a function isEnabled to return whether serial receive is on or not - probably redundant as i could just read m_enabled.

I turn serial receive off when driving the NeoPixels, as I'm running the NeoPixels with interrupts ON (have to really) and interrupts cause flickering/weird stuff with the timing for the NeoPixels.

I'll let you know in a day or so if it runs longer now or not. The resets are not a big issue, as I save the current state every time it changes, and restore it on reboot, so the resets are transparent to operation, but annoying as hell, as I could not figure it out (and so I thought that my understanding of esp interrupts/timing/wifi/dog feeding was not correct).

My code for Software Serial (mostly copied from yours, but with a slightly different way of using attachintterupt) is below. Any comments/improvements are appreciated. I may just revert to using your version as they are so similar now (I don't like modifying standard libraries as it makes the code less portable).

This is SoftwareSerial.cpp

/*

SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266.
Copyright (c) 2015 Peter Lerup. All rights reserved.

Modified by Nick Waterton 15th Jan 2016 to allow use of attachinterrupt in normal code.
Limits number of instances to 10 (but can be easily expanded).

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

#include <Arduino.h>
#include "SoftwareSerial.h"

//Make sure static numInstances gets initialised to zero.
uint8_t SoftwareSerial::_numInstances = 0;

//Static pointers to instances, allows interrupts to be forwarded
SoftwareSerial* SoftwareSerial::_instances[MAX_ESPSOFTSERIAL_INSTANCES];

SoftwareSerial::SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic, unsigned int buffSize) {
   m_rxValid = m_txValid = false;
   m_enabled = false;
   m_buffer = NULL;
   m_invert = inverse_logic;
   if (_numInstances < MAX_ESPSOFTSERIAL_INSTANCES) {
     if (isValidGPIOpin(receivePin)) {
       m_rxPin = receivePin;
       m_buffSize = buffSize;
       m_buffer = (uint8_t*)malloc(m_buffSize);
       if (m_buffer != NULL) {
         m_rxValid = true;
         m_inPos = m_outPos = 0;
         pinMode(m_rxPin, INPUT);
         //record instance id and pointer to instance
         _instances[_numInstances] = this;
         _instanceId = _numInstances;
         enableRx(true);
       }
     }
     if (isValidGPIOpin(transmitPin)) {
       m_txValid = true;
       m_txPin = transmitPin;
       pinMode(m_txPin, OUTPUT);
       digitalWrite(m_txPin, !m_invert);
     }
     //increment number of instances
     ++_numInstances;
     // Default speed
     begin(9600);
   }
}

//These get the interrupt call into a class instance (which is difficult)
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange0() { _instances[0]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange1() { _instances[1]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange2() { _instances[2]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange3() { _instances[3]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange4() { _instances[4]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange5() { _instances[5]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange6() { _instances[6]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange7() { _instances[7]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange8() { _instances[8]->handle_interrupt(); }
void ICACHE_RAM_ATTR SoftwareSerial::onRxPinChange9() { _instances[9]->handle_interrupt(); }

SoftwareSerial::~SoftwareSerial() {
  enableRx(false);
  if (m_rxValid)
    _instances[_instanceId] = NULL;
  if (m_buffer)
    free(m_buffer);
}

bool SoftwareSerial::isValidGPIOpin(int pin) {
  // Some GPIO pins are reserved by the system
  return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15);
}

void SoftwareSerial::begin(long speed) {
  // Use getCycleCount() loop to get as exact timing as possible F_CPU works as well
  m_bitTime = ESP.getCpuFreqMHz()*1000000/speed;
}

void ICACHE_RAM_ATTR SoftwareSerial::enableRx(bool on) {
  if (m_rxValid) {  
    if(!on) {
      detachInterrupt(m_rxPin);
      m_enabled = false;
    }
    else {
      if(m_invert) int_type = RISING;
      else int_type = FALLING;
      switch(_instanceId) {
        case 0: attachInterrupt(m_rxPin, onRxPinChange0, int_type); break;
        case 1: attachInterrupt(m_rxPin, onRxPinChange1, int_type); break;
        case 2: attachInterrupt(m_rxPin, onRxPinChange2, int_type); break;
        case 3: attachInterrupt(m_rxPin, onRxPinChange3, int_type); break;
        case 4: attachInterrupt(m_rxPin, onRxPinChange4, int_type); break;
        case 5: attachInterrupt(m_rxPin, onRxPinChange5, int_type); break;
        case 6: attachInterrupt(m_rxPin, onRxPinChange6, int_type); break;
        case 7: attachInterrupt(m_rxPin, onRxPinChange7, int_type); break;
        case 8: attachInterrupt(m_rxPin, onRxPinChange8, int_type); break;
        case 9: attachInterrupt(m_rxPin, onRxPinChange9, int_type); break;
      }
    m_enabled = true;
    }
  flush(); //reset buffers
  }
}

int SoftwareSerial::read() {
  if (!m_rxValid || (m_inPos == m_outPos)) return -1;
  uint8_t ch = m_buffer[m_outPos];
  m_outPos = (m_outPos+1) % m_buffSize;
  return ch;
}

int SoftwareSerial::available() {
  if (!m_rxValid) return 0;
  int avail = m_inPos - m_outPos;
  if (avail < 0) avail += m_buffSize;
  return avail;
}

#define WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bitTime; }

size_t SoftwareSerial::write(uint8_t b) {
  if (!m_txValid) return 0;

  if (m_invert) b = ~b;
  // Disable interrupts in order to get a clean transmit
  cli();
  unsigned long wait = m_bitTime;
  digitalWrite(m_txPin, HIGH);
  unsigned long start = ESP.getCycleCount();
  // Start bit;
  digitalWrite(m_txPin, LOW);
  WAIT;
  for (int i = 0; i < 8; i++) {
    digitalWrite(m_txPin, (b & 1) ? HIGH : LOW);
    WAIT;
    b >>= 1;
  }
  // Stop bit
  digitalWrite(m_txPin, HIGH);
  WAIT;
  sei();
  return 1;
}

bool SoftwareSerial::isEnabled() {
  return m_enabled;
}

void SoftwareSerial::flush() {
  m_inPos = m_outPos = 0;
}

int SoftwareSerial::peek() {
  if (!m_rxValid || (m_inPos == m_outPos)) return -1;
  return m_buffer[m_outPos];
}

void ICACHE_RAM_ATTR SoftwareSerial::rxRead() {
  // Advance the starting point for the samples but compensate for the
  // initial delay which occurs before the interrupt is delivered
  unsigned long wait = m_bitTime + m_bitTime/3 - 500;
  unsigned long start = ESP.getCycleCount();
  uint8_t rec = 0;
  for (int i = 0; i < 8; i++) {
    WAIT;
    rec >>= 1;
    if (digitalRead(m_rxPin))
      rec |= 0x80;
  }
  if (m_invert) rec = ~rec;
  // Stop bit
  WAIT;
  // Store the received value in the buffer unless we have an overflow
  int next = (m_inPos+1) % m_buffSize;
  if (next != m_inPos) {
    m_buffer[m_inPos] = rec;
    m_inPos = next;
  }
}

void ICACHE_RAM_ATTR SoftwareSerial::handle_interrupt() {
  if(digitalRead(m_rxPin) == _instances[_instanceId]->m_invert)  //find start bit
    _instances[_instanceId]->rxRead();
  // Must clear this bit in the interrupt register,
  // it gets set even when interrupts are disabled
  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rxPin);
}

the SoftwareSerial.h file is

/*
SoftwareSerial.h

SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266.
Copyright (c) 2015 Peter Lerup. All rights reserved.

Modified by Nick Waterton 15th Jan 2016 ro allow use of attachinterrupt in normal code.
Limits number of instances to 10 (but can be easily expanded).

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

#ifndef SoftwareSerial_h
#define SoftwareSerial_h

#include <inttypes.h>
#include <Stream.h>

// This class is compatible with the corresponding AVR one,
// the constructor however has an optional rx buffer size.
// Speed up to 115200 can be used.

#define MAX_ESPSOFTSERIAL_INSTANCES 9

class SoftwareSerial : public Stream
{
public:
  SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64);
  ~SoftwareSerial();

  void begin(long speed);
  int peek();
  bool isEnabled();

  virtual size_t write(uint8_t byte);
  virtual int read();
  virtual int available();
  virtual void flush();
  operator bool() {return m_rxValid || m_txValid;}

  // Disable or enable interrupts on the rx pin
  void enableRx(bool on);

  //current instance id
  uint8_t _instanceId = 0;

  using Print::write;

private:
  bool isValidGPIOpin(int pin);
  void rxRead();

  //static pointers to ISR (MAX_ESPSOFTSERIAL_INSTANCES of them)
  static void onRxPinChange0();
  static void onRxPinChange1();
  static void onRxPinChange2();
  static void onRxPinChange3();
  static void onRxPinChange4();
  static void onRxPinChange5();
  static void onRxPinChange6();
  static void onRxPinChange7();
  static void onRxPinChange8();
  static void onRxPinChange9();

  void handle_interrupt();

  //total number of instances
  static uint8_t _numInstances;
  // Max number of instances of this class
  static SoftwareSerial* _instances[MAX_ESPSOFTSERIAL_INSTANCES];

  // Member variables
  int int_type;
  int m_rxPin, m_txPin;
  bool m_rxValid, m_txValid;
  bool m_invert;
  bool m_enabled;
  unsigned long m_bitTime;
  unsigned int m_inPos, m_outPos;
  int m_buffSize;
  uint8_t *m_buffer;

};

// If only one tx or rx wanted then use this as parameter for the unused pin
#define SW_SERIAL_UNUSED_PIN -1

#endif
tuxedo0801 commented 8 years ago

Is Version 3.0.0 now finally solving the WDT issue?!

pgScorpio commented 8 years ago

I doubt this problem has anything to do with SoftwareSerial itself. It seems a general problem with io interrupts during client.connect(....)

I have the same issue when using RcSwitch and found out that when a client.connect is waiting for an answer from the server and an IO interrupt occurs and that interrupt tries to read the pin the esp crashes in the digitalRead.

sully81 commented 7 years ago

@juancgalvez Did you ever get your esp8266 to communicate correctly with the PZEM-004t? I'm attempting to do the same thing and hitting the same issue that you were with the software serial.

ayasystems commented 7 years ago

Me too... I have the same problem with PZEM-004 when use softserial on ESP8266.....

juancgalvez commented 7 years ago

@sully81. Excuse me. I didn't see your message before.

@sully81 and @ayasystems. I guess the issue is where the character is read. In the current version, before line 199 add the following code:

   unsigned long wait = m_bitTime / 2;
   unsigned long start = ESP.getCycleCount();
   WAIT;  // wait to be in the middle of the bit time

This code is necessary to make sure with read as close as possible to the middle of a bit.

m_bitTime / 2 could be m_bitTime / 3. Play with this.

It has been working fine for a long time using the code I posted before.

Respectfully,

Juan C.

jakkik commented 7 years ago

I too had sporadic WDT resets when using the SoftwareSerial library. The sketch ran anywhere from couple of minutes to few hours, but always ended up reset by the WDT.

For me the solution was Software UART project by plieningerweb. This is a generic C library meant to be used with ESP8266 SDK directly, but it can be modified to work with Arduino with just a few tweaks. I've been using this library successfully, with no WDT resets since.

LuisVSa commented 7 years ago

Hi,

I am experimenting resets with SoftwareSerial but could find a partial cure. My project reads 2 serial ports (one at 4800 baud and another at 38400 baud) and outputs the combined information as UDP packets over an wifi network created by the esp8266. The data coming to the 2 inputs are Nmea183 sentences. These sentences have a maximum length of 80 characters. I am generating test sentences with another "dummy" esp8266 with an interval of 50ms between each sentence. I chose the hardware UART for the 38400 baud input set one SoftwareSerial channel to receive the slower 4800 input. Wrong decision as explained next.

I tested all the versions of SoftwareSerial from the first one to the latest. I temporary removed all the other functions of the project (such as user http requests to change the settings of the project, ...) but, always, within a couple of minutes from starting, the wifi part of the esp8266 goes off (still the system keeps receiving Nmea sentences as I have a led that keeps blinking). If no data comes to the SoftwareSerial RX pin everything is fine. When data comes, the system crashes, after few minutes. When I was thinking in using a front end (for example a small arduino mini to get combine the 2 inputs and transmit serially to the hardware uart of the esp8266) I made a small change and the system works now without errors for some hours.

I suppose that interrupts are disabled by the SoftwareSerial code during the reception of a character. Since a character at 4800 baud takes about 2ms to be received, this seems too long for the wifi to work properly. So I just tried to change the input ports: receiving the 38400 baud data on the SoftwareSerial pin and the slower 4800 baud data on the hardware counterpart. Now the time with interrupts is about 260us. It is also a big duration but wifi keept running during 3 hours. Then it stoped !!! :-(

I am not sure if this information is useful. In the OP the baud rate was 115200. Apparently the problem was solved with the following releases of SoftwareSerial, which was not my case. What comes from my tests is that the problem is worst at slower baud rates!

cloeffler commented 7 years ago

@jakkik If you wouldn't mind could you share what modifications were necessary to make that Softuart library work with arduino?

LuisVSa commented 7 years ago

Hello,

I was able to solve the problem by writing my own "software libray". In fact I wrote an ino file that is used by my sketch as I do not know how to make a library.

I do not understand how people use the software serial with wifi as crashes always occur. The reason is that software serial disables interrupts when a start bit appears. In my code I use a microsecond ticker or timer. When an external interrupt appears on the input serial line, going low, because of the start bit, the service routine is very fast and only sets the timer to "tick" at the middle of the the first bit. Then when servicing this "tick" the timer is reprogrammed to "tick" 7 more times at the middle of the following bits. I found by trial an error the timer settings to work at 4800 and 9600. At higher baud rates irt can not cope with the serial data.

In that way, all external interrupts can occur (except for a small time after the start bit) and wifi routines will not suffer from long periods of interrupt disabled.

NOTE: I am referring to the ESP8266! Of course!

allene222 commented 6 years ago

@LuisVSa I have been tearing my hair out with the crashes with Software Serial. My system has two 9600 baud ports both inputting nmea-0183 data once a second. Have you, or can you, share what you did to get this to work? My system crashes after 1/2 hour to 6 hours. About half the crashes are caused by the heep running out in which case I get an exception and a core dump. The other half have stable heep but I get a WDT reset. In both cases, the code starts again. I think the low data rates that we are both using explain why there is not a lot of hits on this issue in Google. Most people are using higher bit rates and the library is probably optimized for that case.

allene222 commented 6 years ago

Hello group,

I am posting this message to share the testing I have done over the last few days. There is definitely an issue with SoftwareSerial at 9600 baud. I have done multiple test with the input being a single GPS sentence once every second. Here is an example $GPRMC,011734.000,A,3726.1428,N,12208.9468,W,0.03,28.27,121017,,,A*41 My program monitors the heep and the reason for reset. I see two failure modes. One is a hardware watchdog reset. This might happen after 1/2 hour or after 6 hours. The second is a sudden steady decrease in heep followed by an exception and a core dump. This might happen right away, or after several hours of steady heep.

I modified my code and removed the Software Serial and replaced it with a routine that monitors the timer and outputs the same data that the swSer routine would have output. Thus all the code is the same except on Software Serial. It ran last night for 12 hours without a reset.

I can't say what the cause of this is but I can say there is still a problem.

I am using a fresh Arduino install with the SW Ser version that is loaded with the v2.3.0 ESP-8266 code. The ESP-8266 I have tried a couple of versions including the latest WeMos v2.3.0 with the usb connector on the top.

I will post code if anyone wants to see it.

devyte commented 6 years ago

@allene222 if you suspect the SoftwareSerial class, can you reproduce with a minimal sketch?