Проблема стабильности с ведением журнала веб-сервера и смартметра
За последние несколько месяцев я успешно записал данные со счетчиков электроэнергии и газа в моем доме на удаленный веб-сервер.
Теперь я пытаюсь добавить возможность включать и отключать дверной звонок через Интернет.
Несмотря на то, что это работает, я столкнулся с двумя проблемами.
Проблемы
Arduino зависает через несколько часов
При передаче показаний счетчика дверной звонок не может быть переключился.
Вопросы
Есть ли способ сделать управление дверным звонком приоритетным?
и ..
есть ли способ выяснить, почему Arduino через некоторое время зависает?
Настройка:
- Плата Arduino Mega 2560
- Экран Ethernet
- ЖК-дисплей
- Ретрансляция
- датчик освещенности(для управления подсветкой)
- последовательное подключение к интеллектуальному счетчику электроэнергии/газа
//V1.1: Cleaned up messy code
#include <LiquidCrystal_I2C.h>
#include <Wire.h> // Comes with Arduino IDE
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#include <AltSoftSerial.h>
#include <SPI.h>
#include <Ethernet.h>
//setup ethernet server
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //assign arduino mac address
byte ip[] = { 192, 168, 178, 95}; // ip in lan assigned to arduino
byte gateway[] = { 192, 168, 178, 1}; // internet access via router
byte subnet[] = { 255, 255, 255, 0}; //subnet mask
EthernetServer server(84); //server port arduino server will use
char thingSpeakAddress[] = "";
IPAddress myDns(8, 8, 8, 8);
EthernetClient client;
String writeAPIKey = "XXXMX2WYYR0EVZZZ";
String tsData = "hello"; //dummy data
String readString; //used by server to capture POST request
const int updateThingSpeakInterval = 20 * 1000; // Time interval in milliseconds to update ThingSpeak (number of seconds * 1000 = interval)
// Variable Setup
long lastConnectionTime = 0;
boolean lastConnected = false;
int failedCounter = 0;
unsigned long int updateCounter = 0; //set refresh counter to 0
//////////////////////
//slimme meter////////
/////////////////////
char c;
AltSoftSerial altSerial;
const int requestPin = 48; //data pin connected to smartmeter
char input; // incoming serial data (byte)
bool readnextLine = false;
#define BUFSIZE 75
char buffer[BUFSIZE]; //Buffer for serial data to find \n .
int bufpos = 0;
long mEVLT = 0; //Meter reading Electrics - consumption low tariff
long mEVHT = 0; //Meter reading Electrics - consumption low tariff
long mEAV = 0; //Meter reading Electrics - Actual consumption
long mG = 0; //Meter reading Gas
//unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds
// the "L" is needed to use long type numbers
//const unsigned long postingInterval = 10 L * 1000 L; // delay between updates, in milliseconds, the "L" is needed to use long type numbers
/////////////////////////////////////////
//photcel////////controls lcd backlight//
////////////////////////////////////////
int photocellPin = 5; // the cell and 10K pulldown are connected to a0
int photocellReading; // the analog reading from the analog resistor divider
//boolean doorbell = true;
int creset = 43; //Telling arduino that you are connecting pin 43 with reset
//////////
//setup///
//////////
void setup() {
Wire.begin();
digitalWrite(22, LOW);
digitalWrite(creset, HIGH);
pinMode(creset, OUTPUT);
pinMode(22, OUTPUT); //pin selected to control
pinMode(6, OUTPUT); //pin selected to control
pinMode(7, OUTPUT); //pin selected to control
pinMode(8, OUTPUT); //pin selected to control
//pinMode(5, OUTPUT); //pin 5 selected to control
Ethernet.begin(mac, ip, myDns, gateway, subnet);
server.begin();
Serial.begin(9600);
altSerial.begin(9600);
Serial.println(F("Smarthome controller")); // keep track of what is loaded
Serial.println(F("Send a g in serial monitor to test client")); // what to do to test client
lcd.begin(20, 4); // initialize the lcd for 16 chars 2 lines, turn on backlight
// sensorValue = analogRead(sensorPin);
lcd.backlight(); // finish with backlight on
//-------- Write characters on the display ------------------
// NOTE: Cursor Position: Lines and Characters start at 0
lcd.setCursor(0, 0); //Start at character 4 on line 0
lcd.print("Smarthome controller");
delay(1000);
lcd.setCursor(0, 1);
lcd.print("powerd by arduino");
delay(1000);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print("");
lcd.setCursor(0, 3);
delay(1000);
lcd.print("Slimmemeter: ");
//lcd.backlight(); // turn on backlight.
lcd.setCursor(0, 2);
lcd.print("Doorbell: onn ");
}
void loop() {
if (updateCounter > 400) {
digitalWrite(creset, LOW); // Reset arduino
}
// Update ThingSpeak
if (!client.connected() && (millis() - lastConnectionTime > updateThingSpeakInterval)) {
decodeTelegram();
}
// Check if Arduino Ethernet needs to be restarted
if (failedCounter > 3) {
startEthernet();
}
lastConnected = client.connected();
EthernetClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
char cl = client.read();
//read char by char HTTP request
if (readString.length() < 100) {
//store characters to string
readString += cl;
//Serial.print(c);
}
//if HTTP request has ended
if (cl == '\n') {
///////////////
Serial.print(readString); //print to serial monitor for debuging
//now output HTML data header
if (readString.indexOf('?') >= 0) { //don't send new page
client.println("HTTP/1.1 204 Zoomkat");
client.println();
client.println();
} else {
client.println(F("HTTP/1.1 200 OK")); //send new page on browser request
client.println(F("Content-Type: text/html"));
client.println();
client.println(F("<HTML>"));
client.println(F("<HEAD>"));
client.println(F("<TITLE>Smarthome</TITLE>"));
client.println(F("</HEAD>"));
client.println(F("<BODY>"));
client.println(F("<H1>Smarthome control page</H1>"));
// DIY buttons
client.println(F("Deurbel"));
client.println(F("<a href=/?on2 target=inlineframe>ON</a>"));
client.println(F("<a href=/?off3 target=inlineframe>OFF</a><br><br>"));
client.println(F("Pins"));
client.println(F(" <a href=/?off2468 target=inlineframe>ALL ON</a>"));
client.println(F(" <a href=/?off3579 target=inlineframe>ALL OFF</a>"));
client.println(F("<IFRAME name=inlineframe style='display:none'>"));
client.println(F("</IFRAME>"));
client.println(F("</BODY>"));
client.println(F("</HTML>"));
}
delay(1);
//stopping client
client.stop();
if (readString.indexOf("**secretcode**") > 0) //checks for secretkey
{
///////////////////// control arduino pin
if (readString.indexOf("on2") > 0) //checks for 2
{
digitalWrite(22, LOW); // set pin 5 high
//Serial.println(F("Led 5 On"));
//Serial.println();
lcd.setCursor(0, 2);
lcd.print("Doorbell: on ");
//doorbell = true;
}
if (readString.indexOf("off3") > 0) //checks for 3
{
digitalWrite(22, HIGH); // set pin 5 low
//Serial.println(F("Led 5 Off"));
//Serial.println();
lcd.setCursor(0, 2);
lcd.print("Doorbell: off ");
//doorbell = false;
}
//clearing string for next read
readString = "";
}
}
}
}
}
//lcd.setCursor(0, 0); //Start at character 4 on line 0
//lcd.print("SMARTHOME CONTROLLER;.");
//String analogValue = String(analogRead(A5), DEC);
photocellReading = analogRead(photocellPin);
//lcd.setCursor(0, 1);
//lcd.print(photocellReading);
if (photocellReading < 800) {
lcd.backlight(); // turn on backlight.
} else {
lcd.noBacklight(); // turn on backlight.
}
}
////////////////////////////////////////////////////////////////////////////
//////////////////////////FUNCTIONS/////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
void updateThingSpeak(String tsData)
//void sendGET() //client function to send and receive GET data from external server.
{
//lcd.setCursor(0, 2);
// lcd.print(" ");
lcd.setCursor(13, 3);
lcd.print("verstur");
// delay(1000);
if (client.connect(thingSpeakAddress, 80)) {
client.print("GET /smarthome/log.php?stroomLaag=");
client.print(mEVLT);
client.print("&stroomHoog=");
client.print(mEVHT);
client.print("&stroomVerbruik=");
client.print(mEAV);
client.print("&gasStand=");
client.print(mG);
client.println(" HTTP/1.1");
client.println("Host: .nl");
client.println("User-Agent: arduino-ethernet");
client.println("Connection: close");
client.println();
lastConnectionTime = millis();
updateCounter++;
Serial.print("Connecting to log server...");
Serial.println(updateCounter);
//x=x+1; //page upload counter
// lcd.setCursor(0,3);
// lcd.print("Data send ");
//delay(1000);
lcd.setCursor(13, 3);
//lcd.print(" ");
lcd.print("ok ");
lcd.setCursor(16, 3);
lcd.print(updateCounter);
if (client.connected()) {
Serial.println("Connecting to suscesfull...");
//Serial.println();
failedCounter = 0;
} else {
failedCounter++;
Serial.println("Connection to failed (" + String(failedCounter, DEC) + ")");
Serial.println("connection failed");
Serial.println();
lcd.setCursor(13, 3);
lcd.print("fout");
lcd.setCursor(16, 3);
lcd.print(String(failedCounter, DEC));
}
} else {
failedCounter++;
Serial.println("Connection to ThingSpeak Failed (" + String(failedCounter, DEC) + ")");
Serial.println();
lastConnectionTime = millis();
}
while (client.connected() && !client.available()) delay(1); //waits for data
while (client.connected() || client.available()) { //connected or data available
char cl = client.read(); //gets byte from ethernet buffer
//readString += c; //places captured byte in readString
//Serial.print(c); //print raw data
}
client.stop(); //stop client
readString = ""; //clear readString variable
}
////////////////////////////////////////////////////
void startEthernet() {
client.stop();
Serial.println("Connecting Arduino to network...");
Serial.println();
delay(1000);
// Connect to network amd obtain an IP address using DHCP
if (Ethernet.begin(mac) == 0) {
Serial.println("DHCP Failed, reset Arduino to try again");
Serial.println();
} else {
Ethernet.begin(mac, ip, gateway, gateway, subnet);
server.begin();
Serial.println("Arduino connected to network using DHCP");
Serial.println();
}
delay(1000);
}
////////////////////////////////////////////////////
void decodeTelegram() {
long tl = 0;
long tld = 0;
if (altSerial.available()) {
//input = altSerial.read();
c = altSerial.read();
// --- 7 bits instelling ---
c &= ~(1 << 7);
char inChar = (char) c;
//Serial.print(c);
input = c;
//Serial.println(input);
//char inChar = (char)input;
// Fill buffer up to and including a new line (\n)
buffer[bufpos] = input & 127;
bufpos++;
if (input == '\n') { // We received a new line (data up to \n)
if (sscanf(buffer, "1-0:1.8.1(%ld.%ld", & tl, & tld) == 2) {
tl *= 1000;
tl += tld;
mEVLT = tl;
}
// 1-0:1.8.2 = Elektra verbruik hoog tarief (DSMR v4.0)
if (sscanf(buffer, "1-0:1.8.2(%ld.%ld", & tl, & tld) == 2) {
tl *= 1000;
tl += tld;
mEVHT = tl;
}
// 1-0:1.7.0 = Electricity consumption actual usage (DSMR v4.0)
if (sscanf(buffer, "1-0:1.7.0(%ld.%ld", & tl, & tld) == 2) {
mEAV = tl + tld;
}
// 0-1:24.2.1 = Gas (DSMR v4.0) on Kaifa MA105 meter
//if (strncmp(buffer, "0-1:24.2.0", strlen("0-1:24.2.1")) == 0) {
if (sscanf(strrchr(buffer, '(') + 1, "%d.%d", & tl, & tld) == 2) {
mG = (tl * 1000) + tld;
}
//}
// Empty buffer again (whole array)
for (int i = 0; i < 75; i++) {
buffer[i] = 0;
}
bufpos = 0;
}
if (input == '!') { //uitroepteken geeft einde van telegram aan, dus we gaan data versturen
// if (millis() - lastConnectionTime > postingInterval) {
// httpRequest();
updateThingSpeak("field1=0");
// httpRequest();
mEVLT = 0;
mEVHT = 0;
mEAV = 0;
mG = 0;
client.stop();
//}
} //Einde vraagteken detectie
//Serial.print(c);
} //Einde 'if AltSerial.available'
} //Einde 'decodeTelegram()' functie
@user1595774, 👍0
Обсуждение2 ответа
Пожалуйста, ознакомьтесь с некоторыми комментариями, которые могут вам помочь.
Почему вы используете программный последовательный порт?
У этой ATmega 4 реальных последовательных порта!
delay()
опасен во время выполнения основного цикла. Попробуйте использовать его только в настройках.
Каждый раз, когда вы вызываете задачу, убедитесь, что вы не останетесь там навсегда.
Выходите из задачи, если что-то кажется слишком долгим. Используйте millis()
для отметки разницы во времени, чтобы измерить затраченное время.
При необходимости распечатайте его в опции компилятора отладки.
Печать в случае сбоя не тратит время MCU, если ошибок нет.
Я не вижу никаких проблем с использованием delay() в циклах программы; если вы собираетесь заявить, что это опасно, пожалуйста, объясните, почему. SoftSerial удобен всякий раз, когда вы используете последовательную связь на последовательных выводах, отличных от стандартных, и для согласованности в программировании я обычно использую его, чтобы мой код был очень четким в именовании выводов. millis() великолепен, однако вы должны помнить, что добавление каких-либо операторов отладки, таких как lcd.write() или Serial.write(), обязательно приведет к смещению времени. При всем при этом могу ли я спросить, в чем заключается ваш главный смысл в этом ответе, пожалуйста?, @SDsolar
Мои рекомендации только для того, чтобы помочь вам найти две проблемы. Процессор зависает из-за повреждения буфера оперативной памяти. Последовательное оборудование сокращает время обработки последовательных данных, а задержка или длинный цикл означают, что системные службы для Ethernet и последовательных задач будут остановлены., @Rui Rfm Margarido
Я не уверен, почему требуется softserial, возможно, потому что сообщение от smartpowermeter инвертировано. Я постараюсь добавить защиту от длины буфера и сообщу вам о результате., @user1595774
buffer[bufpos] = input & 127;
bufpos++;
Никогда не используйте буфер без защиты длины.
Отметить, если достигнут максимальный буфер, принудительно никогда не записывать после последней позиции.
Вот так:
if ( bufpos > MaxBuff )
{
Serial.print("errornumber");
bufpos = MaxBuff;
// И заставить \n закончить сообщение об ошибке
}
- Управление реле 5В с помощью Wemos D1 R1
- Наиболее эффективная и экономичная установка домашней автоматизации
- Как заставить 5-вольтовое реле работать с NodeMCU
- Когда следует использовать транзистор, МОП-транзистор, а когда следует использовать реле?
- Несколько клиентских серверов через Wi-Fi
- WebSocketsServer.h: No such file or directory
- Как получить данные из базы данных моего сервера в переменную в моем Arduino?
- Причины, по которым нельзя подключать реле непосредственно к цифровому контакту Arduino
Я уже сталкивался с неожиданным зависанием микроконтроллера ATmega, но в моем случае это было из-за колебаний в источнике питания. Поскольку у вас есть полная плата Arduino, на ней должны быть необходимые конденсаторы для фильтрации. Хотя недостаточная мощность все еще может быть проблемой даже с конденсаторами. Так что, если вы не питаете его от недостаточных батарей, это, вероятно, ошибка в вашем коде. Вы можете добавить больше журналов, а последнее зарегистрированное сообщение будет подсказкой; это требует постоянного прослушивания последовательного порта другой машиной., @youen
может быть проблема в потребляемой мощности катушки реле, @Juraj
Arduino питается от адаптера на 1 ампер от сети., @user1595774