BLYNK
BLYNK.IO       📲 GETTING STARTED       📗 DOCS       👉 SKETCH BUILDER

Terminal repeating...and lots of connecting to server messages

All my attempts at redirecting Blynk output to other screens, including terminal… and I never tripped across this simple hardware Serial loopback option… doh :man_facepalming:

PS, I am not seeing any terminal duplication with above test code, just retained info from past, until stopping & restarting project, which then clears Terminal.

I don’t think I have the rest of the LED widgets correct, also none of the inputs either… just focusing on the terminal printing.

1 Like

Out of curiosity, does the 10-second Blynk.virtualWrite “ping” need to correspond to the Terminal widget virtual pin or can you “ping” any virtual pin?

Hi @Gunner,

Looks pretty close, here is the QR code.
It’s pretty cool getting the Blynk logo in terminal.

Same for me now, when I stop then play, the terminal screen clears.

Okay, as I said before, I have a project running that doesn’t suffer from the same issues with duplicate serial output when you leave the project and come back to it after a while.

My “good” project didn’t use RTC and was pushing some status info out every 4-8 seconds (randomised). I quickly ruled-out RTC being an issue then focussed on pushing data out to the terminal on a frequent basis.
Once per minute wasn’t enough, so I dropped down to every 10 seconds, which worked for me. Some experimentation may give an intermediate frequency that works well.

I was initially pushing the current time out to Terminal, then looked for a solution that didn’t clutter the terminal up with unwanted crap.
A nullstring (“”) didn’t work, then I tried a character followed by a backspace to delete it. The backspace didn’t actually delete the character that had just been written, but the backspace on its own doesn’t move the cursor forward, so that seemed to work.

I guess that if the terminal widget has been idle for a while, then the app is also idle, some sort of refresh process takes place when the app wakes-up, which is incorrectly re-writing the previous data back to the terminal widget. It seems that this is just an iOS thing :anguished:

Can I suggest that we do a bit more testing and experimentation to ensure that we gather as much info as possible, then present our findings to the devs and ask for a bug fix?

I don’t use a local server, so @877 will need to do some testing to make sure it works the same, but I’m thinking that this is an app related thing rather than a server thing.

Pete.

I’m not sure, I’ll add it to my test plan (AKA a post-it note).

Pete.

It’s something I stumbled across on the forum. I was a bit sceptical, but it works well.

Can you try it on your iOS device?
Set the code running and minimise the app, then leave the phone for 5 minutes. My phone is set to auto lock after 30 seconds, so my testing has been to unlock the phone, then click on the Blynk app to maximise it. At that point (with the unmodified code) the screen would then add the existing terminal contents on to the end, so that it’s duplicated. Stopping Blynk (by putting it in to edit mode) then hitting play again restores the terminal to what it should be.

Pete.

Nice job figuring that out, I appreciate it. I have already got this running on my local server and your workaround is working, it would seem to be app related.

I can try that if you like, pin V2 is unused so I will try that.

@Gunner here is my latest code if you want to try it (edit out local or cloud server depending):

//v011 Garage alarm (see changelog below)
//
//
//Project requirements:
//The alarm panel has two outputs, Armed and Sounder
//Poll these two outputs every 1000ms and write the result to a virtual LED (Blynk app on phone)
//The alarm panel has one input SET which can arm or disarm the system
//When a virtual button is pressed (on phone Blynk app) the alarm is armed or unarmed.
//
//
//Changelog:
//v004 had issues with Blynk server heartbeat timeouts and app disconnecting and sometimes unresponsive
//v005 combines both armed and sounder functions into one, run by one timer
//v006 added serial to terminal code as per this thread https://community.blynk.cc/t/serial-arduino-to-terminal-blynk/31547/4
//v007 added correct serial to terminal code working correctly - needs rx to tx link on device
//v008 added date/time stamp to terminal widget, required rtc widget on app, and added serial prints when states change
//v009 fixes for terminal repeating (used BlynkTimer and increased timer interval. Optional add Blynk.run to function so that more CPU time is allocated for housekeeping. Rewrote serial prints as virtual writes to V1. Search for "v009" to see changes.
//v010 added delay between timer functions. More fixes for terminal repeats (changed Blynk.virtualWrite for Serial.print).
//v011 changed send_terminal function as per PeteKnight (Blynk forum), changed terminal prints to serial prints. Removed unneeded comments.
//
//
//Future improvements:
// Change code to Blynk.virtualWrite(vPin, "Random blatherings\n")
// Blynk.virtualWrite(V1, "\n" );   //starts new line
//


//#define BLYNK_DEBUG  //optional, enable for debugging to serial 

#define BLYNK_PRINT Serial // Defines the object that is used for printing Blynk stuff
#include <BlynkSimpleEsp8266.h>
#include <ESP8266mDNS.h>  //for OTA updates
#include <WiFiUdp.h>     //for OTA updates
#include <ArduinoOTA.h>  //for OTA updates
#include <WidgetRTC.h>   //for clock sync
  

//Blynk credentials
char auth[] = ""; // v011 TEST Breadboard auth token

//Wifi credentials
char ssid[] = "";  //Enter your WIFI Name
char pass[] = "";  //Enter your WIFI Password

//Server credentials
char server[] = "192.168.x.xx"; //LOCAL SERVER ONLY
int port = 8080;  //LOCAL SERVER ONLY


//Support for Blynk terminal
WidgetTerminal terminal(V1); //terminal reads from virtual pin specified, possibly not needed when using "Blynk.virtualWrite(Vx, content)"
bool terminal_output_enabled = true;

//Support for Blynk real time clock
WidgetRTC rtc;

//SimpleTimer timer; //setup simple timer to call functions on a timed basis from void loop
BlynkTimer timer; // v009 improvement http://docs.blynk.cc/#blynk-firmware-blynktimer 


//Setup constants for the sketch
const byte Armed = D5;    // INPUT - is the panel armed, or un-armed/alarmed? (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
const byte Sounder = D6;  // INPUT - is the sounder on or off? (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
const byte SET = D4;      // OUTPUT - set or unset the alarm (HIGH = unset the alarm,  LOW = set the alarm)

//Setup variables for Armed
int ArmedState = digitalRead(Armed); //reads armed state of the alarm (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
int lastArmedState = ArmedState; // the previous read from the input pin

//Setup variables for Sounder
int SounderState = digitalRead(Sounder); //reads state of sounder i.e on or off (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
int lastSounderState = SounderState; // the previous read from the input pin

//Setup variables for debouncing of inputs
unsigned long lastArmedDebounceTime = 0;    //setup debounce variable for checkArmed function
unsigned long lastSounderDebounceTime = 0;  // setup debounce variable for checkSounder function
unsigned long debounceDelay = 50;           // the global debounce time in ms, increase if debounce issues continue

//Setup variable for Blynk virtual pin
static unsigned long last_interrupt_time = 0;
bool LastVirtualButtonState = 0;  //"0","FALSE","LOW' means exactly the same



void setup()
{
  Serial.begin(9600);
  Serial.setRxBufferSize(1024); // Increase the serial buffer size //v011 improvement
  Blynk.begin(auth, ssid, pass, server, port);  //connects to Wifi and LOCAL Blynk server (running on raspberry pi)
  //Blynk.begin(auth, ssid); // for Blynk cloud server
  setSyncInterval(60 * 60); // v009 increase time if needed - Sync Blynk real time clock (RTC) interval in seconds (default 10x60 = 10 minutes)
 
  //OTA stuff (update device from Arduino IDE remotely)
  ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); });
  //ArduinoOTA.setHostname("Wemos_no_05_Garage_Alarm");     // for live system
  ArduinoOTA.setHostname("Wemos_no_01_TEST_GARAGE_ALARM");  // for TEST system
  ArduinoOTA.begin();
  
  //Setup the previously assigned constants
  pinMode(Armed, INPUT);       //is the panel armed, or un-armed/alarmed? (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
  pinMode(Sounder, INPUT);     //is the sounder on or off? (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
  pinMode(SET, OUTPUT);        //set or unset the alarm (HIGH = unset the alarm,  LOW = set the alarm)
  digitalWrite(SET, LOW);      //ensures the alarm defaults to SET condition after power loss of Wemos

  //write the current states to the Blynk app
  Blynk.virtualWrite(V5, (ArmedState * 255));      // writes set or unset state of alarm to Blynk virtual LED pin V5
  Blynk.virtualWrite(V6, (!SounderState * 255));   //writes sounder on or off state to Blynk virtual LED pin V6 (inverted logic as sounder is on when at 0V

  //Setup Blynktimers
  timer.setInterval(1000L, Send_Serial);    // Timer calls function to send any serial data to terminal - default 100ms  - v009 increase interval if needed //v011 increased to 1000ms
  delay(500);                               //add delay to ensure timer functions do not get called at the same time
  timer.setInterval(1000L, checkCombined);  //Timer calls function to read state of alarm panel - default 100ms  - v009 increase interval if needed //v011 increased to 1000ms
  //delay(500);                               //need to alter timing - add delay to ensure timer functions do not get called at the same time  
  timer.setInterval(10000L, keep_terminal_awake);  // Write to the terminal widget every 10 seconds


  //Optional print free memory to terminal
   Serial.println("v011 Garage alarm"); 
   Serial.print("Free RAM: ");      
   Serial.print(ESP.getFreeHeap()); 
   Serial.println(" bytes");     
}



void loop()
{
  Blynk.run();  //This function should be called frequently to process incoming commands and perform housekeeping of Blynk connection.
  timer.run();  //Initiates SimpleTimer to runs timed functions
  ArduinoOTA.handle();  // For OTA
}


// a function called every 10 seconds to keep terminal awake, to prevent duplicate entries
void keep_terminal_awake()
{
  Blynk.virtualWrite(V1, "\b"); // Write nothing (backspace) to the terminal widget on V1
}


BLYNK_CONNECTED()
{
  rtc.begin();  // Synchronize time on connection
  serial_print_date_time();
  Serial.println("Server connected");     
}



//A function to print current date and time to Blynk terminal widget, gets called from the below functions
void serial_print_date_time()
{
  String currentDate = String(day()) + "/" + month() + "/" + year();
  String currentTime = String(hour()) + ":" + minute() + ":" + second();
  Serial.print(currentDate);
  Serial.print(" @ ");
  Serial.print(currentTime);
  Serial.print(" ");
}



//New function by PeteKnight (Blynk forum)
  void Send_Serial()
  {
    // Sends serial data to Blynk as well as the serial monitor (handy for setups where the MCU isn't connected to serial because OTA is being used)
    // Note that a jumper is needed between Tx and Rx, which needs to be removed if doing a serial flash upload (but this is not necessary for OTA flash upload)
  
    if (terminal_output_enabled)
    {    
      String content = "";
      char character;
      while(Serial.available())
      {
        character = Serial.read();
        content.concat(character);
      }
      if (content != "")
      {
        Blynk.virtualWrite (V1, content);
      }
    } 
  } //end of void Send_Serial



void checkCombined()// a combined function to read the "armed" state and the "sounder" state
{
 int readingArmed = digitalRead(Armed); // read the state of "Armed" into a local variable:

  if (readingArmed != lastArmedState)   //has the state changed?
  {
    lastArmedDebounceTime = millis();  // if yes(state has changed), reset the debouncing timer to the current millis
  }


  if ((millis() - lastArmedDebounceTime) > debounceDelay) // whatever readingArmed is at, it's been there for longer than the debounce delay, so take it as the actual current state
  {

    if (readingArmed != ArmedState) // has the armed state has changed?
    {
        ArmedState = readingArmed;  // if yes(state has changed) 
        Blynk.virtualWrite(V5, (ArmedState) * 255); // writes ArmedState to Blnk V5 virtual LED names "Alarm armed?"

        if (ArmedState == LOW)
        {
          serial_print_date_time();
          Serial.println("System disarmed"); //v010
        }

        else
        {
          serial_print_date_time();
          Serial.println("System armed"); //v010
        }
    }
  }
  
  lastArmedState = readingArmed; // save the readingArmed. Next time through the function, it'll be the lastArmedState:

 Blynk.run(); //v009 optional extra Blynk.run to allocate more CPU/RAM to the Blynk process, to remove duplicate terminal issues

 int readingSounder = digitalRead(Sounder); // read the state of "Armed" into a local variable:

  if (readingSounder != lastSounderState)   //has the state changed?
  {
    lastSounderDebounceTime = millis();  // if yes(state has changed), reset the debouncing timer to the current millis
  }


  if ((millis() - lastSounderDebounceTime) > debounceDelay) // whatever readingSounder is at, it's been there for longer than the debounce delay, so take it as the actual current state
  {

    if (readingSounder != SounderState) // has the sounder state has changed?
    {
          SounderState = readingSounder;  // if yes(state has changed) 
          Blynk.virtualWrite(V6, (!SounderState) * 255); // writes SounderState to Blnk V6 virtual LED named "Sounder on?"

          if (SounderState == LOW)
          {
          Blynk.notify("Garage alarm is sounding!");  //only send Blynk app notification when then sounder is ON
          serial_print_date_time();
          Serial.println("Sounder activated!"); //v010
          }

          else
          {
          serial_print_date_time();
          Serial.println("Sounder deactivated"); //v010
          }
        
     }
  }
  
  lastSounderState = readingSounder; // save the readingSounder. Next time through the function, it'll be the lastSounderState:
}



// BLYNK_WRITE is a function called every time the device gets an update of a Virtual Pin value from the server (e.g. Blynk app virtual button is pressed)
// contains "latching" code to stop long hold being registered as repeated presses.
BLYNK_WRITE(V3)
{
  int VirtualButtonState = param.asInt(); // assigning incoming value from pin V3 to a variable

  if ((VirtualButtonState) && (!LastVirtualButtonState)) // "VirtualButtonState" is the Blynk virtual button current state ||||||  this means same as "if ((VirtualButtonState == 1) && (LastVirtualButtonState == 0))"
    //if V3 virtual button is still being pressed, the LastVirtualState is set to 1, and !LastVirtualState will therefore be 0. Hence 1 && 0 condition == 0 and therefore function will not be called.
  {
    digitalWrite(SET, !digitalRead(SET));       //writes the inverse value to the pin  (booleon NOT operator )
    Blynk.virtualWrite(V0, digitalRead(SET) * 255);  // for  information only, writes the state of the keyswitch SET contacts to Blynk virtual LED at V0

    if (digitalRead(SET) == LOW)
    {
     serial_print_date_time();
     Serial.println("System arm request"); //v010
    }

    else
    {
     serial_print_date_time();
     Serial.println("System disarm request"); //v010
    }
  }

  LastVirtualButtonState = VirtualButtonState;  // sets LastVirtualButtonState to the same as pinValue, so if pinValue (V3 button) is high, LastVirtualPinState gets set to high
}

Just doing some testing at the moment, but while I’m waiting for the terminal to ‘do it’s thing’ (or not) I thought I’d make a few improvements…

Here’s a better version of your serial_print_date_time() that forces 2 decimal places even when there’s only one digit to display:

//A function to print current date and time to Blynk terminal widget, gets called from the below functions
void serial_print_date_time()
{
  char date_time [22];   
  sprintf (date_time, "%02d/%02d/%04d @ %02d:%02d:%02d ", day(), month(), year(),hour(), minute(), second()        );
  Serial.print(date_time);
}

and if you want to use a button (I used V0) to clear your serial monitor then you can do this:

BLYNK_WRITE(V0)    // Momentary button to clear the terminal
{
  if (param.asInt())
  {    
    Blynk.virtualWrite(V1, "clr");  // Clear the terminal content
  }
}

Edited to add…
When I was testing with just a bare MCU and a breadboard jumper wire between D6 or D7 and 3.3v/Gnd, I was having problems with floating pins. So I changed the pin declarations:

  pinMode(Armed, INPUT_PULLUP);       //is the panel armed, or un-armed/alarmed? (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
  pinMode(Sounder, INPUT_PULLUP);     //is the sounder on or off? (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)

Pete.

1 Like

I’ve just tested this writing the up-time in seconds to a labelled value widget on V2, like this:

  Blynk.virtualWrite(V2, millis()/1000); // Write uptime to V2

but it didn’t work.

It seems that the data needs to be written to the terminal widget itself.

Pete.

Same here Pete, I tried writing some text to V2 and it has the duplicating error…

1 Like

I’m currently testing a 30 second write to the terminal.

Edited to add…

It duplicates on 30 seconds, trying 20 next…

Pete.

1 Like

Okay, it seems that 10 seconds wasn’t far off the upper limit.

12 seconds works okay.

At 13 seconds it seems like it’s locked-up for a few seconds when you re-enter the app, which is usually what happens just before the duplicates appear, but then it recovers without duplicates.

Going to 13.5 does cause duplicates though - at least for me.

(This is starting to sound like the Monty Python “Holy Hand Grenade of Antioch” sketch!) :rofl::rofl::rofl:

I think sticking with 10 seconds gives a 20-30% leeway, which is probably a good thing. What happens if you try the same numbers?

Pete.

Ha don’t forget to throw it on three :grinning:

I’ll check and report back in the morning when back at my pc, thanks :+1:

1 Like

Hi @PeteKnight

OK so I have spent some time investigating, and here are my findings.

Results:

10 seconds - ok - no repeats (5min locked)
10 seconds - ok - no repeats (3mins locked)
11 seconds - repeats Blynk logo but does not scroll  (3mins locked)
12 seconds - repeats Blynk logo but does not scroll  (3mins locked)
12 seconds - repeats Blynk logo but does not scroll, terminal jumps a little (3mins locked)
12.5 seconds -repeats Blynk logo and scrolls (3min locked)
13 seconds - repeats Blynk logo, does not auto scroll (3mins locked)
13 seconds - repeats Blynk logo and scrolls (3mins locked)
14 seconds - repeated only the last two lines, not Blynk logo (I pressed arm then disarm before locking) - (5min locked)
14 seconds - repeated whole Blynk logo (only Blynk logo - pressed no buttons this time) - (5mins locked)
14 seconds - repeated the last line only, not Blynk logo (reset device, pressed disarm button once) - (5mins locked)
14 seconds - repeats whole Blynk logo (3mins locked)

Method used:
Each time I cleared the terminal, flashed OTA, waited for terminal text to appear, pressed home button to exit the app, and finally pressed power button to lock the iPhone.

I repeated some tests, and varied the lock time between 3-5 minutes.

Thoughts:

  1. Did you check if your terminal had not double printed without scrolling at 12 seconds? It was not obvious anything had repeated unless I tried scrolling up.
  2. When a button is pressed that writes to terminal before locking the phone, terminal seems only to repeat that last set of terminal writes (as per 14 seconds results above).

PS - thanks for the code improvements above :smile:

Updated observation:

  1. If sketch is flashed, and stop/play is pressed on app, the terminal is cleared.
  2. If sketch is flashed, a button is pressed that writes to terminal, and then stop/play is pressed, the terminal flashes but is NOT cleared.

My terminal widget was almost full screen (like in my previous screenshots) and the only data in there was the connecting to Wi-Fi, Blynk logo and a few other lines of data, so there was never any scrolling necessary. I think your tests are more indicative that’s mine, as I wasn’t using any real world data, just focusing on trying to recreate your issue then trying to find a potential workaround.

It looks from your tests that 10 seconds is only just frequent enough to prevent any issues, so maybe use 8 seconds as the frequency and see how it goes when linked-up to your alarm panel.

You’re welcome!

When I get a moment I’ll create a topic that summarises the issue and links to this and the other topic that highlighted the same issue.

Pete.

Thank you @PeteKnight for all the help, I now have a working project at least with your workaround. Please let me know if you need any more testing and let me know of any progress! :smile:

Maybe I could trouble you (or anyone else) for a little more advice? The order of the initial terminal prints is bugging me:

Issues:

  1. Time has not sync’d with server before the “date/time Server connected” message
  2. for some reason the “v012 Garage alarm” and “Free ram” messages are being printed last, even though they are in the void setup().

I have tried:
Adding a 1000ms delay after in BLYNK.CONNECTED() which did not work.

BLYNK_CONNECTED()
{
  rtc.begin();  // Synchronize time on connection
  delay(1000);
  serial_print_date_time();
  Serial.println("Server connected");     
}
  • I then tried:
void setup() {
    Blynk.begin(...);
    while (Blynk.connect() == false) {
        // Wait until Blynk is connected
    }

which also had no effect.

I think BLYNK.CONNECTED() is getting called while void setup() is still being run through. And then the serial data is also being called every 1000ms, I guess the terminal is being bombarded.

They are not major issues, but I would at least like to fix no.1 if possible.

Any hints appreciated! :smile:

Latest code:

//v012 Garage alarm (see changelog below)
//
//
//Project requirements:
//The alarm panel has two outputs, Armed and Sounder
//Poll these two outputs every 1000ms and write the results to two virtual LED's (Blynk app on phone)
//The alarm panel has one input SET which can arm or disarm the system
//When a virtual button is pressed (on phone Blynk app) the alarm is armed or unarmed.
//
//
//Changelog:
//v004 had issues with Blynk server heartbeat timeouts and app disconnecting and sometimes unresponsive
//v005 combines both armed and sounder functions into one, run by one timer
//v006 added serial to terminal code as per this thread https://community.blynk.cc/t/serial-arduino-to-terminal-blynk/31547/4
//v007 added correct serial to terminal code working correctly - needs rx to tx link on device
//v008 added date/time stamp to terminal widget, required rtc widget on app, and added serial prints when states change
//v009 fixes for terminal repeating (used BlynkTimer and increased timer interval. Optional add Blynk.run to function so that more CPU time is allocated for housekeeping. Rewrote serial prints as virtual writes to V1. Search for "v009" to see changes.
//v010 added delay between timer functions. More fixes for terminal repeats (changed Blynk.virtualWrite for Serial.print).
//v011 changed send_terminal function as per PeteKnight (Blynk forum), changed terminal prints to serial prints. Removed unneeded comments.
//v012 updated serial_print_date_time to force 2 decimal places, added button v2 to clear terminal (credit PeteKnight)
//
//To do list:
//Fix to ensure time synch occured before printing date/time ins BLYNK_CONNECTED() function
//Fix order initia; prints are made to terminal (BLYNK_CONNECTED() being called at same time as void setup() serial prints ?
//


//#define BLYNK_DEBUG  //optional, enable for debugging to serial 

#define BLYNK_PRINT Serial // Defines the object that is used for printing Blynk stuff
#include <BlynkSimpleEsp8266.h>
#include <ESP8266mDNS.h>  //for OTA updates
#include <WiFiUdp.h>     //for OTA updates
#include <ArduinoOTA.h>  //for OTA updates
#include <WidgetRTC.h>   //for clock sync
#include <TimeLib.h> // check if needed
  

//Blynk credentials
char auth[] = ""; // LIVE system garage alarm - Enter the Auth code which was send by Blynk

//Wifi credentials
char ssid[] = "";  //Enter your WIFI Name
char pass[] = "";  //Enter your WIFI Password

//Server credentials
char server[] = "192.168.1.xx"; //LOCAL SERVER ONLY
int port = xxxx;  //LOCAL SERVER ONLY


//Support for Blynk terminal
WidgetTerminal terminal(V1); //terminal reads from virtual pin specified, possibly not needed when using "Blynk.virtualWrite(Vx, content)"
bool terminal_output_enabled = true;

//Support for Blynk real time clock
WidgetRTC rtc;

//SimpleTimer timer; //setup simple timer to call functions on a timed basis from void loop
BlynkTimer timer; // v009 improvement http://docs.blynk.cc/#blynk-firmware-blynktimer 


//Setup constants for the sketch
const byte Armed = D5;    // INPUT - is the panel armed, or un-armed/alarmed? (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
const byte Sounder = D6;  // INPUT - is the sounder on or off? (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
const byte SET = D4;      // OUTPUT - set or unset the alarm (HIGH = unset the alarm,  LOW = set the alarm)

//Setup variables for Armed
int ArmedState = digitalRead(Armed); //reads armed state of the alarm (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
int lastArmedState = ArmedState; // the previous read from the input pin

//Setup variables for Sounder
int SounderState = digitalRead(Sounder); //reads state of sounder i.e on or off (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
int lastSounderState = SounderState; // the previous read from the input pin

//Setup variables for debouncing of inputs
unsigned long lastArmedDebounceTime = 0;    //setup debounce variable for checkArmed function
unsigned long lastSounderDebounceTime = 0;  // setup debounce variable for checkSounder function
unsigned long debounceDelay = 50;           // the global debounce time in ms, increase if debounce issues continue

//Setup variable for Blynk virtual pin
static unsigned long last_interrupt_time = 0;
bool LastVirtualButtonState = 0;  //"0","FALSE","LOW' means exactly the same



void setup()
{
  Serial.begin(9600);
  Serial.setRxBufferSize(1024); // Increase the serial buffer size //v011 improvement
  Blynk.begin(auth, ssid, pass, server, port);  //connects to Wifi and LOCAL Blynk server (running on raspberry pi)
  //Blynk.begin(auth, ssid); // for Blynk cloud server

  setSyncInterval(60 * 60); // v009 increase time if needed - Sync Blynk real time clock (RTC) interval in seconds (default 10x60 = 10 minutes)
 
  //OTA stuff (update device from Arduino IDE remotely)
  ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); });
  ArduinoOTA.setHostname("Wemos_no_05_Garage_Alarm");     // for live system
  //ArduinoOTA.setHostname("Wemos_no_01_TEST_GARAGE_ALARM");  // for TEST system
  ArduinoOTA.begin();
  
  //Setup the previously assigned constants
  pinMode(Armed, INPUT);       //is the panel armed, or un-armed/alarmed? (armed = HIGH/3.3V and Unarmed/alarmed = LOW/0V)
  pinMode(Sounder, INPUT);     //is the sounder on or off? (Sounder on = LOW/0.33V and Sounder off = HIGH/3.3V)
  pinMode(SET, OUTPUT);        //set or unset the alarm (HIGH = unset the alarm,  LOW = set the alarm)
  digitalWrite(SET, LOW);      //ensures the alarm defaults to SET condition after power loss of Wemos

  //write the current states to the Blynk app
  Blynk.virtualWrite(V5, (ArmedState * 255));      // writes set or unset state of alarm to Blynk virtual LED pin V5
  Blynk.virtualWrite(V6, (!SounderState * 255));   //writes sounder on or off state to Blynk virtual LED pin V6 (inverted logic as sounder is on when at 0V

  //Setup Blynktimers
  timer.setInterval(1000L, Send_Serial);    // Timer calls function to send any serial data to terminal - default 100ms  - v009 increase interval if needed //v011 increased to 1000ms
  delay(500);                               //add delay to ensure timer functions do not get called at the same time
  timer.setInterval(1000L, checkCombined);  //Timer calls function to read state of alarm panel - default 100ms  - v009 increase interval if needed //v011 increased to 1000ms
  delay(250);                               //add delay to ensure timer functions do not get called at the same time  
  timer.setInterval(8000L, keep_terminal_awake);  // Write to the terminal widget every 8 seconds to stop repeated prints (temp bug fix)
  // do not exceed 10 seconds, it stops working for some reason....

  //Optional print free memory to terminal
   Serial.println("v012 Garage alarm"); 
   Serial.print("Free RAM: ");      
   Serial.print(ESP.getFreeHeap()); 
   Serial.println(" bytes");     
}



void loop()
{
  Blynk.run();  //This function should be called frequently to process incoming commands and perform housekeeping of Blynk connection.
  timer.run();  //Initiates SimpleTimer to runs timed functions
  ArduinoOTA.handle();  // For OTA
}




// a function called every 10 seconds to keep terminal awake, to prevent duplicate entries
void keep_terminal_awake()
{
  Blynk.virtualWrite(V1, "\b"); // Write nothing (backspace) to the terminal widget on V1
}




BLYNK_CONNECTED()
{
  rtc.begin();  // Synchronize time on connection
  serial_print_date_time();
  Serial.println("Server connected");     
}



//NEW IMPROVED TIME/DATE FUNCTION by PeteKnight
//A function to print current date and time to Blynk terminal widget.
// Uses string print (sprintf) to print to char buffer, which then gets printed to serial via Serial.print
void serial_print_date_time()
{
  char date_time [22];   
  sprintf (date_time, "%02d/%02d/%04d @ %02d:%02d:%02d ", day(), month(), year(),hour(), minute(), second()        );
  Serial.print(date_time);
}




//New function by PeteKnight (Blynk forum)
  void Send_Serial()
  {
    // Sends serial data to Blynk as well as the serial monitor (handy for setups where the MCU isn't connected to serial because OTA is being used)
    // Note that a jumper is needed between Tx and Rx, which needs to be removed if doing a serial flash upload (but this is not necessary for OTA flash upload)
  
    if (terminal_output_enabled)
    {    
      String content = "";
      char character;
      while(Serial.available())
      {
        character = Serial.read();
        content.concat(character);
      }
      if (content != "")
      {
        Blynk.virtualWrite (V1, content);
      }
    } 
  } //end of void Send_Serial



void checkCombined()// a combined function to read the "armed" state and the "sounder" state
{
 int readingArmed = digitalRead(Armed); // read the state of "Armed" into a local variable:

  if (readingArmed != lastArmedState)   //has the state changed?
  {
    lastArmedDebounceTime = millis();  // if yes(state has changed), reset the debouncing timer to the current millis
  }


  if ((millis() - lastArmedDebounceTime) > debounceDelay) // whatever readingArmed is at, it's been there for longer than the debounce delay, so take it as the actual current state
  {

    if (readingArmed != ArmedState) // has the armed state has changed?
    {
        ArmedState = readingArmed;  // if yes(state has changed) 
        Blynk.virtualWrite(V5, (ArmedState) * 255); // writes ArmedState to Blnk V5 virtual LED names "Alarm armed?"

        if (ArmedState == LOW)
        {
          serial_print_date_time();
          Serial.println("System disarmed"); //v010
        }

        else
        {
          serial_print_date_time();
          Serial.println("System armed"); //v010
        }
    }
  }
  
  lastArmedState = readingArmed; // save the readingArmed. Next time through the function, it'll be the lastArmedState:

 Blynk.run(); //v009 optional extra Blynk.run to allocate more CPU/RAM to the Blynk process, to remove duplicate terminal issues

 int readingSounder = digitalRead(Sounder); // read the state of "Armed" into a local variable:

  if (readingSounder != lastSounderState)   //has the state changed?
  {
    lastSounderDebounceTime = millis();  // if yes(state has changed), reset the debouncing timer to the current millis
  }


  if ((millis() - lastSounderDebounceTime) > debounceDelay) // whatever readingSounder is at, it's been there for longer than the debounce delay, so take it as the actual current state
  {

    if (readingSounder != SounderState) // has the sounder state has changed?
    {
          SounderState = readingSounder;  // if yes(state has changed) 
          Blynk.virtualWrite(V6, (!SounderState) * 255); // writes SounderState to Blnk V6 virtual LED named "Sounder on?"

          if (SounderState == LOW)
          {
          Blynk.notify("Garage alarm is sounding!");  //only send Blynk app notification when then sounder is ON
          serial_print_date_time();
          Serial.println("Sounder activated!"); //v010
          }

          else
          {
          serial_print_date_time();
          Serial.println("Sounder deactivated"); //v010
          }
        
     }
  }
  
  lastSounderState = readingSounder; // save the readingSounder. Next time through the function, it'll be the lastSounderState:
}


// A function where pressing the app button "Clear terminal" will of course clear the terminal output
BLYNK_WRITE(V2)    // Momentary button to clear the terminal
{
  if (param.asInt())
  {    
    Blynk.virtualWrite(V1, "clr");  // Clear the terminal content
  }
}


// BLYNK_WRITE is a function called every time the device gets an update of a Virtual Pin value from the server (e.g. Blynk app virtual button is pressed)
// contains "latching" code to stop long hold being registered as repeated presses.
BLYNK_WRITE(V3)
{
  int VirtualButtonState = param.asInt(); // assigning incoming value from pin V3 to a variable

  if ((VirtualButtonState) && (!LastVirtualButtonState)) // "VirtualButtonState" is the Blynk virtual button current state ||||||  this means same as "if ((VirtualButtonState == 1) && (LastVirtualButtonState == 0))"
    //if V3 virtual button is still being pressed, the LastVirtualState is set to 1, and !LastVirtualState will therefore be 0. Hence 1 && 0 condition == 0 and therefore function will not be called.
  {
    digitalWrite(SET, !digitalRead(SET));       //writes the inverse value to the pin  (booleon NOT operator )
    Blynk.virtualWrite(V0, digitalRead(SET) * 255);  // for  information only, writes the state of the keyswitch SET contacts to Blynk virtual LED at V0

    if (digitalRead(SET) == LOW)
    {
     serial_print_date_time();
     Serial.println("System arm request"); //v010
    }

    else
    {
     serial_print_date_time();
     Serial.println("System disarm request"); //v010
    }
  }

  LastVirtualButtonState = VirtualButtonState;  // sets LastVirtualButtonState to the same as pinValue, so if pinValue (V3 button) is high, LastVirtualPinState gets set to high
}

I’ve created an Issues and Errors topic that summarises the problem here:

Feel free to add to it if you have any additional data.

I looked at the time sync and sequencing of the messages and couldn’t find a solution.
I think you’re correct about the cause.

Pete.

Great, thanks for keeping me informed, I will be watching closely! :smile:

Thanks for looking. It’s not that big of a deal, I might come back to it later on. For now I’ve had enough of terminal :laughing:

I don’t want to clutter the new thread, but it may be worth pointing out you can recreate the issue with a simple, standard, vanilla Terminal widget sketch. The serial port jumper, RTC, … may confuse matters.

Great job laying this out for the developers to fix, @PeteKnight!

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "xxx";
char ssid[] = "xxx";
char pass[] = "xxx";

WidgetTerminal terminal(V1);

BlynkTimer timer;

void terminal_test(void) {
   terminal.println(millis());  
   terminal.flush();
}

void setup(void) {
   Serial.begin(115200);
   Blynk.begin(auth, ssid, pass);
   terminal.clear();
   timer.setInterval(60000L, terminal_test);
}

void loop(void) {
   Blynk.run();
   timer.run();
}
1 Like

Can i see your full code?