this is my firts topic here, i have looked for the solution on this forum but I wasn't succeed on any attempt.
I am trying to build a pet feeder for my dogs and I am finished with coding and feeder construction.
The idea is to have an automatic and manual feed template at Blynk. I will set the duration of the relay on and off based on the portion amount selected on blynk app. My only problem is that I am not getting the timer to work well. I am using NTPClient and tried to use delay(), millis() and BlynkTimer.
If I choose 1, 2 or 3 at the feed amount, the result is not as planned, even if i do not change any value, the time is highly variable. For example, if feed amount is 1, I would expect to have 2 sec for relay on and I am getting values varying between 1 to 8 seconds.
What am I missing on my code? Please help me on that.
welcome to the arduino-forum.
Well done posting your code as a code-section.
I'm not familiar with Blynk. But there is a general strategy to analyse code-behaviour.
printing to the serial monitor. But not at highest possible speed.
This code-version has some debug-macros added which show with more detail what is going on.
Try this code-version and post what you get in the serial monitor.
Post the content of the serial monitor as a code-section
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
#define dbgi(myFixedText, variableName,timeInterval) \
{ \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
}
#define dbgc(myFixedText, variableName) \
{ \
static long lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
#define dbgcf(myFixedText, variableName) \
{ \
static float lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 2;
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
#define BLYNK_TEMPLATE_ID "xxxx"
#define BLYNK_TEMPLATE_NAME "xxx"
#define BLYNK_FIRMWARE_VERSION "0.1.0"
#define BLYNK_PRINT Serial
#define APP_DEBUG
#define USE_NODE_MCU_BOARD
#define motor D0
#include "BlynkEdgent.h"
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const long utcOffsetInSeconds = -10800;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "0.br.pool.ntp.org", utcOffsetInSeconds);
int HH, MM, final_time;
int time_blynk, data, qtd, auto_feed, feedback;
BLYNK_WRITE(V0) {
data = param.asInt();
}
BLYNK_WRITE(V1) {
time_blynk = param.asInt();
}
BLYNK_WRITE(V2) {
qtd = param.asInt();
}
BLYNK_WRITE(V3) {
auto_feed = param.asInt();
}
void setup() {
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
delay(100);
BlynkEdgent.begin();
pinMode(motor, OUTPUT);
digitalWrite(motor, LOW);
timeClient.begin();
qtd = 1;
feedback = 0;
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 250);
dbgi("01",qtd,1000); // print only once every second
BlynkEdgent.run();
timeClient.update();
dbgc("00",time_blynk); // print only if value of time_blynk has CHANGED (and print only one time per change)
HH = timeClient.getHours();
MM = timeClient.getMinutes();
final_time = 3600 * HH + 60 * MM;
dbgc("02",final_time); // only in case the value of "final_time" has CHANGED print the value once
dbgc("03",feedback); // only in case the value of "feedback" has CHANGED print the value once
dbgc("04",auto_feed); // only in case the value of "auto_feed" has CHANGED print the value once
if (time_blynk == final_time && feedback == 0 && auto_feed == 1) {
Serial.print("MotorLigado");
digitalWrite(motor, HIGH);
delay(2000 * qtd);
Serial.print("MotorDesligado");
digitalWrite(motor, LOW);
feedback = 1;
}
if (time_blynk != final_time) {
feedback = 0;
}
if (data == 1) {
digitalWrite(motor, HIGH);
Serial.print("MotorLigado");
delay(2000 * qtd);
Serial.print("MotorDesligado");
digitalWrite(motor, LOW);
}
}
Hi Stefan,
Thank you for your answer and sorry for my delay. I was in a business trip and without access to my personal computer.
This is what I am getting on serial monitor. I hit manual feed sometimes and auto feed once at the specific time , after that turned off auto feed. Do you see anything that is wrong below?
I don't know. My support was to post a code-version that enables to analyse the real program flow. As long as you can't describe very very detailed what functionality you want to have. How should I judge if everything is correct or not?
Sorry for not explaining so well.
My objective with this code is to press the "manual feed" button and depending on the quantity I selected (qtd =1,2 or 3), the motor is turned on for 2, 4 or 6 seconds. My problem is that my delay is not working properly.
If you see the serial monitor, "MotorLigadoMotorDesligado" string means that it went through my feed function and Blynk is working good. But the intervals are varying too much. First line, 16:52:45.148 minus 16:52:42.805, it stayed on for 2,3 seconds and I expected 6 seconds. Other examples:
16:53:05.016 - 16:53:07.031 = 2 seconds
16:53:14.764 - 16:53:11.577 = 3,2 seconds.
I have tried millis() and BlynkTimer as well, none of them worked well. Is there anything that I should modify on the code, or is there a possibility of being the NODEMCU the problem?
I haven't analysed your code if you follow the concept I describe below the timing-details are handled completely locally on the I call it the feeding-contro-arduino
Example feeding through motor for two seconds
If you are trying to remotely control the thing by sending some data to Blynk
then waiting for an answer from blynk before your feeding-arduino goes on executing code and this datatransmission is repeated multiple times within the process of
switch feeding-motor on
(dataprocessing back and forth with blynk)
2 seconds later switch feeding-motor off
Blynk wil slow down everything. If I remember right Blynk has a limitation to not process commands that wants to exchange data more often than once every second. Blynk has 100.000 of users imagine each user having 10 to 50 devices and all of them sending data 100 times per second:
that would result in a total Blynk-Server-overload
A different approach will work better:
remote unit is giving commands.
Examples:
Sending a command "Motor on for 2 seconds"
This single "command" is received over blynk by and all the details
make timestamp
switch on motor
keep motor on for 2 seconds
switch of motor
Are done locally without any data-pin-pong-transmission over blynk between your feeding-arduino and your remote-device
So these commands could be different blynk--App-buttons
blynk-App-button 2 seconds
blynk-App-button 4 seconds
blynk-App-button 6 seconds
set automatic-feed time hour
set automatic-feed time minute
etc.
If this is running you could try adding to send back status-info
If you have tapped the 4-second-button
the feeding-arduino sends back a single byte that indicates "motor ON"
if the motor is switched off send a single byte "motor OFF"
as you are using a delay(2000) even multiplied with a factor
as long as this delay() is executed there willbe no communication with Blynk
I'm pretty sure that a code-template using
BlynkEdgent.run();
timeClient.update();
has a comment
call BlynkEdgent.run(); repeatedly and very often
You wil have to change your code to be completely non -blocking.
This requires a completely different thinking than using delay()