Котёл TermoTop C\Z Mazda брал Б.У , Протокол оставил EVO(ещё не успел протестить с с термотоп) байт запуска сменил в eeprom 0х21( ну и да, все настройки изменил в еепроме и залил его, а потом основной скетч) По поводу номера, да идёт + и ещё 12 цифр(+хххххххххххх) После ночи перестал управляться с MQTT(хотя светодиод на сим продолжал быстро моргать и был в сети, проверил звонком) Отправил смс , перегрузился сим и всё заработало ( понаблюдаю ещё)
Baron8, вот скетч подготовки еепром, там настраивается в том числе и количество символов в номере телефона (включая символ '+'). Там по умолчанию стоит 12. Вам надо поменять там на это количество на 13.
#include <EEPROM.h>
enum Cells {
ResetNumber_cell, //0
TimeWebasto_cell, //1
ProtocolSTART_cell, //2
StartByte_cell, //3
ProtocolSTATUS_cell, //4
Heater_cell, //5
delta_cell, //6
sizeNumber_cell, //7
sizeCells,
TelNumber1_cell =20, //20
TelNumber2_cell =40, //40
DallasAddr_cell =60 //60
};
// названия температур котла:
enum TempC {VyhlopC, EngineC, UlicaC, SalonC, size_arrayTemp};
// возможные протоколы чтения статуса котла:
enum ProtocolSTATUS_ {
STATUSBUS, // статусы котла читаются по цифровой шине
ANALOG // статусы котла считываются силами ардуино - по датчикам
};
// возможные протоколы запуска котла:
enum ProtocolSTART_ {
STARTBUS, // запуск котла происходит по цифровой шине
IMPULSE, // запуск котла происходит импульсом GND (для подпайки к кнопке пуск на таймере котла)
POTENCIAL // запуск котла происходит подачей потенциала +12В (пока плюс висит - котёл работает)
};
// возможные типы котла:
enum Heater_ { TTC_E, VEVO, EVO, HYDRONIC};
//--------------------ниже здесь устанавливаем какие настройки у девайса будут по умолчанию
byte ProtocolSTATUS = STATUSBUS; // в данном случае статусы читаются по цифровой шине
byte ProtocolSTART = STARTBUS; // в данном случае запуск котла происходит по цифровой шине
byte Heater = EVO; // в данном случае тип котла EVO
byte StartByte = 0x20; // в данном случае байт на старт котла 0x20 (для шины w-bus)
byte worktime = 30; // в данном случае время цикла работы котла 30 минут
byte deltaT = 45; // разница температур улицы и выхлопа, выше которой считается, что котёл успешно стартанул
byte sizeTelNumber = 12; // количество символов в номере телефона, включая +. Т.е. +79121234567 это будет 12 символов
//сюда пишем адреса датчиков даллас (левые 8 байт):
byte DS18B20 [size_arrayTemp][10] = {
{0x28, 0xFF, 0xB2, 0xB5, 0xC1, 0x17, 0x05, 0xD1, VyhlopC, -100},
{0x28, 0xFF, 0xD3, 0xE2, 0xC1, 0x17, 0x04, 0x0D, EngineC, -100},
{0x28, 0xFF, 0xF8, 0xBC, 0xC1, 0x17, 0x04, 0x48, UlicaC, -100},
{0x28, 0xFF, 0x3F, 0xB7, 0xC1, 0x17, 0x05, 0xF1, SalonC, -100}
};
//----------------------
void setup() {
pinMode(13, OUTPUT);
digitalWrite(13, 0);
EEPROM.write(TimeWebasto_cell, worktime);
EEPROM.write(ProtocolSTART_cell, ProtocolSTART);
EEPROM.write(ProtocolSTATUS_cell, ProtocolSTATUS);
EEPROM.write(Heater_cell, Heater);
EEPROM.write(ResetNumber_cell, 0);
EEPROM.write(StartByte_cell, StartByte);
EEPROM.write(delta_cell, deltaT);
EEPROM.write(sizeNumber_cell, sizeTelNumber);
for (int i = sizeCells ; i < DallasAddr_cell ; i++) EEPROM.write(i, '0'); // стираем ячейки под номера телефонов
// ниже записываем в еепром адреса датчиков даллас:
for (byte i=0; i<sizeof(DS18B20)/10; i++) {
for (byte k=0; k<10; k++) EEPROM.write (DallasAddr_cell+i*10+k, DS18B20 [i][k]);
}
digitalWrite(13, HIGH);
}
void loop() {}
Заливаете этот скетч, дожидаетесь когда led 13 загорится. Потом заливаете основной скетч этот v3.81
const char ver[] = "Firmware 3.81"; // версия прошивки
//---------------------------Настройки MQTT-----------------------------------------------------------------------------
const char ACCESSPOINT[]= "\"internet.mts.by\""; // точка доступа оператора связи симкарты
const char PROTOCOLIUS[] = "MQIsdp"; // это и оставляем
const char MQTTNAME[] = "Ma V"; // это смотрим на сервере MQTT
const char MQTTUSER[] = "i tyw"; // это смотрим на сервере MQTT
const char MQTTPASSWORD[] = "1Q 678W"; // это смотрим на сервере MQTT
const char SERVERNAME_PORT[] = "\"m16.cloudmqtt.com\", \"14689\""; // это смотрим на сервере MQTT
#define TIMEBROKER 80 // продолжительность сессии с брокером , мин (пока такое и оставляем)
//----------------------------------название ячеек еепром----------------------------------------------------------------
#include <EEPROM.h>
enum Cells {
ResetNumber_cell, //0
TimeWebasto_cell, //1
ProtocolSTART_cell, //2
StartByte_cell, //3
ProtocolSTATUS_cell, //4
Heater_cell, //5
delta_cell, //6
sizeNumber_cell, //7
TelNumber1_cell =20, //20
TelNumber2_cell =40, //40
DallasAddr_cell =60 //60
};
//------------------- распиновка ног ардуино (плата весий 8.5-8.8)--------------------------------------------------------
#define OutWebasto_12V 2 // это +12В выход потенциала вкл/выкл вебасто (напрямую к котлу без таймера).
#define Dallas_pin 3 // пин шины OneWire для датчиков даллас
#define DopOn 4 // сюда доп канал от сигналки на включение вебасто
#define DopOff 5 // сюда доп канал от сигналки на выключение вебасто
#define Ohrana 6 // Сюда состояние охраны сигналки
#define Trevoga 7 // Сюда состояние тревоги
#define IGN 8 // Сюда состояние зажигания
#define Sost 9 // Сюда состояние вебасто (+12В когда работает)
#define ResetGSM 12 // пин ресета GSM подключен к реле, разрывающее питание модуля.
#define Eng 14 // (А0) Сюда состояние работы ДВС
#define StatusWebastoLED 15 // (А1) пин LED индикация включенности котла
#define StartButtonpin 16 // (А2) пин тактовой кнопки вкл/выкл котла
#define DTR 17 // пин (А3), управляющий энергосберегающим режимом GSM модуля
#define StartEng 18 // (A4) это импульсный минусовой выход вкл/выкл ДВС. подключать на вход событий сиги.
#define OutWebasto_GndImp 19 // (A5) это импульсный минусовой выход вкл/выкл вебасто (к впайке к кнопке таймера).
#define Voltmeter_pin A7 // пин, которым измеряем напряжение питания
#define StartButton 0 // программный номер тактовой кнопки вкл/выкл котла
#define DopOnButton 1 // программный номер тактовой кнопки (допканала) вкл котла
#define DopOffButton 2 // программный номер тактовой кнопки (допканала) выкл котла
const bool RelayON = 1; // логика управления реле ресета GSM, в данном случае включается высоким уровнем на пине
#define GSM_RX 10 // пин софт RX Arduino для соединения с TX модуля SIM800
#define GSM_TX 11 // пин софт TX Arduino для соединения с RX модуля SIM800
//------------------------------------для GSM модуля----------------------------------------------------------------------
#include <SoftwareSerial.h>
SoftwareSerial SIM800 (GSM_RX, GSM_TX);//Rx, Tx //UART для соединения с GSM модулем
String currStr = "";
String TelNumber[] = {"", "", "", ""};
byte isStringMessage = 0;
byte KTOreport = 1; // флаг кто запросил отчет о запуске котла или ДВС
byte KTOzapros = 0; // флаг кто запросил баланс или запрос параметров
byte ResetNumber = 0; // количество ресетов GSM модуля для статистики (хранится в еепром)
byte SizeTelNumber = 12; // количество символов в номере телефона
int16_t Refresh_time = 1200; //сек
uint32_t prev_refreshMQTT = 0;
uint32_t prevGSMnastr = 0;
uint32_t interval_doprosGSM = 123000UL;
uint16_t delayATcommand = 5000;
byte fails = 0;
byte failresets = 0;
bool settingGSM = 1;
enum gsmstatus_ {WaitGSM, echoOFF, EnergySave, Head, setText, setProgctrl, closeIncoming, newMessage, delSMS, setGPRS, setAccPoint,
setGPRSconnect, setBrokerconnect, setAuthPack, setSubPack, setPubPack
};
byte gsmstatus = WaitGSM;
//------------------- для шины 1-wire и датчиков DS18B20---------------------------------------------------------------------
#include <OneWire.h> // библиотека для DS18B20
OneWire ds(Dallas_pin); // датчики DS18B20 на нужный пин
enum TempC {VyhlopC, EngineC, UlicaC, SalonC, size_arrayTemp}; // перечисление нужных температур (в конце размер массива температур)
// ниже соответствие адресов датчиков различным температурам
byte DS18B20 [size_arrayTemp][10] = {
{0x28, 0xFF, 0xB2, 0xB5, 0xC1, 0x17, 0x05, 0xD1, VyhlopC, -100},
{0x28, 0xFF, 0xD3, 0xE2, 0xC1, 0x17, 0x04, 0x0D, EngineC, -100},
{0x28, 0xFF, 0xF8, 0xBC, 0xC1, 0x17, 0x04, 0x48, UlicaC, -100},
{0x28, 0xFF, 0x3F, 0xB7, 0xC1, 0x17, 0x05, 0xF1, SalonC, -100}
};
byte delta = 50; // разница температур выхлопа и улицы, выше которой считается, что пламя в котле есть.
int8_t HeaterC = -50;
//---------------------------для организации W-BUS и различные таймеры-----------------------------------------------------------
#include <Button.h>
Button test;
#define K_LINE Serial //UART для соединения с шиной котла
#define TX 1
#define NEED 1
#define READY 10
// команды для котлов ЭВО
byte StartByte = 0x20;
const byte HEATER_BEGIN[] {0x51, 0x0A};
byte HEATER_START[] {StartByte, 0x3B};
byte HEATER_PRESENCE[] {0x44, StartByte, 0x00};
const byte HEATER_STOP[] {0x10};
const byte HEATER_STATUS_VEVO[] {0x50, 0x05};
const byte HEATER_STATUS_EVO[] {0x50, 0x30, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x1E, 0x32};
const byte HEATER_DTC_REQUEST[] {0x56, 0x01};
const byte HEATER_DTC_ERASE[] {0x56, 0x03};
// команды для котлов ТТС/TTE
const byte START_SESSION[] {0x81};
const byte REQUEST_2A10101[] {0x2A, 0x01, 0x01};
const byte REQUEST_2A10102[] {0x2A, 0x01, 0x02};
const byte REQUEST_2A10105[] {0x2A, 0x01, 0x05};
const byte REQUEST_DTC[] {0xA1};
const byte START_TTC[] {0x31, 0x22, 0xFF};
const byte STOP_TTC[] {0x31, 0x22, 0x00};
enum needAction_ {NO_ACTION, NEED_SMSZAPROS, NEED_SERVICEINFO, NEED_DTCCLEAR};// возможные действия, стоящие в очереди
byte needAction = NO_ACTION; // переменная действия, стоящего в очереди
enum ProtocolSTATUS_ {STATUSBUS, ANALOG}; // возможные протоколы чтения статуса котла
enum ProtocolSTART_ {STARTBUS, IMPULSE, POTENCIAL}; // возможные протоколы запуска котла
enum Heater_ {TTC_E, VEVO, EVO, HYDRONIC}; // тип котла
byte ProtocolSTATUS = STATUSBUS;
byte ProtocolSTART = STARTBUS;
byte Heater = EVO;
bool noData = 0; // флаг пришли ли данные от котла после запроса.
byte w_bus_init = 0; //состояние инициализация шины w-bus (25мс LOW, 25мс HIGH для ЭВО
// либо 300ms LOW, 50ms HIGH, 25ms LOW, 3025ms HIGH для TTC
byte requiredmessage = 1; //флаг, что отправляем в данный момент поддержание старта, запрос параметров или запрос ошибок
byte StartMessageRepeat = 0; //количество отправленных сообщений на старт котла
byte StopMessageRepeat = 4; //количество отправленных сообщений на остановку котла
byte TimeWebasto = 30; //время работы котла, = 30мин
uint32_t currmillis = 0; // снимок системного времени
uint32_t Prev_PeriodW_BusMessage = 0; //переменная для таймера периодической отправки сообщений состояния котла в шину W-Bus
uint32_t Prev_PeriodW_BusStartStop = 0; //переменная для таймера периодической отправки сообщений старта/стопа котла в шину W-Bus
uint32_t prevdelSMS = 0; //переменная для таймера периодического удаления СМС
uint32_t prevVpit = 0; //переменная для таймера периодического измерения напряжения АКБ
uint32_t prevInitreset = 0; //переменная для таймера сброса инита шины
bool Initreset = 0; //переменная для таймера сброса инита шины
uint32_t timerInit = 0; bool timerInitflag = 0; //для таймера инита шины W-BUS
uint32_t prevNeedTimer = 0; bool NeedTimer = 0; //для таймера задержки функций SMSzapros() и ServiceINFO() на время обновления параметров по шине
uint32_t prevReportEngine = 0; bool reportEngine = false; //таймер задержки на отправку отчёта о запуске двигателя
uint32_t prevReport = 0;bool report = false; //таймер задержки на отправку отчёта о запуске котла
uint32_t last_Flame = 0; //для таймера сброса флага пламени, если нет ответов от котла
uint32_t prevGND_impulse = 0; bool GND_impulse_timer = 0; //для таймера создания импульса GND - для протокола запуска котла импульсом GND
uint32_t prevStartEng=0; bool StartEng_timer=0; //для таймера - старт двигателя: минусовой импульс 1.5 сек на вход событий сигналки для запуска ДВС
uint32_t prevWorkCycleHeater; bool WorkCycleHeater_timer=0; //для таймера отсчёта цикла работы котла
//---------------------------------Основные переменные--------------------------------------------------------------------------------
bool webasto = 0; // флаг команды на работу Webasto. 0 - котел выключен, 1 - котел включен
bool startWebasto_OK = 0; // флаг успешного запуска котла
float Vpit = 0.0; // Измеряемое напряжение на выходе ИБП
bool engine =0; // флаг работает ли ДВС или нет
bool ignition=0; // флаг включено ли зажигание или нет
bool ohrana=0; // флаг включена ли охрана или нет
bool trevoga=0; // флаг включена ли тревога или нет
bool alarmSMS = 0; // флаг отправлена ли смс о тревоге или нет
bool waterpump = 0; // флаг работы циркуляционного насоса
bool plug = 0; // флаг работы штифта накаливания
bool airfan = 0; // флаг работы нагнетателя воздуха
bool fuelpump = 0; // флаг работы топливного насоса
bool blowerfan = 0; // флаг работы вентилятора печки автомобиля
byte DTC[7] ={0}; // коды неисправностей котла
//---------------------------СТАРТОВЫЙ ЦИКЛ--------------------------------------------------------------------------------------------
void setup()
{
delay (4500);
test.NO();
test.pullUp();
test.duration_bounce ( 50);
test.duration_click_Db ( 250);
test.duration_inactivity_Up(5000);
test.duration_inactivity_Dn(1000);
test.duration_press ( 500);
test.button(StartButtonpin, DopOn, DopOff);
pinMode (DopOn, INPUT_PULLUP);
pinMode (DopOff, INPUT_PULLUP);
pinMode (Sost, INPUT_PULLUP);
pinMode (Ohrana, INPUT_PULLUP);
pinMode (Trevoga, INPUT_PULLUP);
pinMode (IGN, INPUT_PULLUP);
pinMode (Eng, INPUT_PULLUP);
pinMode (OutWebasto_12V, OUTPUT); digitalWrite (OutWebasto_12V, LOW);
pinMode (StartEng, OUTPUT); digitalWrite (StartEng, LOW);
pinMode (13, OUTPUT); digitalWrite (13, LOW);
pinMode (StatusWebastoLED, OUTPUT); digitalWrite (StatusWebastoLED, LOW);
pinMode (OutWebasto_GndImp, OUTPUT); digitalWrite (OutWebasto_GndImp, HIGH);
pinMode (DTR, OUTPUT); digitalWrite (DTR, LOW ); // делаем низкий для вывода GSM из "спячки"
pinMode (ResetGSM, OUTPUT); digitalWrite (ResetGSM, !RelayON); // реле ресет на данный момент делаем "неактивно"
SIM800.begin(19200); // сериал соединение для gsm модуля
SizeTelNumber = EEPROM.read(sizeNumber_cell);
if (SizeTelNumber>25)while(1);
TimeWebasto = EEPROM.read(TimeWebasto_cell);
ProtocolSTART = EEPROM.read(ProtocolSTART_cell);
ProtocolSTATUS = EEPROM.read(ProtocolSTATUS_cell);
ResetNumber = EEPROM.read(ResetNumber_cell);
StartByte = EEPROM.read(StartByte_cell);
Heater = EEPROM.read(Heater_cell);
delta = EEPROM.read(delta_cell);
for (int i=0; i<SizeTelNumber; i++)
{
TelNumber[0]+= '0';
TelNumber[3]+= '0';
TelNumber[1]+= (char)EEPROM.read (i+TelNumber1_cell);
TelNumber[2]+= (char)EEPROM.read (i+TelNumber2_cell);
}
// ниже читаем из еепром адреса датчиков температуры даллас
for (byte i = 0; i<size_arrayTemp; i++)
{
for (byte k=0; k<9; k++) DS18B20 [i][k] = EEPROM.read(DallasAddr_cell+i*10+k);
}
if (Heater == EVO || Heater == VEVO) K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == TTC_E) K_LINE.begin(10400);
//for (byte i=0; i<20; i++) {digitalWrite (13, !digitalRead(13)); delay (80);}
//digitalWrite (13,0);
fails = 3;
}
//------------------------------------------ЛУП-----------------------------------------------------------------------------------
void loop() {
currmillis = millis();
test.read();
digitalWrite (StatusWebastoLED, webasto);
//digitalWrite (13, startWebasto_OK);
//digitalWrite (13, webasto);
if (settingGSM) NastroykaGSM ();
else if (fails >= 3) settingGSM = 1;
//если MQTT активен
if (failresets < 5)
{
static bool last_MQTTwebasto = 0;
static bool last_startwebasto_OK = 0;
if (last_MQTTwebasto != webasto || last_startwebasto_OK != startWebasto_OK){ MQTTsendDatastream(); last_MQTTwebasto = webasto; last_startwebasto_OK = startWebasto_OK;}
refreshDataMQTT ();
}
if (!webasto)
{
static uint32_t prevTestModem = 0;
if (!settingGSM && currmillis - prevTestModem > interval_doprosGSM)
{
digitalWrite (DTR, LOW);
delay (150);
SIM800.println (F("AT"));
prevTestModem = currmillis;
fails++;
interval_doprosGSM = 10000;
}
}
if (ProtocolSTATUS==ANALOG) {if (Temper(VyhlopC) - Temper(UlicaC) > delta) startWebasto_OK = 1;
else startWebasto_OK = 0;}
if (ProtocolSTART==IMPULSE) {
webasto = !digitalRead (Sost);
static bool previos_webasto = 0;
if (previos_webasto!=webasto)
{
if (!previos_webasto && webasto) Refresh_time = 60;
if (previos_webasto && !webasto) Refresh_time = 1200;
previos_webasto=webasto;
}
}
if (!ohrana) alarmSMS = false;
if (trevoga && !alarmSMS) AlarmSMS ();
readModem();
DelSMS();
Heater_BUS();
timers_and_buttons ();
izmereniya();
}
//-----------------------------------------------------------конец луп-------------------------------------------------------------
void izmereniya() {
engine = !digitalRead (Eng);
ignition= !digitalRead (IGN);
ohrana= !digitalRead (Ohrana);
trevoga= !digitalRead (Trevoga);
if (currmillis-prevVpit>7000){
//измерение напряжения борт сети
if (ProtocolSTATUS==ANALOG)
{
Vpit = (analogRead(Voltmeter_pin) * 4.13) / 1024;
Vpit = Vpit / (9700.0/(98930.0+9700.0)); // По формуле Vpit = vout / (R2/(R1+R2))
if (Vpit<0.09) Vpit=0.0; // Округление до нуля
}
// ниже измерение датчиков даллас
static bool y=0; // флаг работы: запрос температуры или её чтение
y=!y;
if (y) {ds.reset(); // сброс шины
ds.write(0xCC); // обращение ко всем датчикам
ds.write(0x44); // начать преобразование (без паразитного питания)
}
else {
for (byte i=0; i<size_arrayTemp; i++){
int Temper_ = 20; byte buff[9];
ds.reset();
ds.select(DS18B20[i]);
ds.write(0xBE); // чтение регистров датчиков
for (byte j=0; j<9; j++) buff[j]=ds.read(); // читаем все 9 байт от датчика
ds.reset();
if (OneWire::crc8(buff, 8) == buff[8]){ // если контрольная сумма совпадает
Temper_ = buff[0]|(buff[1]<<8); // читаем температуру из первых двух байт (остальные были нужны только для проверки CRC)
Temper_ = Temper_ / 16;
if (Temper_<150 && Temper_>-55) DS18B20[i][9] = Temper_;
}
else DS18B20[i][9] = -101; // если контрольная сумма не совпала, пусть t будет -101 градус.
}}
prevVpit=currmillis;
}}
int8_t Temper (const byte &addressTemp) {for(byte j=0; j<size_arrayTemp; j++){if(DS18B20[j][8]==addressTemp)return(int8_t)DS18B20[j][9];} return-99;}
void timers_and_buttons (){
// опрос допканалов от сигнализации включения/выключение котла и таймер импульса старт/стоп котла
if (GND_impulse_timer && currmillis - prevGND_impulse > 800) {digitalWrite (OutWebasto_GndImp, HIGH); GND_impulse_timer=false;}
//ниже для таймера создания импульса на старт ДВС
if (StartEng_timer && currmillis - prevStartEng > 1500) {digitalWrite (StartEng, LOW); StartEng_timer=false;}
//ниже для таймера старта котла по шине и аналогу
if (WorkCycleHeater_timer && currmillis - prevWorkCycleHeater > (uint32_t)TimeWebasto * 60000UL) {StopWebasto();}
// таймер отправки отчета об успешности запуска котла (отчёт через 6 мин после старта)
if(webasto && report && currmillis - prevReport > 360000UL)
{ report = false; Queue(NEED_SMSZAPROS); }
// таймер отправки отчета об успешности запуска ДВС (отчёт через 90сек после старта)
if(reportEngine && currmillis - prevReportEngine > 90000ul)
{reportEngine = false; Queue(NEED_SMSZAPROS);}
//если нажали на допканал - делаем соответствующее состояние котла
if (test.event_click_Dn (DopOnButton) && !webasto) {StartWebasto(); KTOreport = 1;}
if (test.event_click_Dn (DopOffButton) && webasto) {StopWebasto();}
//если нажали тактовую кнопку меняем состояние котла на противоположное
if (test.event_press_short (StartButton))
{
if (!webasto) {StartWebasto(); report = false;}
else {StopWebasto();}
}
}
void startSMS(byte stat) //__________________Цикл подготовки модуля к отправке СМС-сообщений по первому номеру
{
if (stat==0) stat = KTOreport;
digitalWrite (DTR, LOW); // выводим из спячки GSM модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.print(F("AT+CMGS=\"")); SIM800.print(TelNumber[stat]); SIM800.println("\"");
delay(200);
}
void EndSMS ()
{
SIM800.println((char)26); // Команда отправки СМС
delay(3500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
}
void NastroykaGSM () {
if (fails >= 3) Reset();
else if (currmillis - prevGSMnastr > delayATcommand)
{
fails ++;
if (gsmstatus == WaitGSM) SIM800.println (F("AT"));
else if (gsmstatus == EnergySave) SIM800.println (F("AT+CSCLK=1"));
else if (gsmstatus == Head) SIM800.println (F("AT+CIPHEAD=1"));
else if (gsmstatus == echoOFF) SIM800.println (F("ATE0"));
else if (gsmstatus == setText) SIM800.println (F("AT+CMGF=1"));
else if (gsmstatus == setProgctrl) SIM800.println (F("AT+IFC=0, 0"));
else if (gsmstatus == closeIncoming) SIM800.println (F("AT+GSMBUSY=1"));
else if (gsmstatus == newMessage) SIM800.println (F("AT+CNMI=1,2,2,1,0"));
else if (gsmstatus == delSMS) SIM800.println (F("AT+CMGDA=\"DEL ALL\""));
else if (gsmstatus == setGPRS) SIM800.println (F("AT+SAPBR=3,1, \"Contype\",\"GPRS\""));
else if (gsmstatus == setAccPoint) SIM800.print (F("AT+SAPBR=3,1, \"APN\",")), SIM800.println (ACCESSPOINT);
else if (gsmstatus == setGPRSconnect) SIM800.println (F("AT+SAPBR=1,1"));
else if (gsmstatus == setBrokerconnect) SIM800.print (F("AT+CIPSTART=\"TCP\",")), SIM800.println (SERVERNAME_PORT);
else if (gsmstatus == setAuthPack) AUTHsend ();
else if (gsmstatus == setSubPack) SUBsend("h/#");
else if (gsmstatus == setPubPack) MQTTsendDatastream();
delayATcommand = 5000;
prevGSMnastr = currmillis;
}
}
void DelSMS ()
{
if (currmillis - prevdelSMS > 7200000ul){ //раз в 2 часа
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGDA=\"DEL ALL\"\r")); // удаляем все смс, ки
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
prevdelSMS = currmillis;}
}
//void SMSzapros_ () {if (webasto)SMSzapros(); else {NeedTimer = 1; prevNeedTimer = currmillis; needAction = NEED_SMSZAPROS; w_bus_init = 1;}}
void Queue (const byte &actioN) {NeedTimer = 1; prevNeedTimer = currmillis; needAction = actioN; if (!webasto) w_bus_init = 1; }
void readModem() //_____Цикл чтения входящих СМС-сообщений______________
{
static uint32_t timerMQTTreconnect = 0;
static bool TimerMQTTreconnect = 0;
// если было неудачное подключение к MQTT, ждём час и снова пробуем
if (TimerMQTTreconnect && currmillis - timerMQTTreconnect > 3600000UL)
{
TimerMQTTreconnect = 0;
fails = 3;
settingGSM = 1;
}
if (!SIM800.available()) return;
static bool SaveNumber2 = 0; // флаг когда необходима запись номера#2, он true
char currSymb = SIM800.read();
if ('\r' == currSymb || '&' == currSymb)
{
if (isStringMessage!=0&&isStringMessage!=3) //если текущая строка - SMS-сообщение, отреагируем на него соответствующим образом
{
if (!currStr.compareTo(F("ZAPROS"))) {Queue(NEED_SMSZAPROS);} // Передача параметров по СМС
else if (!currStr.compareTo(F("Service-info"))) {Queue(NEED_SERVICEINFO);} // Передача сервисной информации по СМС
else if (!currStr.compareTo(F("Erase DTC"))) {Queue(NEED_DTCCLEAR);} // Запрос на стриание ошибок
else if (!currStr.compareTo(F("ZAPROSTEL"))) {SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
else if (!currStr.compareTo(F("GSMResets-0"))) {ResetNumber=0; EEPROM.write (ResetNumber_cell, ResetNumber); Queue(NEED_SERVICEINFO);} //сброс счетчика ресетов GSM модуля
else if (!currStr.compareTo(F("Version"))) {startSMS(isStringMessage); SIM800.println (ver); EndSMS ();} //запрос версии ПО
else if (!currStr.compareTo(F("Signal-level"))) {digitalWrite (DTR, LOW); delay (150); SIM800.println(F("AT+CSQ")); digitalWrite (DTR, HIGH);} // запрос уровня сигнала GSM
else if (!currStr.compareTo(F("Webasto-ON"))) { startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (!webasto) {StartWebasto (); KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println (F("vkluchena")); EndSMS();}
else if (!currStr.compareTo(F("Webasto-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (webasto)StopWebasto (); // если получили команду на выключение и вебаста в настоящий момент включена - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("otkluchena"));EndSMS();}
// если получили команду на включение ДВС и он в настоящий момент выключен - включаем
else if (!currStr.compareTo(F("Engine-ON"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (!engine) { digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = true; prevReportEngine = currmillis; KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println(F("start")); EndSMS();}
else if (!currStr.compareTo(F("Engine-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (engine){ digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = false;} // если получили команду на выключение ДВС и он в настоящий момент работает - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("ostanovlen")); EndSMS();}
else if (!currStr.compareTo(F("Impulse"))) {if (!webasto) {ProtocolSTART = IMPULSE; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk GND_impulse")); EndSMS();}}
else if (!currStr.compareTo(F("Startbus"))) {if (!webasto) {ProtocolSTART = STARTBUS; EEPROM.write(ProtocolSTART_cell,ProtocolSTART); webasto = 0;
startSMS(isStringMessage); SIM800.println(F("zapusk BUS")); EndSMS();}}
else if (!currStr.compareTo(F("Potencial"))) {if (!webasto) {ProtocolSTART = POTENCIAL; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk +12V Potencial")); EndSMS();}}
else if (!currStr.compareTo(F("DallasAddr"))) {startSMS(isStringMessage); SMSDallasAddr(); EndSMS();}
else if (currStr.endsWith(F("Status"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= STATUSBUS && st<=ANALOG )ProtocolSTATUS = st; EEPROM.write(ProtocolSTATUS_cell,ProtocolSTATUS);
startSMS(isStringMessage); SIM800.print (F("Status: "));
if (ProtocolSTATUS == 0)SIM800.println(F("BUS"));
else if (ProtocolSTATUS == 1)SIM800.println(F("Analog"));
EndSMS();}}
else if (currStr.endsWith(F("HeaterType"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= TTC_E && st<=HYDRONIC) Heater = st; EEPROM.write(Heater_cell,Heater);
startSMS(isStringMessage); SIM800.print (F("Heater: "));
if (Heater == 0)SIM800.println(F("TTC_E")), K_LINE.end(), K_LINE.begin(10400);
else if (Heater == 1)SIM800.println(F("VEVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 2)SIM800.println(F("EVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 3)SIM800.println(F("HYDRONIC"));
EndSMS();}}
else if (currStr.endsWith(F("ADDR"))) {if (!webasto) { startSMS(isStringMessage); byte savadr = ConvertAddr();
if (savadr>=0 && savadr<=3) {SIM800.print(F("address ")); TempName(savadr); SIM800.println (F(" saved OK!"));
SMSDallasAddr();
}
else SIM800.println(F("address is incorrect!"));
EndSMS(); }}
else if (currStr.endsWith(F("Delta"))) {if (!webasto) {delta = currStr.toInt(); //
EEPROM.write(delta_cell, delta); startSMS(isStringMessage);
SIM800.print(F("DeltaT: ")); SIM800.print(delta); SIM800.print(F("*C")); EndSMS();}}
else if (currStr.endsWith(F("min"))) {if (!webasto) {TimeWebasto = currStr.toInt(); // для задания время цикла работы отправить сообщение вида "25 min", где 25 время работы в мин
if (TimeWebasto>59) TimeWebasto = 59;
if (TimeWebasto<=15) TimeWebasto = 15;
EEPROM.write(TimeWebasto_cell,TimeWebasto);
startSMS(isStringMessage); SIM800.print(F("Webasto time: ")); SIM800.print(TimeWebasto); SIM800.print(F("min")); EndSMS();}}
else if (currStr.endsWith(F("StartByte"))) {byte Z =currStr.toInt(); if (Z>=0x14 && Z<=0x17) StartByte= Z+12;
startSMS(isStringMessage); SIM800.print(F("StartByte: ")); SIM800.print(StartByte, HEX); SIM800.print(F("h")); EndSMS();}
else if (!currStr.compareTo(F("ResetNumbers"))) {if (isStringMessage == 1) {startSMS(isStringMessage); SIM800.println(F("Phone numbers are erased")); EndSMS();
TelNumber[1] = TelNumber[0]; TelNumber[2] = TelNumber[0]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[1][i]); EEPROM.write (i+TelNumber2_cell, TelNumber[2][i]); }}}
else if (!currStr.compareTo(F("WriteNumber2"))&& isStringMessage == 1) {
SaveNumber2 = 1; startSMS(isStringMessage); SIM800.println(F("Otpravte lyuboye SMS s nomera#2 dlya sohraneniya nomera")); EndSMS();}
else if (!currStr.compareTo(F("Balance"))) SMSbalance();
isStringMessage = 0;
}
else if (isStringMessage==3){ if (!currStr.compareTo(F("WriteNumber1"))) { TelNumber[1] = TelNumber[3]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[3][i]);}
startSMS(1); SIM800.println(F("Tel Number#1 is saving in memory")); SIM800.print("Tel#1: "); SIM800.println (TelNumber[1]); EndSMS();
}
else if (!currStr.compareTo(F("ZAPROS"))) { SMSzapros();}
else if (!currStr.compareTo(F("ZAPROSTEL"))) { SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
isStringMessage = 0;
}
else if (isStringMessage==0) { if (TelNumber[1]!=TelNumber[0] && !SaveNumber2){
if (currStr.startsWith("+CMT: \""+TelNumber[1])) { isStringMessage = 1; KTOzapros = 1; }
else if (currStr.startsWith("+CMT: \""+TelNumber[2])) { isStringMessage = 2; KTOzapros = 2; }
else if (currStr.startsWith("+CUSD: 0,")) //если текущая строка начинается с "+CUSD",то следующая строка является запросом баланса
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
else if (currStr.startsWith(F("+CSQ:"))) //если текущая строка начинается с "+CSQ",то значит был запрос на уровень сигнала GSM, отправим ответ запрашивающему
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
}
else if (currStr.startsWith(F("+CMT:")) && !SaveNumber2) { isStringMessage = 3; for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7];}}
else if (currStr.startsWith(F("+CMT:")) && SaveNumber2) { for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7]; EEPROM.write (i+TelNumber2_cell, TelNumber[3][i]);} TelNumber[2] = TelNumber[3];
startSMS(2); SIM800.println(F("Vash nomer sochranyon kak Number#2 v pamyati!"));
SIM800.print(F("Tel#1: ")); SIM800.println(TelNumber[1]); SIM800.print(F("Tel#2: ")); SIM800.println (TelNumber[2]); EndSMS(); SaveNumber2 = 0; }
// ----------------------------данные для MQTT
if (currStr.indexOf(F("CLOSED")) > -1 || currStr.indexOf(F("ERROR")) > -1) {
fails = 3;
settingGSM = 1;
}
if (currStr.indexOf(F("OK")) > -1) {
if (!settingGSM){ if (failresets > 4 || (failresets <= 4 && gsmstatus >= setPubPack)) {fails = 0; digitalWrite (DTR, HIGH); interval_doprosGSM = 123000UL;}}
if (settingGSM && gsmstatus > WaitGSM && gsmstatus < setBrokerconnect) {
// если подключиться к MQTT серверу не удалось, оставляем настройки только для СМС
// и запускаем таймер на час, чтобы через час ещё раз пробовать:
if (gsmstatus == delSMS && failresets > 4)
{settingGSM = 0;
digitalWrite (DTR, HIGH);
timerMQTTreconnect = currmillis;
TimerMQTTreconnect = 1;
}
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 400;
}
}
if (currStr.indexOf(F("CONNECT")) > -1) if (gsmstatus == setBrokerconnect) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SMS Ready")) > -1) if (gsmstatus == WaitGSM) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SEND OK")) > -1) {
fails = 0;
if (settingGSM && gsmstatus >= setAuthPack && gsmstatus <= setPubPack) {
if (gsmstatus == setPubPack) {
settingGSM = 0;
failresets = 0;
} delayATcommand = 300;
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
}
}
//-------------------------топики MQTT, принимаемые от брокера
if (currStr.indexOf(F("h/ctrl")) > -1)
{
bool comanda = currStr.substring(currStr.indexOf(F("h/ctrl")) + 6).toInt();
if (comanda) {StartWebasto(); report = false;}
else {StopWebasto();}
}
if (currStr.indexOf(F("h/time/ctrl")) > -1) {
String TimeW = currStr.substring(currStr.indexOf(F("h/time/ctrl")) + 11);
TimeWebasto = TimeW.toInt();
EEPROM.write(TimeWebasto_cell,TimeWebasto);
PUBsend("h/time/stat", TimeW, 0);
remTime(0) ;
}
if (currStr.indexOf(F("h/refr0")) > -1) MQTTsendDatastream();
// ------------------------конец данных для MQTT
}
currStr = "";
}
else if ('\n' != currSymb && '$' != currSymb) currStr += String(currSymb);
if (currStr.length()>60) currStr += "\r\n";
}
byte ConvertAddr() {
byte addrbuf[9];
byte Cs=0;
for (byte i = 0; i<9; i++) {
char str[]=" ";
for (byte k = 0; k<2; k++) str[k] = currStr[3*i+k];
addrbuf[i] = strtol(str,NULL,HEX);
if (i<7) Cs+= addrbuf[i] = strtol(str,NULL,HEX);
}
if (OneWire::crc8(addrbuf, 7) == addrbuf[7] && addrbuf[8]>=0 && addrbuf[8]<=3) {
for (byte i = 0; i<size_arrayTemp; i++) {
if (addrbuf[8]==DS18B20[i][8]) {for (byte k=0; k<9; k++) DS18B20 [i][k] = addrbuf[k], EEPROM.write (DallasAddr_cell+addrbuf[8]*10+k, addrbuf[k]);}
}
return addrbuf[8];
}
else return 20;
}
void SMSDallasAddr(){
SIM800.println (F("Dallas Addresses:"));
for (byte i = 0; i<size_arrayTemp; i++) {
TempName(DS18B20[i][8]); SIM800.println();
for (byte k = 0; k<8; k++) {if (DS18B20[i][k]<=0x0F)SIM800.print(F("0")); SIM800.print(DS18B20[i][k],HEX); SIM800.print(F(" "));}
SIM800.println();
}
}
void SMSzaprosTEL(){
startSMS(isStringMessage);
PrintNumbers ();
EndSMS();
}
void SMSbalance() {
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.println (F("AT+CUSD=1,\"#100#\"")); // команда на замену на транслит *111*6*2# у МТС
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
}
void AlarmSMS() {for (byte i = 0; i<2; i++) {startSMS(i+1); SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!")); EndSMS();} alarmSMS = true;}
void PrintNumbers () {for (byte i=0; i<2; i++) {SIM800.print(F("Tel#")); SIM800.print (i+1); SIM800.print(F(" ")); SIM800.println(TelNumber[i+1]);}}
void TempName (const byte &_address_) { if (_address_== VyhlopC) SIM800.print(F("Vyhlop: "));
else if (_address_== EngineC) SIM800.print(F("Engine: "));
else if (_address_== UlicaC) SIM800.print(F("Ulica: "));
else if (_address_== SalonC) SIM800.print(F("Salon: "));
}
void SMSzapros()
{
startSMS(isStringMessage);
if (isStringMessage == 3){
SIM800.println (F("Tel.number#1 not has been save in memory"));
SIM800.println (F("For save Tel#1 send SMS command \"WriteNumber1\""));
PrintNumbers ();
}
else {
SIM800.print (F("Webasto ")); on_off (webasto);
if (webasto) {
SIM800.print (F("StartWebasto "));
if (startWebasto_OK) SIM800.println (F("OK"));
else SIM800.println (F("FAIL"));}
SIM800.print (F("Engine ")); on_off (engine);
SIM800.print (F("IGN ")); on_off (ignition);
SIM800.print (F("Ohrana ")); on_off (ohrana);
if (trevoga) SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!"));
SIM800.print(F("Battery: ")); if ((needAction>0 && !noData) || needAction == 0 || ProtocolSTATUS == ANALOG) {SIM800.print (Vpit,1); SIM800.println(F(" V"));}
else if (needAction>0 && noData && ProtocolSTATUS != ANALOG) SIM800.println(F(" No Data"));
SIM800.println(F("Temperatures:"));
if (ProtocolSTATUS != ANALOG){
SIM800.print(F("Heater: ")); if ((needAction>0 && !noData) || needAction == 0) {SIM800.print (HeaterC); grad ();}
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
// ниже распечатаем все температуры
for (byte i = 0; i<size_arrayTemp; i++) {
if (DS18B20[i][8]==VyhlopC && ProtocolSTATUS != ANALOG) {}
else {TempName(DS18B20[i][8]); SIM800.print (Temper(DS18B20[i][8])); grad ();}}
if (ProtocolSTATUS == STATUSBUS){
SIM800.print(F("Errors: ")); if ((needAction>0 && !noData) || needAction == 0) SIM800.println (DTC[0]);
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
}
EndSMS();
}
void on_off (const bool &stat) {if (stat) SIM800.println (F("ON")); else SIM800.println (F("OFF")); }
void grad () {SIM800.println (F("*C")); }
void ServiceINFO(){
startSMS(isStringMessage);
SIM800.print(F("Heater: "));
if (Heater==TTC_E) SIM800.println(F("TTC/E"));
else if (Heater==VEVO) SIM800.println(F("VEVO"));
else if (Heater==EVO) SIM800.println(F("EVO"));
else if (Heater==HYDRONIC) SIM800.println(F("HYDRONIC"));
SIM800.print(F("Start: "));
if (ProtocolSTART==IMPULSE) SIM800.println(F("GND Imp"));
else if (ProtocolSTART==STARTBUS) {SIM800.print(F("BUS"));
if (Heater== VEVO || Heater== EVO) {SIM800.print(F(" 0x")); SIM800.println (StartByte, HEX);}
else SIM800.println();}
else if (ProtocolSTART==POTENCIAL) SIM800.println(F("Potencial+12V"));
SIM800.print(F("Status: "));
if (ProtocolSTATUS==ANALOG) SIM800.println(F("ANALOG"));
else if (ProtocolSTATUS==STATUSBUS) SIM800.println(F("BUS"));
if (ProtocolSTART!=IMPULSE) {SIM800.print(F("Webasto Time: ")); SIM800.print (TimeWebasto); SIM800.println(F("min"));}
SIM800.print(F("Modem Resets: ")); SIM800.println (ResetNumber);
if (ProtocolSTATUS==ANALOG) {SIM800.print(F("DeltaT: ")); SIM800.print(delta);SIM800.println(F("*C"));}
if (ProtocolSTATUS==STATUSBUS){
if (!noData) {
SIM800.print(F(" BurnFAN ")); on_off (airfan);
SIM800.print(F(" WaterPUMP ")); on_off (waterpump);
SIM800.print(F(" PLUG ")); on_off (plug);
SIM800.print(F(" FuelPUMP ")); on_off (fuelpump);
SIM800.print(F(" Blower ")); on_off (blowerfan);
SIM800.print (F("Errors: ")); SIM800.println (DTC[0]);
if (DTC[0] >0) for (byte i=0; i<DTC[0]; i++) {
if (DTC[i+1]<=0x0F) SIM800.print(F("0"));
SIM800.print (DTC[i+1], HEX);
if (bitRead(DTC[6], i+1)) SIM800.println (F(" Active"));
else SIM800.println (F(" Passive")); }
}
else SIM800.println(F("Heater not answer. No Data"));}
EndSMS();
}
void StartWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StartMessageRepeat = 0;
webasto = 1; digitalWrite (OutWebasto_12V, HIGH);
prevWorkCycleHeater=currmillis;
WorkCycleHeater_timer = true;
}
report = true; prevReport = currmillis;
w_bus_init = NEED;
Refresh_time = 60;
}
void StopWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StopMessageRepeat = 0;
webasto = 0; digitalWrite (OutWebasto_12V, LOW);
WorkCycleHeater_timer = false;
}
report = false;
Refresh_time = 1200;
}
void Heater_BUS (){
static byte header = 0; // состояние заголовка
static byte message_size = 0; // размер тела принимаемого сообщения, кол-во байт
static byte j = 2; // инкремент
static byte n = 2;
const byte bufsize = 140; // размер буфера принятого сообщения
static byte buf [bufsize] = {0}; // буфер принятого сообщения
static byte checksum = 0; // контрольная сумма входящего сообщения
static uint32_t prevRESETheader=0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
static bool RESETheader_timer = 0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
if (Heater == EVO || Heater == VEVO){
if (webasto) {
if (StartMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800) && w_bus_init >= 9){
sendMessage (HEATER_START, sizeof(HEATER_START));
StartMessageRepeat++;
Prev_PeriodW_BusStartStop = currmillis;
}
if (StartMessageRepeat>=4){ if (currmillis-Prev_PeriodW_BusMessage>4000) {
if (requiredmessage==1) sendMessage (HEATER_PRESENCE, sizeof(HEATER_PRESENCE));
else if (requiredmessage==2) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));}
else if (requiredmessage==3) sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));
requiredmessage++; if (requiredmessage > 3) requiredmessage = 1;
StopMessageRepeat = 0;
Prev_PeriodW_BusMessage = currmillis;
}}}
else if (StopMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800)){
sendMessage (HEATER_STOP, sizeof(HEATER_STOP));
StopMessageRepeat++;
StartMessageRepeat = 0;
Prev_PeriodW_BusStartStop = currmillis;
}
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = currmillis; timerInitflag = 1;}
if ( timerInitflag && (currmillis - timerInit>24) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
if ( timerInitflag && (currmillis - timerInit>24) && w_bus_init==2) {K_LINE.begin (2400,SERIAL_8E1 );timerInitflag = 0; w_bus_init=9;
if (needAction>0) sendMessage (HEATER_BEGIN, sizeof(HEATER_BEGIN)); }
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (buf[0]==0x4F || buf[0]==0x44){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
else {header = 0; RESETheader_timer=0;}
}
// длина сообщения
else if (header == 1 ){buf[1]=K_LINE.read(); message_size = buf[1]; if (message_size > bufsize) message_size = bufsize; header = 4;j=2;n=2;checksum = 0;}
// пишем тело сообщения
else if (header == 4 && j< message_size+n) {
buf[j] = K_LINE.read();
if (j<message_size+n-1) checksum^= buf[j]; // подсчёт КС
if (j==message_size+n-1) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum^=buf[i]; // прибавляем к контрольной сумме старт байты
// если контрольная сумма верна:
if ( checksum == buf[message_size+n-1]) {
if (Heater == EVO){
if (buf[2]==0xD0 && buf[3]==0x30) { // если получили сообщение с текущими данными
if (buf[4] ==0x0A) {
airfan = (bool)buf[5]; // получаем флаг работы нагнетателя воздуха
plug = (buf[5] & 0x02)>>1; // получаем флаг работы штифта накаливания
fuelpump = (buf[5] & 0x04)>>2; // получаем флаг работы топливного насоса
waterpump = (buf[5] & 0x08)>>3; // получаем флаг работы циркуляционного насоса
blowerfan = (buf[6] & 0x10)>>4; // получаем флаг работы вентилятора печки автомобиля
}
if (buf[6] ==0x0C) HeaterC = buf[7]-50; // получаем температуру антифриза в котле
if (buf[8]==0x0E) {Vpit = (float)(buf[9]<<8|buf[10])/1000.0;} // получаем напряжение борт сети
if (buf[11]==0x10) {startWebasto_OK = (bool)buf[12]; last_Flame = currmillis;} // проверяем наличие пламени
if (needAction>0 && needAction<NEED_DTCCLEAR) {w_bus_init = 12; sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));}
}
if (buf[0]==0x44 && buf[2]==0xC4 && buf[3]==0x00) {w_bus_init = 1; delay (500); K_LINE.flush();} // если получили от котла сбой инита, делаем переинит
if (buf[2]==0xD6 && buf[3]==0x01) {if (needAction>0 && needAction<NEED_DTCCLEAR) w_bus_init = 13;
for (byte h = 0; h< sizeof(DTC); h++) DTC[h]=0;
DTC[0] = buf[4];
for (byte h = 0; h< buf[4]; h++) {DTC[h+1]=buf[h*3+5]; bitWrite (DTC[6], h+1, (buf[h*3+6] & 0x02)>>1);}
}
if (buf[2]==0xD6 && buf[3]==0x03) {startSMS (KTOzapros); SIM800.println (F("DTCs are erased!")); EndSMS(); needAction=0; w_bus_init = 9; NeedTimer = 0; }
if (buf[2]==0xD1 && buf[3]==0x0A) {w_bus_init = 10; }// если получили ответ на сообщение старта коммуникации
}
if (Heater == VEVO){
if (buf[2]==0xD0 && buf[3]==0x05) {startWebasto_OK = buf[7]; last_Flame = currmillis;} // проверяем наличие пламени у VEVO
}
}
// если контрольная сумма не совпала:
//else K_LINE.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=2; checksum = 0;
}
}// end EVO VEVO
else if (Heater == TTC_E){
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = currmillis; timerInitflag = 1;}
if ( timerInitflag && (currmillis - timerInit>299) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
if ( timerInitflag && (currmillis - timerInit>49) && w_bus_init==2) {timerInit = currmillis; digitalWrite(TX, 0); w_bus_init=3; }
if ( timerInitflag && (currmillis - timerInit>24) && w_bus_init==3) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=4; }
if ( timerInitflag && (currmillis - timerInit>3024) && w_bus_init==4) {K_LINE.begin (10400); timerInitflag = 0; w_bus_init=9; sendMessage (START_SESSION, sizeof(START_SESSION));}
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (!bitRead (buf[0],6) && bitRead (buf[0],7)){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
}
// второй старт байт
else if (header == 1){buf[1]=K_LINE.read(); if (buf[1]==0xF1){ header = 2;} else {header = 0; RESETheader_timer=0;}}
// третий старт байт
else if (header == 2){
buf[2]=K_LINE.read();
if (buf[2]==0x51){ message_size = buf[0];
if (buf[0] !=0x80) {header = 4; message_size&=~0x80; j=3; n=3;}
else {header = 3; j=4;n=4;}
if (message_size > bufsize) message_size = bufsize; checksum = 0;} else {header = 0; RESETheader_timer=0; }
}
// если размер сообщения указан в дополнительном байте (нулевой байт 0x80) читаем этот дополнительный байт:
else if (header == 3){
buf[3]=K_LINE.read();
message_size = buf[3];
if (message_size > bufsize) message_size = bufsize;
checksum = 0; header = 4;
}
// пишем тело сообщения
else if (header == 4 && j< message_size+n+1) {
buf[j] = K_LINE.read();
if (j<message_size+n) checksum+= buf[j]; // подсчёт КС
if (j==message_size+n) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum+=buf[i]; // прибавляем к контрольной сумме старт байты
//for (byte i=0; i<message_size+n+1; i++) {Serial.print (buf[i], HEX); Serial.print(" ");}
// если контрольная сумма верна:
if (buf[message_size+n] == checksum) {
// if (buf[n]== 0xC1) Serial.println ("StartSession OK!!!");
}
// если контрольная сумма не совпала:
//else Serial.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=3; checksum = 0;
}
} // end TTC_E
// таймер сброса заголовка если данные оборвались во время приёма заголовка
if (RESETheader_timer && currmillis - prevRESETheader > 500) {RESETheader_timer = 0; header = 0;}
if (currmillis - last_Flame>20000 && ProtocolSTATUS==STATUSBUS) {startWebasto_OK=0; last_Flame = currmillis;} // делаем статус "нет пламени" через 20 сек, если не получаем сообщения от котла
if (Initreset && currmillis - prevInitreset>17000) {Initreset = 0; w_bus_init = 0;} // сброс инита, если прошло более 17 сек после отправки последнего сообщения
if (needAction>0 && needAction < NEED_DTCCLEAR && w_bus_init==10) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));
w_bus_init=11;}
if ((NeedTimer && currmillis - prevNeedTimer>11000) || w_bus_init == 13) {NeedTimer = 0;
if (currmillis - prevNeedTimer>11000) noData = 1;
if (needAction == NEED_SMSZAPROS) SMSzapros();
else if (needAction == NEED_SERVICEINFO) ServiceINFO();
else if (needAction == NEED_DTCCLEAR) {startSMS(KTOzapros); SIM800.println (F("DTC not cleared. Heater no answer!")); EndSMS();}
w_bus_init = 9; noData = 0; needAction = 0;
}
if (needAction==NEED_DTCCLEAR && w_bus_init==10) { sendMessage (HEATER_DTC_ERASE, sizeof(HEATER_DTC_ERASE)); w_bus_init = 20; }
}
void Reset() {
static bool resettimer = 0; // для таймера удерживания реле в режиме сброс питания
static uint32_t resetTimer = 0; // для таймера удерживания реле в режиме сброс питания
if (!resettimer) {
digitalWrite (ResetGSM, RelayON);
resettimer = 1;
resetTimer = currmillis;
}
else if (currmillis - resetTimer > 5000 ) {
resettimer = 0;
digitalWrite (ResetGSM, !RelayON);
digitalWrite (DTR, LOW);
delay (150);
prevGSMnastr = currmillis;
delayATcommand = 150;
fails = 0;
failresets ++; if (failresets > 4) failresets = 5;
gsmstatus = WaitGSM;
settingGSM = 1;
interval_doprosGSM = 123000UL;
ResetNumber++; EEPROM.write (ResetNumber_cell, ResetNumber);
}
}
void sendMessage(const byte *command, const size_t size){
Initreset = 1; prevInitreset = currmillis; // включение таймера сброса инита
if (Heater == TTC_E){
const byte siZe = size+4;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) {Mes[i]=size; bitWrite(Mes[i], 7 , 1);}
if (i==1) Mes[i] = 0x51;
if (i==2) Mes[i] = 0xF1;
if (i==3) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum+=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum+=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
else if (Heater == EVO || Heater == VEVO){
const byte siZe = size+3;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) Mes[i] = 0xF4;
if (i==1) Mes[i]=size+1;
if (i==2) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum^=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum^=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
}
void AUTHsend ()
{
cipsend_begin ();
uint16_t timeBroker = TIMEBROKER;
SIM800.write (0x10);
SIM800.write (strlen(PROTOCOLIUS) + strlen(MQTTNAME) + strlen(MQTTUSER) + strlen(MQTTPASSWORD) + 12);
SIM800.write ((byte)0);
SIM800.write (strlen(PROTOCOLIUS));
SIM800.write (PROTOCOLIUS);
SIM800.write (0x03);
SIM800.write (0xC2);
timeBroker *=60;
byte * timeBr = (byte*)&timeBroker;
SIM800.write(*(timeBr+=1)); // время сеанса связи с брокером, сек (ст. байт)
SIM800.write(*(--timeBr)); // время сеанса связи с брокером, сек (мл. байт)
//SIM800.write (0x04); // время сеанса связи с брокером, сек (ст. байт)
//SIM800.write (0xB0); // время сеанса связи с брокером, сек (мл. байт)
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTNAME));
SIM800.write (MQTTNAME);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTUSER));
SIM800.write (MQTTUSER);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTPASSWORD));
SIM800.write (MQTTPASSWORD);
SIM800.write (0x1A);
}
void PUBsend (char topPub[30], const String Command, const bool &multidata) {
if (!multidata) {cipsend_begin ();}
SIM800.write (0x30);
SIM800.write (strlen (topPub) + Command.length() + 2);
SIM800.write ((byte)0x00);
SIM800.write (strlen (topPub));
SIM800.write (topPub);
for (byte i = 0; i < Command.length(); i++) SIM800.write (Command[i]);
if (!multidata) {SIM800.write(0x1A); digitalWrite (DTR, HIGH);}
}
void SUBsend (char topSub[20]) {
cipsend_begin ();
SIM800.write (0x82);
SIM800.write (strlen(topSub) + 5);
SIM800.write ((byte)0);
SIM800.write (0x01);
SIM800.write ((byte)0);
SIM800.write (strlen(topSub));
SIM800.write (topSub);
SIM800.write ((byte)0x00);
SIM800.write (0x1A);
}
void MQTTsendDatastream()
{
cipsend_begin ();
PUBsend ("h/time/stat", String(TimeWebasto), 1);
remTime(true);
PUBsend ("h/stat", String(webasto) + "&$", 1);
PUBsend ("h/flame", String(startWebasto_OK), 1);
PUBsend ("h/temp/dvs", String(Temper(EngineC)), 1);
PUBsend ("h/temp/heat", String(HeaterC), 1);
PUBsend ("h/temp/ul", String(Temper(UlicaC)), 1);
PUBsend ("h/temp/sal", String(Temper(SalonC)), 1);
PUBsend ("h/napr", String(Vpit), 1);
SIM800.write (0x1A);
prev_refreshMQTT = currmillis;
digitalWrite (DTR, HIGH);
}
void remTime ( bool s) {
int rem = TimeWebasto - ((currmillis - prevWorkCycleHeater) / 60000ul);
PUBsend ("h/remtime", webasto ? String(rem) : "выкл", s);
}
void cipsend_begin ()
{
digitalWrite (DTR, LOW); delay (150);
SIM800.println (F("AT+CIPSEND"));
delay (250);
}
void refreshDataMQTT ()
{
if (currmillis - prev_refreshMQTT > (uint32_t)Refresh_time * 1000)
{
MQTTsendDatastream();
prev_refreshMQTT = currmillis;
fails++;
}
}
И у вас котел то запускается с нашего девайса или нет, я так и не понял? А как с отображением параметра "наличие пламени"? Попробуйте тип котла VEVO, возможно наличие пламени начнет нормально считываться, и котел возможно запустится, если он не запускался. А вот считывание температуры по шине только для EVO пока реализовано.
Сегдня поставил девайс на авто , попробовал с mqtt запустить котел , котел включился , и температуры обновились , вроде все работает, приехал на работу , решил проверить как девайс себя чувствует, температура котла осталась той же на момент выключения котла , я его на 52 градусах заглушил, нажал обновить , обновилась только температура салона, а котел как был на 52 , так и остался , хотя двигатель был прогрет, отправил смс ЗАПРОС , на смс пришла актуальная температура, и после этого стало и на дашборде тоже правильно показывать, как будто через мктт температуру может считать только когда котел работает или после смс с запросом состояния. Ну ладно пошел работать , через час решил еще раз проверить дашборд , но данные не обновлялись, решил посмотреть через вебсокет с компа идут команды или нет, так вот команды на сервер идут, а ответных не было, но через минуту и отвечать девайс начал , но температура котла была не актуальная а последнее показание после выключения котла , попробовал запустить котел с дашборда , котел пустился и температура пошла верная , проверял смсками. Почему то через смс всегда верную температуру котла выдает,( я так проверяю косвенно температуру двигателя , т. к. на двигатель датчик отдельный не цеплял) а вот через мктт только когда котел в работе. Потом вечером попробовал запустить перед уходом с работы , так через мктт не запустился ни в какую , запустил смской , вообще через смс работает стабильно , как и на старом скетче , а вот мктт надо допиливать еще , и еще бы в дашборд добавить напряжение в бортсети , тоже важный параметр. И еще счетчик ресетов модема вырос , на старой прошивке он постоянно в нуле был , значит все стабильно работало, я девайс все лето не выключал , правда котел всего раза два за лето для профилактики запускал , но время от времени смс на девайс отправлял , все равно деньги снимали за симку, так что смысла отключать на лето не было.
ждал когда ты спросишь про обновление температуры котла при выключенном котле через мкютт. Да, я не делал ещё как это сделано через СМС. Если ты помнишь, там нужно ведь будить котёл, и проводить с ним "беседу", чтобы данные вытянуть. Это не очень просто сделать, поэтому я оставил пока так. Слишком много всего нужно было поменять для MQTT и было не до этого. Странно что mqtt через время перестаёт реагировать. У меня на тесте уже третие сутки висит и всё норм - GSM онлайн и отвечает. Правда это не в машине, а дома в уюте и от БП 12В.
Счетчик модема растёт на +1 при каждом перевключении питания всего девайса, это нормально. Т.к. перед подключением к MQTT происходит принудительный рестарт модема.
по поводу нету связи через MQTT через некоторое время возможно происходит следующее. Ты запускаешь котёл , пиктограмма кнопка запуска становится зелёная галка. и всё, окончания работы котла ты не дожидаешься. Потом через время когда ты открываешь программу MQTT DASH там будут последние данные, которые были, т.е. кнопка так и останется зелёная. И если при этом её нажать ничего не произойдёт. Тебе нужно при каждом открытии MQTT Dash сначала нажимать кнопку "обновить данные". И должно сброситься время внизу на всех пиктограммах. и обновится ярлыки. Да бывает не сразу команда по MQTT срабатывает, можно подождать сек 10 и снова попробовать. Но у меня так ооочень редко бывает. Обычно с первого раза. Нужно перенести девайс в машину и тоже потестить в полевых условиях.
И у вас котел то запускается с нашего девайса или нет, я так и не понял? А как с отображением параметра "наличие пламени"? Попробуйте тип котла VEVO, возможно наличие пламени начнет нормально считываться, и котел возможно запустится, если он не запускался. А вот считывание температуры по шине только для EVO пока реализовано.
Да , котёл запускается, не дожидаюсь пламени - тушу его на режиме продувки перед поджигом, т.к. для экспериментов ,чистки и замены подшипника нагнетательного вентилятора снял его с авто и запускаю на столе. Раньше пользовался таймером. И впринципе только пару дней как слепил адаптер на ардуинке начал со скетча 3.65 , а тут как раз и MQTT появился))) спасибо за труд! очень интересное устройство .Продолжу эксперименты и отпишусь.
И хотел ещё сказать, что многие Китайцы халтурят с ардуинками , дома много промини от разных китайских друзей все типа 5ти вольтовые и 16мГц , но одну зашиваю заработала сразу,а одну зашил не запускает котёл, смотрю монитор порта w-bus вообще посылки непонятные идут на запуск котла потом прошил её как 3.3 вольта и 8мГц заработала ))) а ещё одна вообще не хочет работать, постоянно (раз в 30-40 сек) перегружает модуль сим800 и ни на что не реагирует, с трудом с её помощью посмотрел адреса 18b20 (пришлось занизить скорость порта до 2400)
Понятно, я не очень силен в программировании (это мягко сказано) но разве нельзя этот процесс опроса самого котла на предмет температуры , напряжения , датчика пламени и т. д. оформить какой нибудь отдельной функцией , которая будет все это делать и сохранять данные в специальные переменные а эти данные будут забирать уже функции ответа на смс ЗАПРОС или ОБНОВИТЬ mqtt ?
Dushman, вот обновлённый скетч. Исправил ситуацию. Теперь при нажатии кнопки "обновить данные" на MQTT, температура и напряжение принимают значение "wait" что значит ждите. Если через 11 сек котёл не ответил, будет надпись "fail" типа неудача. Если ответил - данные обновятся. Также добавил мониторинг и сброс рестартов модема на через MQTT. Метрики обновил в той инструкции в картинках которая выше.
Скетч v3.83 Update: (v3.82 убрал, там были косяки).
const char ver[] = "Firmware 3.83"; // версия прошивки
//---------------------------Настройки MQTT-----------------------------------------------------------------------------
const char ACCESSPOINT[]= "\"internet.mts.by\""; // точка доступа оператора связи симкарты
const char PROTOCOLIUS[] = "MQIsdp"; // это и оставляем
const char MQTTNAME[] = "MaksVV"; // это смотрим на сервере MQTT
const char MQTTUSER[] = "i f"; // это смотрим на сервере MQTT
const char MQTTPASSWORD[] = "1 ffgW"; // это смотрим на сервере MQTT
const char SERVERNAME_PORT[] = "\"m16.cloudmqtt.com\", \"14685\""; // это смотрим на сервере MQTT
#define TIMEBROKER 80 // продолжительность сессии с брокером , мин (пока такое и оставляем)
//----------------------------------название ячеек еепром----------------------------------------------------------------
#include <EEPROM.h>
enum Cells {
ResetNumber_cell, //0
TimeWebasto_cell, //1
ProtocolSTART_cell, //2
StartByte_cell, //3
ProtocolSTATUS_cell, //4
Heater_cell, //5
delta_cell, //6
sizeNumber_cell, //7
TelNumber1_cell =20, //20
TelNumber2_cell =40, //40
DallasAddr_cell =60 //60
};
//------------------- распиновка ног ардуино (плата весий 8.5-8.8)--------------------------------------------------------
#define OutWebasto_12V 2 // это +12В выход потенциала вкл/выкл вебасто (напрямую к котлу без таймера).
#define Dallas_pin 3 // пин шины OneWire для датчиков даллас
#define DopOn 4 // сюда доп канал от сигналки на включение вебасто
#define DopOff 5 // сюда доп канал от сигналки на выключение вебасто
#define Ohrana 6 // Сюда состояние охраны сигналки
#define Trevoga 7 // Сюда состояние тревоги
#define IGN 8 // Сюда состояние зажигания
#define Sost 9 // Сюда состояние вебасто (+12В когда работает)
#define ResetGSM 12 // пин ресета GSM подключен к реле, разрывающее питание модуля.
#define Eng 14 // (А0) Сюда состояние работы ДВС
#define StatusWebastoLED 15 // (А1) пин LED индикация включенности котла
#define StartButtonpin 16 // (А2) пин тактовой кнопки вкл/выкл котла
#define DTR 17 // пин (А3), управляющий энергосберегающим режимом GSM модуля
#define StartEng 18 // (A4) это импульсный минусовой выход вкл/выкл ДВС. подключать на вход событий сиги.
#define OutWebasto_GndImp 19 // (A5) это импульсный минусовой выход вкл/выкл вебасто (к впайке к кнопке таймера).
#define Voltmeter_pin A7 // пин, которым измеряем напряжение питания
#define StartButton 0 // программный номер тактовой кнопки вкл/выкл котла
#define DopOnButton 1 // программный номер тактовой кнопки (допканала) вкл котла
#define DopOffButton 2 // программный номер тактовой кнопки (допканала) выкл котла
const bool RelayON = 1; // логика управления реле ресета GSM, в данном случае включается высоким уровнем на пине
#define GSM_RX 10 // пин софт RX Arduino для соединения с TX модуля SIM800
#define GSM_TX 11 // пин софт TX Arduino для соединения с RX модуля SIM800
//------------------------------------для GSM модуля----------------------------------------------------------------------
#include <SoftwareSerial.h>
SoftwareSerial SIM800 (GSM_RX, GSM_TX);//Rx, Tx //UART для соединения с GSM модулем
String currStr = "";
String TelNumber[] = {"", "", "", ""};
byte isStringMessage = 0;
byte KTOreport = 1; // флаг кто запросил отчет о запуске котла или ДВС
byte KTOzapros = 0; // флаг кто запросил баланс или запрос параметров
byte ResetNumber = 0; // количество ресетов GSM модуля для статистики (хранится в еепром)
byte SizeTelNumber = 12; // количество символов в номере телефона
int16_t Refresh_time = 1200; //сек
uint32_t prev_refreshMQTT = 0;
uint32_t prevGSMnastr = 0;
uint32_t interval_doprosGSM = 123000UL;
uint16_t delayATcommand = 5000;
byte fails = 0;
byte failresets = 0;
bool settingGSM = 1;
enum gsmstatus_ {WaitGSM, echoOFF, EnergySave, Head, setText, setProgctrl, closeIncoming, newMessage, delSMS, setGPRS, setAccPoint,
setGPRSconnect, setBrokerconnect, setAuthPack, setSubPack, setPubPack
};
byte gsmstatus = WaitGSM;
//------------------- для шины 1-wire и датчиков DS18B20---------------------------------------------------------------------
#include <OneWire.h> // библиотека для DS18B20
OneWire ds(Dallas_pin); // датчики DS18B20 на нужный пин
enum TempC {VyhlopC, EngineC, UlicaC, SalonC, size_arrayTemp}; // перечисление нужных температур (в конце размер массива температур)
// ниже соответствие адресов датчиков различным температурам
byte DS18B20 [size_arrayTemp][10] = {
{0x28, 0xFF, 0xB2, 0xB5, 0xC1, 0x17, 0x05, 0xD1, VyhlopC, -100},
{0x28, 0xFF, 0xD3, 0xE2, 0xC1, 0x17, 0x04, 0x0D, EngineC, -100},
{0x28, 0xFF, 0xF8, 0xBC, 0xC1, 0x17, 0x04, 0x48, UlicaC, -100},
{0x28, 0xFF, 0x3F, 0xB7, 0xC1, 0x17, 0x05, 0xF1, SalonC, -100}
};
byte delta = 50; // разница температур выхлопа и улицы, выше которой считается, что пламя в котле есть.
int8_t HeaterC = -50;
//---------------------------для организации W-BUS и различные таймеры-----------------------------------------------------------
#include <Button.h>
Button test;
#define K_LINE Serial //UART для соединения с шиной котла
#define TX 1
#define NEED 1
#define READY 10
// команды для котлов ЭВО
byte StartByte = 0x20;
const byte HEATER_BEGIN[] {0x51, 0x0A};
byte HEATER_START[] {StartByte, 0x3B};
byte HEATER_PRESENCE[] {0x44, StartByte, 0x00};
const byte HEATER_STOP[] {0x10};
const byte HEATER_STATUS_VEVO[] {0x50, 0x05};
const byte HEATER_STATUS_EVO[] {0x50, 0x30, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x1E, 0x32};
const byte HEATER_DTC_REQUEST[] {0x56, 0x01};
const byte HEATER_DTC_ERASE[] {0x56, 0x03};
// команды для котлов ТТС/TTE
const byte START_SESSION[] {0x81};
const byte REQUEST_2A10101[] {0x2A, 0x01, 0x01};
const byte REQUEST_2A10102[] {0x2A, 0x01, 0x02};
const byte REQUEST_2A10105[] {0x2A, 0x01, 0x05};
const byte REQUEST_DTC[] {0xA1};
const byte START_TTC[] {0x31, 0x22, 0xFF};
const byte STOP_TTC[] {0x31, 0x22, 0x00};
enum needAction_ {NO_ACTION, NEED_SMSZAPROS, NEED_SERVICEINFO, NEED_MQTTZAPROS, NEED_DTCCLEAR};// возможные действия, стоящие в очереди
byte needAction = NO_ACTION; // переменная действия, стоящего в очереди
enum ProtocolSTATUS_ {STATUSBUS, ANALOG}; // возможные протоколы чтения статуса котла
enum ProtocolSTART_ {STARTBUS, IMPULSE, POTENCIAL}; // возможные протоколы запуска котла
enum Heater_ {TTC_E, VEVO, EVO, HYDRONIC}; // тип котла
byte ProtocolSTATUS = STATUSBUS;
byte ProtocolSTART = STARTBUS;
byte Heater = EVO;
bool noData = 0; // флаг пришли ли данные от котла после запроса.
byte w_bus_init = 0; //состояние инициализация шины w-bus (25мс LOW, 25мс HIGH для ЭВО
// либо 300ms LOW, 50ms HIGH, 25ms LOW, 3025ms HIGH для TTC
byte requiredmessage = 1; //флаг, что отправляем в данный момент поддержание старта, запрос параметров или запрос ошибок
byte StartMessageRepeat = 0; //количество отправленных сообщений на старт котла
byte StopMessageRepeat = 4; //количество отправленных сообщений на остановку котла
byte TimeWebasto = 30; //время работы котла, = 30мин
uint32_t currmillis = 0; // снимок системного времени
uint32_t Prev_PeriodW_BusMessage = 0; //переменная для таймера периодической отправки сообщений состояния котла в шину W-Bus
uint32_t Prev_PeriodW_BusStartStop = 0; //переменная для таймера периодической отправки сообщений старта/стопа котла в шину W-Bus
uint32_t prevdelSMS = 0; //переменная для таймера периодического удаления СМС
uint32_t prevVpit = 0; //переменная для таймера периодического измерения напряжения АКБ
uint32_t prevInitreset = 0; //переменная для таймера сброса инита шины
bool Initreset = 0; //переменная для таймера сброса инита шины
uint32_t timerInit = 0; bool timerInitflag = 0; //для таймера инита шины W-BUS
uint32_t prevNeedTimer = 0; bool NeedTimer = 0; //для таймера задержки функций SMSzapros() и ServiceINFO() на время обновления параметров по шине
uint32_t prevReportEngine = 0; bool reportEngine = false; //таймер задержки на отправку отчёта о запуске двигателя
uint32_t prevReport = 0;bool report = false; //таймер задержки на отправку отчёта о запуске котла
uint32_t last_Flame = 0; //для таймера сброса флага пламени, если нет ответов от котла
uint32_t prevGND_impulse = 0; bool GND_impulse_timer = 0; //для таймера создания импульса GND - для протокола запуска котла импульсом GND
uint32_t prevStartEng=0; bool StartEng_timer=0; //для таймера - старт двигателя: минусовой импульс 1.5 сек на вход событий сигналки для запуска ДВС
uint32_t prevWorkCycleHeater; bool WorkCycleHeater_timer=0; //для таймера отсчёта цикла работы котла
//---------------------------------Основные переменные--------------------------------------------------------------------------------
bool webasto = 0; // флаг команды на работу Webasto. 0 - котел выключен, 1 - котел включен
bool startWebasto_OK = 0; // флаг успешного запуска котла
float Vpit = 0.0; // Измеряемое напряжение на выходе ИБП
bool engine =0; // флаг работает ли ДВС или нет
bool ignition=0; // флаг включено ли зажигание или нет
bool ohrana=0; // флаг включена ли охрана или нет
bool trevoga=0; // флаг включена ли тревога или нет
bool alarmSMS = 0; // флаг отправлена ли смс о тревоге или нет
bool waterpump = 0; // флаг работы циркуляционного насоса
bool plug = 0; // флаг работы штифта накаливания
bool airfan = 0; // флаг работы нагнетателя воздуха
bool fuelpump = 0; // флаг работы топливного насоса
bool blowerfan = 0; // флаг работы вентилятора печки автомобиля
byte DTC[7] ={0}; // коды неисправностей котла
//---------------------------СТАРТОВЫЙ ЦИКЛ--------------------------------------------------------------------------------------------
void setup()
{
delay (4500);
test.NO();
test.pullUp();
test.duration_bounce ( 50);
test.duration_click_Db ( 250);
test.duration_inactivity_Up(5000);
test.duration_inactivity_Dn(1000);
test.duration_press ( 500);
test.button(StartButtonpin, DopOn, DopOff);
pinMode (DopOn, INPUT_PULLUP);
pinMode (DopOff, INPUT_PULLUP);
pinMode (Sost, INPUT_PULLUP);
pinMode (Ohrana, INPUT_PULLUP);
pinMode (Trevoga, INPUT_PULLUP);
pinMode (IGN, INPUT_PULLUP);
pinMode (Eng, INPUT_PULLUP);
pinMode (OutWebasto_12V, OUTPUT); digitalWrite (OutWebasto_12V, LOW);
pinMode (StartEng, OUTPUT); digitalWrite (StartEng, LOW);
pinMode (13, OUTPUT); digitalWrite (13, LOW);
pinMode (StatusWebastoLED, OUTPUT); digitalWrite (StatusWebastoLED, LOW);
pinMode (OutWebasto_GndImp, OUTPUT); digitalWrite (OutWebasto_GndImp, HIGH);
pinMode (DTR, OUTPUT); digitalWrite (DTR, LOW ); // делаем низкий для вывода GSM из "спячки"
pinMode (ResetGSM, OUTPUT); digitalWrite (ResetGSM, !RelayON); // реле ресет на данный момент делаем "неактивно"
SIM800.begin(19200); // сериал соединение для gsm модуля
SizeTelNumber = EEPROM.read(sizeNumber_cell);
if (SizeTelNumber>25)while(1);
TimeWebasto = EEPROM.read(TimeWebasto_cell);
ProtocolSTART = EEPROM.read(ProtocolSTART_cell);
ProtocolSTATUS = EEPROM.read(ProtocolSTATUS_cell);
ResetNumber = EEPROM.read(ResetNumber_cell);
StartByte = EEPROM.read(StartByte_cell);
HEATER_START[0] = StartByte;
HEATER_PRESENCE[1] = StartByte;
Heater = EEPROM.read(Heater_cell);
delta = EEPROM.read(delta_cell);
for (int i=0; i<SizeTelNumber; i++)
{
TelNumber[0]+= '0';
TelNumber[3]+= '0';
TelNumber[1]+= (char)EEPROM.read (i+TelNumber1_cell);
TelNumber[2]+= (char)EEPROM.read (i+TelNumber2_cell);
}
// ниже читаем из еепром адреса датчиков температуры даллас
for (byte i = 0; i<size_arrayTemp; i++)
{
for (byte k=0; k<9; k++) DS18B20 [i][k] = EEPROM.read(DallasAddr_cell+i*10+k);
}
if (Heater == EVO || Heater == VEVO) K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == TTC_E) K_LINE.begin(10400);
//for (byte i=0; i<20; i++) {digitalWrite (13, !digitalRead(13)); delay (80);}
//digitalWrite (13,0);
fails = 3;
}
//------------------------------------------ЛУП-----------------------------------------------------------------------------------
void loop() {
currmillis = millis();
test.read();
digitalWrite (StatusWebastoLED, webasto);
//digitalWrite (13, startWebasto_OK);
//digitalWrite (13, webasto);
if (settingGSM) NastroykaGSM ();
else if (fails >= 3) settingGSM = 1;
//если MQTT активен
if (failresets < 5)
{
static bool last_MQTTwebasto = 0;
static bool last_startwebasto_OK = 0;
if (last_MQTTwebasto != webasto || last_startwebasto_OK != startWebasto_OK && !timerInitflag){ MQTTsendDatastream(); last_MQTTwebasto = webasto; last_startwebasto_OK = startWebasto_OK;}
refreshDataMQTT ();
}
if (!webasto)
{
static uint32_t prevTestModem = 0;
if (!settingGSM && currmillis - prevTestModem > interval_doprosGSM)
{
digitalWrite (DTR, LOW);
delay (150);
SIM800.println (F("AT"));
prevTestModem = currmillis;
fails++;
interval_doprosGSM = 10000;
}
}
if (ProtocolSTATUS==ANALOG) {if (Temper(VyhlopC) - Temper(UlicaC) > delta) startWebasto_OK = 1;
else startWebasto_OK = 0;}
if (ProtocolSTART==IMPULSE) {
webasto = !digitalRead (Sost);
static bool previos_webasto = 0;
if (previos_webasto!=webasto)
{
if (!previos_webasto && webasto) Refresh_time = 60;
if (previos_webasto && !webasto) Refresh_time = 1200;
previos_webasto=webasto;
}
}
if (!ohrana) alarmSMS = false;
if (trevoga && !alarmSMS) AlarmSMS ();
readModem();
DelSMS();
Heater_BUS();
timers_and_buttons ();
izmereniya();
}
//-----------------------------------------------------------конец луп-------------------------------------------------------------
void izmereniya() {
engine = !digitalRead (Eng);
ignition= !digitalRead (IGN);
ohrana= !digitalRead (Ohrana);
trevoga= !digitalRead (Trevoga);
if (currmillis-prevVpit>7000){
//измерение напряжения борт сети
if (ProtocolSTATUS==ANALOG)
{
Vpit = (analogRead(Voltmeter_pin) * 4.13) / 1024;
Vpit = Vpit / (9700.0/(98930.0+9700.0)); // По формуле Vpit = vout / (R2/(R1+R2))
if (Vpit<0.09) Vpit=0.0; // Округление до нуля
}
// ниже измерение датчиков даллас
static bool y=0; // флаг работы: запрос температуры или её чтение
y=!y;
if (y) {ds.reset(); // сброс шины
ds.write(0xCC); // обращение ко всем датчикам
ds.write(0x44); // начать преобразование (без паразитного питания)
}
else {
for (byte i=0; i<size_arrayTemp; i++){
int Temper_ = 20; byte buff[9];
ds.reset();
ds.select(DS18B20[i]);
ds.write(0xBE); // чтение регистров датчиков
for (byte j=0; j<9; j++) buff[j]=ds.read(); // читаем все 9 байт от датчика
ds.reset();
if (OneWire::crc8(buff, 8) == buff[8]){ // если контрольная сумма совпадает
Temper_ = buff[0]|(buff[1]<<8); // читаем температуру из первых двух байт (остальные были нужны только для проверки CRC)
Temper_ = Temper_ / 16;
if (Temper_<150 && Temper_>-55) DS18B20[i][9] = Temper_;
}
else DS18B20[i][9] = -101; // если контрольная сумма не совпала, пусть t будет -101 градус.
}}
prevVpit=currmillis;
}}
int8_t Temper (const byte &addressTemp) {for(byte j=0; j<size_arrayTemp; j++){if(DS18B20[j][8]==addressTemp)return(int8_t)DS18B20[j][9];} return-99;}
void timers_and_buttons (){
// опрос допканалов от сигнализации включения/выключение котла и таймер импульса старт/стоп котла
if (GND_impulse_timer && currmillis - prevGND_impulse > 800) {digitalWrite (OutWebasto_GndImp, HIGH); GND_impulse_timer=false;}
//ниже для таймера создания импульса на старт ДВС
if (StartEng_timer && currmillis - prevStartEng > 1500) {digitalWrite (StartEng, LOW); StartEng_timer=false;}
//ниже для таймера старта котла по шине и аналогу
if (WorkCycleHeater_timer && currmillis - prevWorkCycleHeater > (uint32_t)TimeWebasto * 60000UL) {StopWebasto();}
// таймер отправки отчета об успешности запуска котла (отчёт через 6 мин после старта)
if(webasto && report && currmillis - prevReport > 360000UL)
{ report = false; Queue(NEED_SMSZAPROS); }
// таймер отправки отчета об успешности запуска ДВС (отчёт через 90сек после старта)
if(reportEngine && currmillis - prevReportEngine > 90000ul)
{reportEngine = false; Queue(NEED_SMSZAPROS);}
//если нажали на допканал - делаем соответствующее состояние котла
if (test.event_click_Dn (DopOnButton) && !webasto) {StartWebasto(); KTOreport = 1;}
if (test.event_click_Dn (DopOffButton) && webasto) {StopWebasto();}
//если нажали тактовую кнопку меняем состояние котла на противоположное
if (test.event_press_short (StartButton))
{
if (!webasto) {StartWebasto(); report = false;}
else {StopWebasto();}
}
}
void startSMS(byte stat) //__________________Цикл подготовки модуля к отправке СМС-сообщений по первому номеру
{
if (stat==0) stat = KTOreport;
digitalWrite (DTR, LOW); // выводим из спячки GSM модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.print(F("AT+CMGS=\"")); SIM800.print(TelNumber[stat]); SIM800.println("\"");
delay(200);
}
void EndSMS ()
{
SIM800.println((char)26); // Команда отправки СМС
delay(3500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
}
void NastroykaGSM () {
if (fails >= 3) Reset();
else if (currmillis - prevGSMnastr > delayATcommand)
{
fails ++;
if (gsmstatus == WaitGSM) SIM800.println (F("AT"));
else if (gsmstatus == EnergySave) SIM800.println (F("AT+CSCLK=1"));
else if (gsmstatus == Head) SIM800.println (F("AT+CIPHEAD=1"));
else if (gsmstatus == echoOFF) SIM800.println (F("ATE0"));
else if (gsmstatus == setText) SIM800.println (F("AT+CMGF=1"));
else if (gsmstatus == setProgctrl) SIM800.println (F("AT+IFC=0, 0"));
else if (gsmstatus == closeIncoming) SIM800.println (F("AT+GSMBUSY=1"));
else if (gsmstatus == newMessage) SIM800.println (F("AT+CNMI=1,2,2,1,0"));
else if (gsmstatus == delSMS) SIM800.println (F("AT+CMGDA=\"DEL ALL\""));
else if (gsmstatus == setGPRS) SIM800.println (F("AT+SAPBR=3,1, \"Contype\",\"GPRS\""));
else if (gsmstatus == setAccPoint) SIM800.print (F("AT+SAPBR=3,1, \"APN\",")), SIM800.println (ACCESSPOINT);
else if (gsmstatus == setGPRSconnect) SIM800.println (F("AT+SAPBR=1,1"));
else if (gsmstatus == setBrokerconnect) SIM800.print (F("AT+CIPSTART=\"TCP\",")), SIM800.println (SERVERNAME_PORT);
else if (gsmstatus == setAuthPack) AUTHsend ();
else if (gsmstatus == setSubPack) SUBsend("h/#");
else if (gsmstatus == setPubPack) Queue(NEED_MQTTZAPROS), MQTTsendDatastream();
delayATcommand = 5000;
prevGSMnastr = currmillis;
}
}
void DelSMS ()
{
if (currmillis - prevdelSMS > 7200000ul){ //раз в 2 часа
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGDA=\"DEL ALL\"\r")); // удаляем все смс, ки
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
prevdelSMS = currmillis;}
}
//void SMSzapros_ () {if (webasto)SMSzapros(); else {NeedTimer = 1; prevNeedTimer = currmillis; needAction = NEED_SMSZAPROS; w_bus_init = 1;}}
void Queue (const byte &actioN) {needAction = actioN;
if (ProtocolSTATUS==STATUSBUS){NeedTimer = 1; prevNeedTimer = currmillis; if (!webasto) w_bus_init = 1;}
else if (ProtocolSTATUS==ANALOG) {if (needAction==NEED_SMSZAPROS) SMSzapros(); if (needAction==NEED_SERVICEINFO) ServiceINFO();}
}
void readModem() //_____Цикл чтения входящих СМС-сообщений______________
{
static uint32_t timerMQTTreconnect = 0;
static bool TimerMQTTreconnect = 0;
// если было неудачное подключение к MQTT, ждём час и снова пробуем
if (TimerMQTTreconnect && currmillis - timerMQTTreconnect > 3600000UL)
{
TimerMQTTreconnect = 0;
fails = 3;
settingGSM = 1;
}
if (!SIM800.available()) return;
static bool SaveNumber2 = 0; // флаг когда необходима запись номера#2, он true
char currSymb = SIM800.read();
if ('\r' == currSymb || '&' == currSymb)
{
if (isStringMessage!=0&&isStringMessage!=3) //если текущая строка - SMS-сообщение, отреагируем на него соответствующим образом
{
if (!currStr.compareTo(F("ZAPROS"))) {Queue(NEED_SMSZAPROS);} // Передача параметров по СМС
else if (!currStr.compareTo(F("Service-info"))) {Queue(NEED_SERVICEINFO);} // Передача сервисной информации по СМС
else if (!currStr.compareTo(F("Erase DTC"))) {Queue(NEED_DTCCLEAR);} // Запрос на стриание ошибок
else if (!currStr.compareTo(F("ZAPROSTEL"))) {SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
else if (!currStr.compareTo(F("GSMResets-0"))) {ResetNumber=0; EEPROM.write (ResetNumber_cell, ResetNumber); Queue(NEED_SERVICEINFO);} //сброс счетчика ресетов GSM модуля
else if (!currStr.compareTo(F("Version"))) {startSMS(isStringMessage); SIM800.println (ver); EndSMS ();} //запрос версии ПО
else if (!currStr.compareTo(F("Signal-level"))) {digitalWrite (DTR, LOW); delay (150); SIM800.println(F("AT+CSQ")); digitalWrite (DTR, HIGH);} // запрос уровня сигнала GSM
else if (!currStr.compareTo(F("Webasto-ON"))) { startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (!webasto) {StartWebasto (); KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println (F("vkluchena")); EndSMS();}
else if (!currStr.compareTo(F("Webasto-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (webasto)StopWebasto (); // если получили команду на выключение и вебаста в настоящий момент включена - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("otkluchena"));EndSMS();}
// если получили команду на включение ДВС и он в настоящий момент выключен - включаем
else if (!currStr.compareTo(F("Engine-ON"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (!engine) { digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = true; prevReportEngine = currmillis; KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println(F("start")); EndSMS();}
else if (!currStr.compareTo(F("Engine-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (engine){ digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = false;} // если получили команду на выключение ДВС и он в настоящий момент работает - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("ostanovlen")); EndSMS();}
else if (!currStr.compareTo(F("Impulse"))) {if (!webasto) {ProtocolSTART = IMPULSE; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk GND_impulse")); EndSMS();}}
else if (!currStr.compareTo(F("Startbus"))) {if (!webasto) {ProtocolSTART = STARTBUS; EEPROM.write(ProtocolSTART_cell,ProtocolSTART); webasto = 0;
startSMS(isStringMessage); SIM800.println(F("zapusk BUS")); EndSMS();}}
else if (!currStr.compareTo(F("Potencial"))) {if (!webasto) {ProtocolSTART = POTENCIAL; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk +12V Potencial")); EndSMS();}}
else if (!currStr.compareTo(F("DallasAddr"))) {startSMS(isStringMessage); SMSDallasAddr(); EndSMS();}
else if (currStr.endsWith(F("Status"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= STATUSBUS && st<=ANALOG )ProtocolSTATUS = st; EEPROM.write(ProtocolSTATUS_cell,ProtocolSTATUS);
startSMS(isStringMessage); SIM800.print (F("Status: "));
if (ProtocolSTATUS == 0)SIM800.println(F("BUS"));
else if (ProtocolSTATUS == 1)SIM800.println(F("Analog"));
EndSMS();}}
else if (currStr.endsWith(F("HeaterType"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= TTC_E && st<=HYDRONIC) Heater = st; EEPROM.write(Heater_cell,Heater);
startSMS(isStringMessage); SIM800.print (F("Heater: "));
if (Heater == 0)SIM800.println(F("TTC_E")), K_LINE.end(), K_LINE.begin(10400);
else if (Heater == 1)SIM800.println(F("VEVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 2)SIM800.println(F("EVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 3)SIM800.println(F("HYDRONIC"));
EndSMS();}}
else if (currStr.endsWith(F("ADDR"))) {if (!webasto) { startSMS(isStringMessage); byte savadr = ConvertAddr();
if (savadr>=0 && savadr<=3) {SIM800.print(F("address ")); TempName(savadr); SIM800.println (F(" saved OK!"));
SMSDallasAddr();
}
else SIM800.println(F("address is incorrect!"));
EndSMS(); }}
else if (currStr.endsWith(F("Delta"))) {if (!webasto) {delta = currStr.toInt(); //
EEPROM.write(delta_cell, delta); startSMS(isStringMessage);
SIM800.print(F("DeltaT: ")); SIM800.print(delta); SIM800.print(F("*C")); EndSMS();}}
else if (currStr.endsWith(F("min"))) {if (!webasto) {TimeWebasto = currStr.toInt(); // для задания время цикла работы отправить сообщение вида "25 min", где 25 время работы в мин
if (TimeWebasto>59) TimeWebasto = 59;
if (TimeWebasto<=15) TimeWebasto = 15;
EEPROM.write(TimeWebasto_cell,TimeWebasto);
startSMS(isStringMessage); SIM800.print(F("Webasto time: ")); SIM800.print(TimeWebasto); SIM800.print(F("min")); EndSMS();}}
else if (currStr.endsWith(F("StartByte"))) {byte Z =currStr.toInt(); if (Z>=0x14 && Z<=0x17) StartByte= Z+12; EEPROM.write(StartByte_cell, StartByte);
HEATER_START[0] = StartByte;
HEATER_PRESENCE[1] = StartByte;
startSMS(isStringMessage); SIM800.print(F("StartByte: ")); SIM800.print(StartByte, HEX); SIM800.print(F("h")); EndSMS();}
else if (!currStr.compareTo(F("ResetNumbers"))) {if (isStringMessage == 1) {startSMS(isStringMessage); SIM800.println(F("Phone numbers are erased")); EndSMS();
TelNumber[1] = TelNumber[0]; TelNumber[2] = TelNumber[0]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[1][i]); EEPROM.write (i+TelNumber2_cell, TelNumber[2][i]); }}}
else if (!currStr.compareTo(F("WriteNumber2"))&& isStringMessage == 1) {
SaveNumber2 = 1; startSMS(isStringMessage); SIM800.println(F("Otpravte lyuboye SMS s nomera#2 dlya sohraneniya nomera")); EndSMS();}
else if (!currStr.compareTo(F("Balance"))) SMSbalance();
isStringMessage = 0;
}
else if (isStringMessage==3){ if (!currStr.compareTo(F("WriteNumber1"))) { TelNumber[1] = TelNumber[3]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[3][i]);}
startSMS(1); SIM800.println(F("Tel Number#1 is saving in memory")); SIM800.print("Tel#1: "); SIM800.println (TelNumber[1]); EndSMS();
}
else if (!currStr.compareTo(F("ZAPROS"))) { SMSzapros();}
else if (!currStr.compareTo(F("ZAPROSTEL"))) { SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
isStringMessage = 0;
}
else if (isStringMessage==0) { if (TelNumber[1]!=TelNumber[0] && !SaveNumber2){
if (currStr.startsWith("+CMT: \""+TelNumber[1])) { isStringMessage = 1; KTOzapros = 1; }
else if (currStr.startsWith("+CMT: \""+TelNumber[2])) { isStringMessage = 2; KTOzapros = 2; }
else if (currStr.startsWith("+CUSD: 0,")) //если текущая строка начинается с "+CUSD",то следующая строка является запросом баланса
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
else if (currStr.startsWith(F("+CSQ:"))) //если текущая строка начинается с "+CSQ",то значит был запрос на уровень сигнала GSM, отправим ответ запрашивающему
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
}
else if (currStr.startsWith(F("+CMT:")) && !SaveNumber2) { isStringMessage = 3; for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7];}}
else if (currStr.startsWith(F("+CMT:")) && SaveNumber2) { for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7]; EEPROM.write (i+TelNumber2_cell, TelNumber[3][i]);} TelNumber[2] = TelNumber[3];
startSMS(2); SIM800.println(F("Vash nomer sochranyon kak Number#2 v pamyati!"));
SIM800.print(F("Tel#1: ")); SIM800.println(TelNumber[1]); SIM800.print(F("Tel#2: ")); SIM800.println (TelNumber[2]); EndSMS(); SaveNumber2 = 0; }
// ----------------------------данные для MQTT
if (currStr.indexOf(F("CLOSED")) > -1 || currStr.indexOf(F("ERROR")) > -1) {
fails = 3;
settingGSM = 1;
}
if (currStr.indexOf(F("OK")) > -1) {
if (!settingGSM){ if (failresets > 4 || (failresets <= 4 && gsmstatus >= setPubPack)) {fails = 0; digitalWrite (DTR, HIGH); interval_doprosGSM = 123000UL;}}
if (settingGSM && gsmstatus > WaitGSM && gsmstatus < setBrokerconnect) {
// если подключиться к MQTT серверу не удалось, оставляем настройки только для СМС
// и запускаем таймер на час, чтобы через час ещё раз пробовать:
if (gsmstatus == delSMS && failresets > 4)
{settingGSM = 0;
digitalWrite (DTR, HIGH);
timerMQTTreconnect = currmillis;
TimerMQTTreconnect = 1;
}
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 400;
}
}
if (currStr.indexOf(F("CONNECT")) > -1) if (gsmstatus == setBrokerconnect) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SMS Ready")) > -1) if (gsmstatus == WaitGSM) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SEND OK")) > -1) {
fails = 0;
if (settingGSM && gsmstatus >= setAuthPack && gsmstatus <= setPubPack) {
if (gsmstatus == setPubPack) {
settingGSM = 0;
failresets = 0;
} delayATcommand = 300;
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
}
}
//-------------------------топики MQTT, принимаемые от брокера
if (currStr.indexOf(F("h/ctrl")) > -1)
{
bool comanda = currStr.substring(currStr.indexOf(F("h/ctrl")) + 6).toInt();
if (comanda) {StartWebasto(); report = false;}
else {StopWebasto();}
}
if (currStr.indexOf(F("h/time/ctrl")) > -1) {
String TimeW = currStr.substring(currStr.indexOf(F("h/time/ctrl")) + 11);
TimeWebasto = TimeW.toInt();
EEPROM.write(TimeWebasto_cell,TimeWebasto);
PUBsend("h/time/stat", TimeW, 0);
remTime(0) ;
}
if (currStr.indexOf(F("h/refr0")) > -1) {if (!webasto) {Queue(NEED_MQTTZAPROS); delay (100);} MQTTsendDatastream();}
if (currStr.indexOf(F("h/resresets")) > -1) {ResetNumber=0; EEPROM.write (ResetNumber_cell, ResetNumber); PUBsend("h/resets",String(ResetNumber),0);}
// ------------------------конец данных для MQTT
}
currStr = "";
}
else if ('\n' != currSymb && '$' != currSymb) currStr += String(currSymb);
if (currStr.length()>60) currStr += "\r\n";
}
byte ConvertAddr() {
byte addrbuf[9];
byte Cs=0;
for (byte i = 0; i<9; i++) {
char str[]=" ";
for (byte k = 0; k<2; k++) str[k] = currStr[3*i+k];
addrbuf[i] = strtol(str,NULL,HEX);
if (i<7) Cs+= addrbuf[i] = strtol(str,NULL,HEX);
}
if (OneWire::crc8(addrbuf, 7) == addrbuf[7] && addrbuf[8]>=0 && addrbuf[8]<=3) {
for (byte i = 0; i<size_arrayTemp; i++) {
if (addrbuf[8]==DS18B20[i][8]) {for (byte k=0; k<9; k++) DS18B20 [i][k] = addrbuf[k], EEPROM.write (DallasAddr_cell+addrbuf[8]*10+k, addrbuf[k]);}
}
return addrbuf[8];
}
else return 20;
}
void SMSDallasAddr(){
SIM800.println (F("Dallas Addresses:"));
for (byte i = 0; i<size_arrayTemp; i++) {
TempName(DS18B20[i][8]); SIM800.println();
for (byte k = 0; k<8; k++) {if (DS18B20[i][k]<=0x0F)SIM800.print(F("0")); SIM800.print(DS18B20[i][k],HEX); SIM800.print(F(" "));}
SIM800.println();
}
}
void SMSzaprosTEL(){
startSMS(isStringMessage);
PrintNumbers ();
EndSMS();
}
void SMSbalance() {
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.println (F("AT+CUSD=1,\"#100#\"")); // команда на замену на транслит *111*6*2# у МТС
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
}
void AlarmSMS() {for (byte i = 0; i<2; i++) {startSMS(i+1); SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!")); EndSMS();} alarmSMS = true;}
void PrintNumbers () {for (byte i=0; i<2; i++) {SIM800.print(F("Tel#")); SIM800.print (i+1); SIM800.print(F(" ")); SIM800.println(TelNumber[i+1]);}}
void TempName (const byte &_address_) { if (_address_== VyhlopC) SIM800.print(F("Vyhlop: "));
else if (_address_== EngineC) SIM800.print(F("Engine: "));
else if (_address_== UlicaC) SIM800.print(F("Ulica: "));
else if (_address_== SalonC) SIM800.print(F("Salon: "));
}
void SMSzapros()
{
startSMS(isStringMessage);
if (isStringMessage == 3){
SIM800.println (F("Tel.number#1 not has been save in memory"));
SIM800.println (F("For save Tel#1 send SMS command \"WriteNumber1\""));
PrintNumbers ();
}
else {
SIM800.print (F("Webasto ")); on_off (webasto);
if (webasto) {
SIM800.print (F("StartWebasto "));
if (startWebasto_OK) SIM800.println (F("OK"));
else SIM800.println (F("FAIL"));}
SIM800.print (F("Engine ")); on_off (engine);
SIM800.print (F("IGN ")); on_off (ignition);
SIM800.print (F("Ohrana ")); on_off (ohrana);
if (trevoga) SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!"));
SIM800.print(F("Battery: ")); if ((needAction>0 && !noData) || needAction == 0 || ProtocolSTATUS == ANALOG) {SIM800.print (Vpit,1); SIM800.println(F(" V"));}
else if (needAction>0 && noData && ProtocolSTATUS != ANALOG) SIM800.println(F(" No Data"));
SIM800.println(F("Temperatures:"));
if (ProtocolSTATUS != ANALOG){
SIM800.print(F("Heater: ")); if ((needAction>0 && !noData) || needAction == 0) {SIM800.print (HeaterC); grad ();}
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
// ниже распечатаем все температуры
for (byte i = 0; i<size_arrayTemp; i++) {
if (DS18B20[i][8]==VyhlopC && ProtocolSTATUS != ANALOG) {}
else {TempName(DS18B20[i][8]); SIM800.print (Temper(DS18B20[i][8])); grad ();}}
if (ProtocolSTATUS == STATUSBUS){
SIM800.print(F("Errors: ")); if ((needAction>0 && !noData) || needAction == 0) SIM800.println (DTC[0]);
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
}
EndSMS();
}
void on_off (const bool &stat) {if (stat) SIM800.println (F("ON")); else SIM800.println (F("OFF")); }
void grad () {SIM800.println (F("*C")); }
void ServiceINFO(){
startSMS(isStringMessage);
SIM800.print(F("Heater: "));
if (Heater==TTC_E) SIM800.println(F("TTC/E"));
else if (Heater==VEVO) SIM800.println(F("VEVO"));
else if (Heater==EVO) SIM800.println(F("EVO"));
else if (Heater==HYDRONIC) SIM800.println(F("HYDRONIC"));
SIM800.print(F("Start: "));
if (ProtocolSTART==IMPULSE) SIM800.println(F("GND Imp"));
else if (ProtocolSTART==STARTBUS) {SIM800.print(F("BUS"));
if (Heater== VEVO || Heater== EVO) {SIM800.print(F(" 0x")); SIM800.println (StartByte, HEX);}
else SIM800.println();}
else if (ProtocolSTART==POTENCIAL) SIM800.println(F("Potencial+12V"));
SIM800.print(F("Status: "));
if (ProtocolSTATUS==ANALOG) SIM800.println(F("ANALOG"));
else if (ProtocolSTATUS==STATUSBUS) SIM800.println(F("BUS"));
if (ProtocolSTART!=IMPULSE) {SIM800.print(F("Webasto Time: ")); SIM800.print (TimeWebasto); SIM800.println(F("min"));}
SIM800.print(F("Modem Resets: ")); SIM800.println (ResetNumber);
if (ProtocolSTATUS==ANALOG) {SIM800.print(F("DeltaT: ")); SIM800.print(delta);SIM800.println(F("*C"));}
if (ProtocolSTATUS==STATUSBUS){
if (!noData) {
SIM800.print(F(" BurnFAN ")); on_off (airfan);
SIM800.print(F(" WaterPUMP ")); on_off (waterpump);
SIM800.print(F(" PLUG ")); on_off (plug);
SIM800.print(F(" FuelPUMP ")); on_off (fuelpump);
SIM800.print(F(" Blower ")); on_off (blowerfan);
SIM800.print (F("Errors: ")); SIM800.println (DTC[0]);
if (DTC[0] >0) for (byte i=0; i<DTC[0]; i++) {
if (DTC[i+1]<=0x0F) SIM800.print(F("0"));
SIM800.print (DTC[i+1], HEX);
if (bitRead(DTC[6], i+1)) SIM800.println (F(" Active"));
else SIM800.println (F(" Passive")); }
}
else SIM800.println(F("Heater not answer. No Data"));}
EndSMS();
}
void StartWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StartMessageRepeat = 0;
webasto = 1; digitalWrite (OutWebasto_12V, HIGH);
prevWorkCycleHeater=currmillis;
WorkCycleHeater_timer = true;
}
report = true; prevReport = currmillis;
w_bus_init = NEED;
Refresh_time = 60;
}
void StopWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StopMessageRepeat = 0;
webasto = 0; digitalWrite (OutWebasto_12V, LOW);
WorkCycleHeater_timer = false;
}
report = false;
Refresh_time = 1200;
if (ProtocolSTATUS==STATUSBUS) startWebasto_OK = 0;
}
void Heater_BUS (){
static byte header = 0; // состояние заголовка
static byte message_size = 0; // размер тела принимаемого сообщения, кол-во байт
static byte j = 2; // инкремент
static byte n = 2;
const byte bufsize = 140; // размер буфера принятого сообщения
static byte buf [bufsize] = {0}; // буфер принятого сообщения
static byte checksum = 0; // контрольная сумма входящего сообщения
static uint32_t prevRESETheader=0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
static bool RESETheader_timer = 0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
if (Heater == EVO || Heater == VEVO){
if (webasto) {
if (StartMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800) && w_bus_init >= 9){
sendMessage (HEATER_START, sizeof(HEATER_START));
StartMessageRepeat++;
Prev_PeriodW_BusStartStop = currmillis;
}
if (StartMessageRepeat>=4){ if (currmillis-Prev_PeriodW_BusMessage>4000) {
if (requiredmessage==1) sendMessage (HEATER_PRESENCE, sizeof(HEATER_PRESENCE));
else if (requiredmessage==2) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));}
else if (requiredmessage==3) sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));
requiredmessage++; if (requiredmessage > 3) requiredmessage = 1;
StopMessageRepeat = 0;
Prev_PeriodW_BusMessage = currmillis;
}}}
else if (StopMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800)){
sendMessage (HEATER_STOP, sizeof(HEATER_STOP));
StopMessageRepeat++;
StartMessageRepeat = 0;
Prev_PeriodW_BusStartStop = currmillis;
}
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = millis(); timerInitflag = 1;}
else if ( timerInitflag && (millis() - timerInit>24) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
else if ( timerInitflag && (millis() - timerInit>24) && w_bus_init==2) {K_LINE.begin (2400,SERIAL_8E1 );timerInitflag = 0; w_bus_init=9;
if (needAction>0) sendMessage (HEATER_BEGIN, sizeof(HEATER_BEGIN)); }
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (buf[0]==0x4F || buf[0]==0x44){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
else {header = 0; RESETheader_timer=0;}
}
// длина сообщения
else if (header == 1 ){buf[1]=K_LINE.read(); message_size = buf[1]; if (message_size > bufsize) message_size = bufsize; header = 4;j=2;n=2;checksum = 0;}
// пишем тело сообщения
else if (header == 4 && j< message_size+n) {
buf[j] = K_LINE.read();
if (j<message_size+n-1) checksum^= buf[j]; // подсчёт КС
if (j==message_size+n-1) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum^=buf[i]; // прибавляем к контрольной сумме старт байты
// если контрольная сумма верна:
if ( checksum == buf[message_size+n-1]) {
if (Heater == EVO){
if (buf[2]==0xD0 && buf[3]==0x30) { // если получили сообщение с текущими данными
if (buf[4] ==0x0A) {
airfan = (bool)buf[5]; // получаем флаг работы нагнетателя воздуха
plug = (buf[5] & 0x02)>>1; // получаем флаг работы штифта накаливания
fuelpump = (buf[5] & 0x04)>>2; // получаем флаг работы топливного насоса
waterpump = (buf[5] & 0x08)>>3; // получаем флаг работы циркуляционного насоса
blowerfan = (buf[6] & 0x10)>>4; // получаем флаг работы вентилятора печки автомобиля
}
if (buf[6] ==0x0C) HeaterC = buf[7]-50; // получаем температуру антифриза в котле
if (buf[8]==0x0E) {Vpit = (float)(buf[9]<<8|buf[10])/1000.0;} // получаем напряжение борт сети
if (buf[11]==0x10) {startWebasto_OK = (bool)buf[12]; last_Flame = currmillis;} // проверяем наличие пламени
if (needAction>0 && needAction<NEED_DTCCLEAR) {w_bus_init = 12; sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));}
}
if (buf[0]==0x44 && buf[2]==0xC4 && buf[3]==0x00) {w_bus_init = 1; delay (500); K_LINE.flush();} // если получили от котла сбой инита, делаем переинит
if (buf[2]==0xD6 && buf[3]==0x01) {if (needAction>0 && needAction<NEED_DTCCLEAR) w_bus_init = 13;
for (byte h = 0; h< sizeof(DTC); h++) DTC[h]=0;
DTC[0] = buf[4];
for (byte h = 0; h< buf[4]; h++) {DTC[h+1]=buf[h*3+5]; bitWrite (DTC[6], h+1, (buf[h*3+6] & 0x02)>>1);}
}
if (buf[2]==0xD6 && buf[3]==0x03) {startSMS (KTOzapros); SIM800.println (F("DTCs are erased!")); EndSMS(); needAction=0; w_bus_init = 9; NeedTimer = 0; }
if (buf[2]==0xD1 && buf[3]==0x0A) {w_bus_init = 10; }// если получили ответ на сообщение старта коммуникации
}
if (Heater == VEVO){
if (buf[2]==0xD0 && buf[3]==0x05) {startWebasto_OK = buf[7]; last_Flame = currmillis;} // проверяем наличие пламени у VEVO
}
}
// если контрольная сумма не совпала:
//else K_LINE.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=2; checksum = 0;
}
}// end EVO VEVO
else if (Heater == TTC_E){
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = currmillis; timerInitflag = 1;}
if ( timerInitflag && (currmillis - timerInit>299) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
if ( timerInitflag && (currmillis - timerInit>49) && w_bus_init==2) {timerInit = currmillis; digitalWrite(TX, 0); w_bus_init=3; }
if ( timerInitflag && (currmillis - timerInit>24) && w_bus_init==3) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=4; }
if ( timerInitflag && (currmillis - timerInit>3024) && w_bus_init==4) {K_LINE.begin (10400); timerInitflag = 0; w_bus_init=9; sendMessage (START_SESSION, sizeof(START_SESSION));}
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (!bitRead (buf[0],6) && bitRead (buf[0],7)){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
}
// второй старт байт
else if (header == 1){buf[1]=K_LINE.read(); if (buf[1]==0xF1){ header = 2;} else {header = 0; RESETheader_timer=0;}}
// третий старт байт
else if (header == 2){
buf[2]=K_LINE.read();
if (buf[2]==0x51){ message_size = buf[0];
if (buf[0] !=0x80) {header = 4; message_size&=~0x80; j=3; n=3;}
else {header = 3; j=4;n=4;}
if (message_size > bufsize) message_size = bufsize; checksum = 0;} else {header = 0; RESETheader_timer=0; }
}
// если размер сообщения указан в дополнительном байте (нулевой байт 0x80) читаем этот дополнительный байт:
else if (header == 3){
buf[3]=K_LINE.read();
message_size = buf[3];
if (message_size > bufsize) message_size = bufsize;
checksum = 0; header = 4;
}
// пишем тело сообщения
else if (header == 4 && j< message_size+n+1) {
buf[j] = K_LINE.read();
if (j<message_size+n) checksum+= buf[j]; // подсчёт КС
if (j==message_size+n) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum+=buf[i]; // прибавляем к контрольной сумме старт байты
//for (byte i=0; i<message_size+n+1; i++) {Serial.print (buf[i], HEX); Serial.print(" ");}
// если контрольная сумма верна:
if (buf[message_size+n] == checksum) {
// if (buf[n]== 0xC1) Serial.println ("StartSession OK!!!");
}
// если контрольная сумма не совпала:
//else Serial.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=3; checksum = 0;
}
} // end TTC_E
// таймер сброса заголовка если данные оборвались во время приёма заголовка
if (RESETheader_timer && currmillis - prevRESETheader > 500) {RESETheader_timer = 0; header = 0;}
if (currmillis - last_Flame>30000 && ProtocolSTATUS==STATUSBUS) {startWebasto_OK=0; last_Flame = currmillis;} // делаем статус "нет пламени" через 30 сек, если не получаем сообщения от котла
if (Initreset && currmillis - prevInitreset>18000) {Initreset = 0; w_bus_init = 0;} // сброс инита, если прошло более 18 сек после отправки последнего сообщения
if (needAction>0 && needAction < NEED_DTCCLEAR && w_bus_init==10) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));
w_bus_init=11;}
if ((NeedTimer && currmillis - prevNeedTimer>11000) || w_bus_init == 13) {NeedTimer = 0;
if (currmillis - prevNeedTimer>11000) noData = 1;
if (needAction == NEED_SMSZAPROS) SMSzapros();
else if (needAction == NEED_SERVICEINFO) ServiceINFO();
else if (needAction == NEED_MQTTZAPROS) {cipsend_begin ();
PUBsend ("h/temp/heat", noData ? "fail" : String(HeaterC),1);
PUBsend ("h/napr", noData ? "fail" : String(Vpit), 1); SIM800.write (0x1A); digitalWrite (DTR, HIGH);}
else if (needAction == NEED_DTCCLEAR) {startSMS(KTOzapros); SIM800.println (F("DTC not cleared. Heater no answer!")); EndSMS();}
w_bus_init = 9; noData = 0; needAction = 0;
}
if (needAction==NEED_DTCCLEAR && w_bus_init==10) { sendMessage (HEATER_DTC_ERASE, sizeof(HEATER_DTC_ERASE)); w_bus_init = 20; }
}
void Reset() {
static bool resettimer = 0; // для таймера удерживания реле в режиме сброс питания
static uint32_t resetTimer = 0; // для таймера удерживания реле в режиме сброс питания
if (!resettimer) {
digitalWrite (ResetGSM, RelayON);
resettimer = 1;
resetTimer = currmillis;
}
else if (currmillis - resetTimer > 5000 ) {
resettimer = 0;
digitalWrite (ResetGSM, !RelayON);
digitalWrite (DTR, LOW);
delay (150);
prevGSMnastr = currmillis;
delayATcommand = 150;
fails = 0;
failresets ++; if (failresets > 4) failresets = 5;
gsmstatus = WaitGSM;
settingGSM = 1;
interval_doprosGSM = 123000UL;
ResetNumber++; EEPROM.write (ResetNumber_cell, ResetNumber);
}
}
void sendMessage(const byte *command, const size_t size){
Initreset = 1; prevInitreset = currmillis; // включение таймера сброса инита
if (Heater == TTC_E){
const byte siZe = size+4;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) {Mes[i]=size; bitWrite(Mes[i], 7 , 1);}
if (i==1) Mes[i] = 0x51;
if (i==2) Mes[i] = 0xF1;
if (i==3) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum+=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum+=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
else if (Heater == EVO || Heater == VEVO){
const byte siZe = size+3;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) Mes[i] = 0xF4;
if (i==1) Mes[i]=size+1;
if (i==2) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum^=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum^=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
}
void AUTHsend ()
{
cipsend_begin ();
uint16_t timeBroker = TIMEBROKER;
SIM800.write (0x10);
SIM800.write (strlen(PROTOCOLIUS) + strlen(MQTTNAME) + strlen(MQTTUSER) + strlen(MQTTPASSWORD) + 12);
SIM800.write ((byte)0);
SIM800.write (strlen(PROTOCOLIUS));
SIM800.write (PROTOCOLIUS);
SIM800.write (0x03);
SIM800.write (0xC2);
timeBroker *=60;
byte * timeBr = (byte*)&timeBroker;
SIM800.write(*(timeBr+=1)); // время сеанса связи с брокером, сек (ст. байт)
SIM800.write(*(--timeBr)); // время сеанса связи с брокером, сек (мл. байт)
//SIM800.write (0x04); // время сеанса связи с брокером, сек (ст. байт)
//SIM800.write (0xB0); // время сеанса связи с брокером, сек (мл. байт)
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTNAME));
SIM800.write (MQTTNAME);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTUSER));
SIM800.write (MQTTUSER);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTPASSWORD));
SIM800.write (MQTTPASSWORD);
SIM800.write (0x1A);
}
void PUBsend (char topPub[30], const String Command, const bool &multidata) {
if (!multidata) {cipsend_begin ();}
SIM800.write (0x30);
SIM800.write (strlen (topPub) + Command.length() + 2);
SIM800.write ((byte)0x00);
SIM800.write (strlen (topPub));
SIM800.write (topPub);
for (byte i = 0; i < Command.length(); i++) SIM800.write (Command[i]);
if (!multidata) {SIM800.write(0x1A); digitalWrite (DTR, HIGH);}
}
void SUBsend (char topSub[20]) {
cipsend_begin ();
SIM800.write (0x82);
SIM800.write (strlen(topSub) + 5);
SIM800.write ((byte)0);
SIM800.write (0x01);
SIM800.write ((byte)0);
SIM800.write (strlen(topSub));
SIM800.write (topSub);
SIM800.write ((byte)0x00);
SIM800.write (0x1A);
}
void MQTTsendDatastream()
{
cipsend_begin ();
PUBsend ("h/time/stat", String(TimeWebasto), 1);
remTime(true);
PUBsend ("h/stat", String(webasto) + "&$", 1);
PUBsend ("h/flame", String(startWebasto_OK), 1);
PUBsend ("h/temp/dvs", String(Temper(EngineC)), 1);
PUBsend ("h/temp/heat", NeedTimer ? "wait" : String(HeaterC),1);
PUBsend ("h/napr", NeedTimer ? "wait" : String(Vpit), 1);
PUBsend ("h/temp/ul", String(Temper(UlicaC)), 1);
PUBsend ("h/temp/sal", String(Temper(SalonC)), 1);
PUBsend ("h/resets", String(ResetNumber), 1);
SIM800.write (0x1A);
prev_refreshMQTT = currmillis;
digitalWrite (DTR, HIGH);
}
void remTime ( bool s) {
int rem = TimeWebasto - ((currmillis - prevWorkCycleHeater) / 60000ul);
PUBsend ("h/remtime", webasto ? String(rem) : "выкл", s);
}
void cipsend_begin ()
{
digitalWrite (DTR, LOW); delay (150);
SIM800.println (F("AT+CIPSEND"));
delay (250);
}
void refreshDataMQTT ()
{
if (currmillis - prev_refreshMQTT > (uint32_t)Refresh_time * 1000)
{
MQTTsendDatastream();
prev_refreshMQTT = currmillis;
fails++;
}
}
залил скетч 3.81, подправил под 13 цифр номер и чудо- смски работают . Тепер заметил такой глюк- стартбайт какой бы не отправлял(21,23) смс приходит как будто изменён, а на котёл идёт постоянно 20((( посылает нужные мне 21 , только когда в этом месте основного скетча поставлю 21(команды для котлов ЭВО byte StartByte = 0x20;)
Это у тебя 2 ночи , а у меня пол восьмого утра , я с утра все обновления с утра заливаю и перед работой проверяю и отписываюсь , ты проснулся а у тебя уже полный отчет . Так что не работает последний скетч , даже модем к жпрс не подключается , мигает очень медленно , и на смс не отвечает. Где то косяк.
Нужно теперь еще более новым скетчем чистить епром , а я тем же скетчем чистил епром,что и впрошлый раз а адреса я копипастом с прежнего скетча скопировал, я сначала подумал что новая оболочка не так компилирует ,залил скетч 3.81 , девайс заработал , снова залил 3.83 не работает , модем мигает редко , но на смс не отвечает.
Дополняю , короче доехал я до гаража, прошло 8 минут, проверяю дашборд , данные не обновляются, жму все подряд кнопки ноль эмоций, отправил смс , есть ответ, зашёл через браузер в веб сокет запросы идут ответа от девайса нет , перегрузил девай всё опять заработало
ок. на столе все красиво. Я на днях подключу девайс к машине, проверим как себя ведёт. В момент проявления глюка - отсутствия ответа девайса по MQTT светодиод быстро мигает (как при GPRS соединении)?
Когда связь теряется то модем так же мигает часто , как и когда связь есть. Пришел я домой решил еще раз проверить , отправил смс на девайс , девайс ответил что есть одна ошибка 3А , эта ошибка появляется каждый раз после потери связи, я ее сбросил ,но сразу ни чего не изменилось , как нет связи так и нет, попробовал опять через вебсокет , команды от телефона идут , от модема ответа нет, потом через какое то время появился отклик от девайса , и данные пошли, но счетчик ресетов увеличился на 1 , я стал баловаться и жать обновить через разные промежутки времени через 30 сек. потом минуту , потом через 2 , 3 и так далее , последний раз через 7 минут связь была , а через 9 уже все отвалилась, отправил смс на девайс , опять ошибка 3А. Я ее стер но связь не появилась . Я думаю что надо отправлять запрос не реже раза в пару минут , а то может из-за отсутствия обмена данными канал разрывается.
У меня устойчивая связь 5-6 минут, потом устройство перестаёт отвечать на запросы, светодиод на сим800 моргает часто, как при нормальной работе при передачи данных по GPRS. За 15 часов работы устройство перегружало модем 46 раз и соответственно после перезагрузки 5-6 минут слал посылки. Если его постоянно "дёргать" раз в 30-40 сек. (я изменял время цикла работы котла) меня хватило на 10 минут, устройство исправно отвечало
Отправлять на девайс какую нибудь команду пустышку , что бы котел из спячки не выводить , но и сеанс связи не рвать между брокером и девайсом , может это у Мегафона опять какие нибудь заморочки , по времени .
Ага точно у меня сейчас после 6 минут тоже связь пропала . Подожду и проверю появилась ошибка 3А или нет . Эта ошибка значит короткое замыкание на шине W-bus . Надо раз в 4 минуты отправлять пустую команду на девайс и все будет нормально. Я так думаю.
Может поможет в вашем устройстве,
1)у меня раз в минуту устройство отправляет данные на mqtt
2)после отправки сообщения на mqtt он должен вернуть сообщение send ok(вроде такое мож ошибаюсь) если не вернул считаем что связь потеряна
3)если связь теряется проверяется регистрация в сети и если она устойчива в течении 5 минут то подключаю тырнет по новой.
Примерно такой алгоритм, связь всё равно теряется неизвестно почему иногда через 20 суток иногда через 10. По расходам (оператор теле2) примерно 15р в месяц.
у меня примерно такой же алгоритм. По крайней мере так задумывал. Видимо где то косяк допустил и не отрабатывает так как задумывалось. Пока в процессе поиска косяка.
опишите точные условия потери связи. Примерно так: Подаем питание на девайс. Дожидаемся, пока в дашборде обновятся пиктограммы. Далее через 6...8 минут связь теряется. Так происходит?
Блин жду, 7,8 и 10 минут. Всё норм. как так? Может надо котёл включить и после этого начинает проявляться?
Да какие тут условия , подключил питание , дашборд обновился, все работает , кнопки нажимаются , данные обновляются, но если ни чего не делать с котлом, не обновлять данные , не менять время работы ни запускать котел , то через 5-6 минут котел перестает отвечать на команды , ни на обновить ни на запуск , через пол часа происходит перезагрузка и все по новой 5 минут нормальной работы и опять тишина на пол часа , я засекал , через 29 минут он перегружается и все работает . Может это оператор так выставил , что если более 5 минут ни какие байты по соединению не передаются то , оно обрывается, может еще чего , но если за 5 минут произвести с дашбордом каки нибудь манипуляции , обновить или изменить время работы котла , то связь есть еще 5 минут . Как то так. Если котел запущен то данные о температуре сами раз в минуту обновляются. И еще у меня после такой перезагрузки вылазит ошибка 3A.
Еще добавлю , что бы все обновилось надо после получаса бездействия в течении 5 минут нажать обновить , если не нажать, то в плитках напряжения и температуры котла будет fail , ну типо запроса не было на котел и данные не обновились.
я к чему так спрашиваю, потому что сейчас гоняю девайс в хвост и в гриву, но работает собака вообще норм. MQTT активен при любых раскладах. Говорю, пока тогда пробуем #985
у меня сейчас опрос состояния по аналогу ( ево и вево не совпадают с протоколом моего котла ) ведёт сейчас точно так... сейчас откину котёл и проверю без него, может связано с тем , что котёл засыпает и вешает шину на корпус?
Девайс в гараже в авто стоит, скетч подправил, на флешку скинул завтра на работе прошью и проверю, у тебя какой оператор связи? Может это операторские штуки. Ни одного у меня такая байда с пятиминутками.
Я с Украины, сейчас стоит карта водафон (бывший мтс, он стоял у меня в GPS трекере и отлично работал по gprs) , попробую сменить на другого оператора после эксперимента с откюченным котлом
думаю ни к чему, #985 должно решить проблему. А у вас диагностика (клайн адаптер + программа WTT)имеется? это я к тому чтобы протокол связи посмотреть вашей маздовской печки.
тоже 9 шт лишних ))) ещё версии 8.6 ... В общем сменил карту на другого оператора и вылезла другая проблема - включаю , моргает на сим светодиод ( поиск сети) 8 импульсов, клацает реле и так по кругу .... вытаскиваю ардуину всё ок, находится сеть , несколько раз пробовал перегружать безтолку , воткнул обратно водафон всё ок (условно)..
скетч v3.84. Откорректированы временные интервалы взаимодействия с модемом и сервисом MQTT. проверяйте. Сейчас каждые 2 минуты при активном MQTT будет посылаться команда на брокер. А когда котёл включен в т.ч. на брокер шлются также параметры раз в 30 сек. Если подключение к брокеру не получилось раз в 2 минуты просто проверяется активность модема командой "AT"
const char ver[] = "Firmware 3.84"; // версия прошивки
//---------------------------Настройки MQTT-----------------------------------------------------------------------------
const char ACCESSPOINT[]= "\"internet.mts.by\""; // точка доступа оператора связи симкарты
const char PROTOCOLIUS[] = "MQIsdp"; // это и оставляем
const char MQTTNAME[] = "MaksVV"; // это смотрим на сервере MQTT
const char MQTTUSER[] = "iu w"; // это смотрим на сервере MQTT
const char MQTTPASSWORD[] = "1Q W"; // это смотрим на сервере MQTT
const char SERVERNAME_PORT[] = "\"m16.cloudmqtt.com\", \"14445\""; // это смотрим на сервере MQTT
#define TIMEBROKER 80 // продолжительность сессии с брокером , мин (пока такое и оставляем)
//----------------------------------название ячеек еепром----------------------------------------------------------------
#include <EEPROM.h>
enum Cells {
ResetNumber_cell, //0
TimeWebasto_cell, //1
ProtocolSTART_cell, //2
StartByte_cell, //3
ProtocolSTATUS_cell, //4
Heater_cell, //5
delta_cell, //6
sizeNumber_cell, //7
TelNumber1_cell =20, //20
TelNumber2_cell =40, //40
DallasAddr_cell =60 //60
};
//------------------- распиновка ног ардуино (плата весий 8.5-8.8)--------------------------------------------------------
#define OutWebasto_12V 2 // это +12В выход потенциала вкл/выкл вебасто (напрямую к котлу без таймера).
#define Dallas_pin 3 // пин шины OneWire для датчиков даллас
#define DopOn 4 // сюда доп канал от сигналки на включение вебасто
#define DopOff 5 // сюда доп канал от сигналки на выключение вебасто
#define Ohrana 6 // Сюда состояние охраны сигналки
#define Trevoga 7 // Сюда состояние тревоги
#define IGN 8 // Сюда состояние зажигания
#define Sost 9 // Сюда состояние вебасто (+12В когда работает)
#define ResetGSM 12 // пин ресета GSM подключен к реле, разрывающее питание модуля.
#define Eng 14 // (А0) Сюда состояние работы ДВС
#define StatusWebastoLED 15 // (А1) пин LED индикация включенности котла
#define StartButtonpin 16 // (А2) пин тактовой кнопки вкл/выкл котла
#define DTR 17 // пин (А3), управляющий энергосберегающим режимом GSM модуля
#define StartEng 18 // (A4) это импульсный минусовой выход вкл/выкл ДВС. подключать на вход событий сиги.
#define OutWebasto_GndImp 19 // (A5) это импульсный минусовой выход вкл/выкл вебасто (к впайке к кнопке таймера).
#define Voltmeter_pin A7 // пин, которым измеряем напряжение питания
#define StartButton 0 // программный номер тактовой кнопки вкл/выкл котла
#define DopOnButton 1 // программный номер тактовой кнопки (допканала) вкл котла
#define DopOffButton 2 // программный номер тактовой кнопки (допканала) выкл котла
const bool RelayON = 1; // логика управления реле ресета GSM, в данном случае включается высоким уровнем на пине
#define GSM_RX 10 // пин софт RX Arduino для соединения с TX модуля SIM800
#define GSM_TX 11 // пин софт TX Arduino для соединения с RX модуля SIM800
//------------------------------------для GSM модуля----------------------------------------------------------------------
#include <SoftwareSerial.h>
SoftwareSerial SIM800 (GSM_RX, GSM_TX);//Rx, Tx //UART для соединения с GSM модулем
String currStr = "";
String TelNumber[] = {"", "", "", ""};
byte isStringMessage = 0;
byte KTOreport = 1; // флаг кто запросил отчет о запуске котла или ДВС
byte KTOzapros = 0; // флаг кто запросил баланс или запрос параметров
byte ResetNumber = 0; // количество ресетов GSM модуля для статистики (хранится в еепром)
byte SizeTelNumber = 12; // количество символов в номере телефона
const uint16_t Refresh_interval_heaterON = 30000; // интервал обновления параметров по MQTT при включенном котле, миллисек
uint32_t prev_refreshMQTT = 0;
uint32_t prevGSMnastr = 0;
const byte interval_doprosGSM = 2; // интервал проверки модема на активность, мин
uint16_t delayATcommand = 5000;
byte fails = 0;
byte failresets = 0;
bool settingGSM = 1;
enum gsmstatus_ {WaitGSM, echoOFF, EnergySave, Head, setText, setProgctrl, closeIncoming, newMessage, delSMS, setGPRS, setAccPoint,
setGPRSconnect, setBrokerconnect, setAuthPack, setSubPack, setPubPack
};
byte gsmstatus = WaitGSM;
//------------------- для шины 1-wire и датчиков DS18B20---------------------------------------------------------------------
#include <OneWire.h> // библиотека для DS18B20
OneWire ds(Dallas_pin); // датчики DS18B20 на нужный пин
enum TempC {VyhlopC, EngineC, UlicaC, SalonC, size_arrayTemp}; // перечисление нужных температур (в конце размер массива температур)
// ниже соответствие адресов датчиков различным температурам
byte DS18B20 [size_arrayTemp][10] = {
{0x28, 0xFF, 0xB2, 0xB5, 0xC1, 0x17, 0x05, 0xD1, VyhlopC, -100},
{0x28, 0xFF, 0xD3, 0xE2, 0xC1, 0x17, 0x04, 0x0D, EngineC, -100},
{0x28, 0xFF, 0xF8, 0xBC, 0xC1, 0x17, 0x04, 0x48, UlicaC, -100},
{0x28, 0xFF, 0x3F, 0xB7, 0xC1, 0x17, 0x05, 0xF1, SalonC, -100}
};
byte delta = 50; // разница температур выхлопа и улицы, выше которой считается, что пламя в котле есть.
int8_t HeaterC = -50;
//---------------------------для организации W-BUS и различные таймеры-----------------------------------------------------------
#include <Button.h>
Button test;
#define K_LINE Serial //UART для соединения с шиной котла
#define TX 1
#define NEED 1
#define READY 10
// команды для котлов ЭВО
byte StartByte = 0x20;
const byte HEATER_BEGIN[] {0x51, 0x0A};
byte HEATER_START[] {StartByte, 0x3B};
byte HEATER_PRESENCE[] {0x44, StartByte, 0x00};
const byte HEATER_STOP[] {0x10};
const byte HEATER_STATUS_VEVO[] {0x50, 0x05};
const byte HEATER_STATUS_EVO[] {0x50, 0x30, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x1E, 0x32};
const byte HEATER_DTC_REQUEST[] {0x56, 0x01};
const byte HEATER_DTC_ERASE[] {0x56, 0x03};
// команды для котлов ТТС/TTE
const byte START_SESSION[] {0x81};
const byte REQUEST_2A10101[] {0x2A, 0x01, 0x01};
const byte REQUEST_2A10102[] {0x2A, 0x01, 0x02};
const byte REQUEST_2A10105[] {0x2A, 0x01, 0x05};
const byte REQUEST_DTC[] {0xA1};
const byte START_TTC[] {0x31, 0x22, 0xFF};
const byte STOP_TTC[] {0x31, 0x22, 0x00};
enum needAction_ {NO_ACTION, NEED_SMSZAPROS, NEED_SERVICEINFO, NEED_MQTTZAPROS, NEED_DTCCLEAR};// возможные действия, стоящие в очереди
byte needAction = NO_ACTION; // переменная действия, стоящего в очереди
enum ProtocolSTATUS_ {STATUSBUS, ANALOG}; // возможные протоколы чтения статуса котла
enum ProtocolSTART_ {STARTBUS, IMPULSE, POTENCIAL}; // возможные протоколы запуска котла
enum Heater_ {TTC_E, VEVO, EVO, HYDRONIC}; // тип котла
byte ProtocolSTATUS = STATUSBUS;
byte ProtocolSTART = STARTBUS;
byte Heater = EVO;
bool noData = 0; // флаг пришли ли данные от котла после запроса.
byte w_bus_init = 0; //состояние инициализация шины w-bus (25мс LOW, 25мс HIGH для ЭВО
// либо 300ms LOW, 50ms HIGH, 25ms LOW, 3025ms HIGH для TTC
byte requiredmessage = 1; //флаг, что отправляем в данный момент поддержание старта, запрос параметров или запрос ошибок
byte StartMessageRepeat = 0; //количество отправленных сообщений на старт котла
byte StopMessageRepeat = 4; //количество отправленных сообщений на остановку котла
byte TimeWebasto = 30; //время работы котла, = 30мин
uint32_t currmillis = 0; // снимок системного времени
uint32_t Prev_PeriodW_BusMessage = 0; //переменная для таймера периодической отправки сообщений состояния котла в шину W-Bus
uint32_t Prev_PeriodW_BusStartStop = 0; //переменная для таймера периодической отправки сообщений старта/стопа котла в шину W-Bus
uint32_t prevdelSMS = 0; //переменная для таймера периодического удаления СМС
uint32_t prevVpit = 0; //переменная для таймера периодического измерения напряжения АКБ
uint32_t prevInitreset = 0; //переменная для таймера сброса инита шины
bool Initreset = 0; //переменная для таймера сброса инита шины
uint32_t timerInit = 0; bool timerInitflag = 0; //для таймера инита шины W-BUS
uint32_t prevNeedTimer = 0; bool NeedTimer = 0; //для таймера задержки функций SMSzapros() и ServiceINFO() на время обновления параметров по шине
uint32_t prevReportEngine = 0; bool reportEngine = false; //таймер задержки на отправку отчёта о запуске двигателя
uint32_t prevReport = 0;bool report = false; //таймер задержки на отправку отчёта о запуске котла
uint32_t last_Flame = 0; //для таймера сброса флага пламени, если нет ответов от котла
uint32_t prevGND_impulse = 0; bool GND_impulse_timer = 0; //для таймера создания импульса GND - для протокола запуска котла импульсом GND
uint32_t prevStartEng=0; bool StartEng_timer=0; //для таймера - старт двигателя: минусовой импульс 1.5 сек на вход событий сигналки для запуска ДВС
uint32_t prevWorkCycleHeater; bool WorkCycleHeater_timer=0; //для таймера отсчёта цикла работы котла
//---------------------------------Основные переменные--------------------------------------------------------------------------------
bool webasto = 0; // флаг команды на работу Webasto. 0 - котел выключен, 1 - котел включен
bool startWebasto_OK = 0; // флаг успешного запуска котла
float Vpit = 0.0; // Измеряемое напряжение на выходе ИБП
bool engine =0; // флаг работает ли ДВС или нет
bool ignition=0; // флаг включено ли зажигание или нет
bool ohrana=0; // флаг включена ли охрана или нет
bool trevoga=0; // флаг включена ли тревога или нет
bool alarmSMS = 0; // флаг отправлена ли смс о тревоге или нет
bool waterpump = 0; // флаг работы циркуляционного насоса
bool plug = 0; // флаг работы штифта накаливания
bool airfan = 0; // флаг работы нагнетателя воздуха
bool fuelpump = 0; // флаг работы топливного насоса
bool blowerfan = 0; // флаг работы вентилятора печки автомобиля
byte DTC[7] ={0}; // коды неисправностей котла
//---------------------------СТАРТОВЫЙ ЦИКЛ--------------------------------------------------------------------------------------------
void setup()
{
delay (4500);
test.NO();
test.pullUp();
test.duration_bounce ( 50);
test.duration_click_Db ( 250);
test.duration_inactivity_Up(5000);
test.duration_inactivity_Dn(1000);
test.duration_press ( 500);
test.button(StartButtonpin, DopOn, DopOff);
pinMode (DopOn, INPUT_PULLUP);
pinMode (DopOff, INPUT_PULLUP);
pinMode (Sost, INPUT_PULLUP);
pinMode (Ohrana, INPUT_PULLUP);
pinMode (Trevoga, INPUT_PULLUP);
pinMode (IGN, INPUT_PULLUP);
pinMode (Eng, INPUT_PULLUP);
pinMode (OutWebasto_12V, OUTPUT); digitalWrite (OutWebasto_12V, LOW);
pinMode (StartEng, OUTPUT); digitalWrite (StartEng, LOW);
pinMode (13, OUTPUT); digitalWrite (13, LOW);
pinMode (StatusWebastoLED, OUTPUT); digitalWrite (StatusWebastoLED, LOW);
pinMode (OutWebasto_GndImp, OUTPUT); digitalWrite (OutWebasto_GndImp, HIGH);
pinMode (DTR, OUTPUT); digitalWrite (DTR, LOW ); // делаем низкий для вывода GSM из "спячки"
pinMode (ResetGSM, OUTPUT); digitalWrite (ResetGSM, !RelayON); // реле ресет на данный момент делаем "неактивно"
SIM800.begin(19200); // сериал соединение для gsm модуля
SizeTelNumber = EEPROM.read(sizeNumber_cell);
if (SizeTelNumber>25)while(1);
TimeWebasto = EEPROM.read(TimeWebasto_cell);
ProtocolSTART = EEPROM.read(ProtocolSTART_cell);
ProtocolSTATUS = EEPROM.read(ProtocolSTATUS_cell);
ResetNumber = EEPROM.read(ResetNumber_cell);
StartByte = EEPROM.read(StartByte_cell);
HEATER_START[0] = StartByte;
HEATER_PRESENCE[1] = StartByte;
Heater = EEPROM.read(Heater_cell);
delta = EEPROM.read(delta_cell);
for (int i=0; i<SizeTelNumber; i++)
{
TelNumber[0]+= '0';
TelNumber[3]+= '0';
TelNumber[1]+= (char)EEPROM.read (i+TelNumber1_cell);
TelNumber[2]+= (char)EEPROM.read (i+TelNumber2_cell);
}
// ниже читаем из еепром адреса датчиков температуры даллас
for (byte i = 0; i<size_arrayTemp; i++)
{
for (byte k=0; k<9; k++) DS18B20 [i][k] = EEPROM.read(DallasAddr_cell+i*10+k);
}
if (Heater == EVO || Heater == VEVO) K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == TTC_E) K_LINE.begin(10400);
//for (byte i=0; i<20; i++) {digitalWrite (13, !digitalRead(13)); delay (80);}
//digitalWrite (13,0);
fails = 3;
}
//------------------------------------------ЛУП-----------------------------------------------------------------------------------
void loop() {
currmillis = millis();
test.read();
digitalWrite (StatusWebastoLED, webasto);
//digitalWrite (13, startWebasto_OK);
//digitalWrite (13, webasto);
if (settingGSM) NastroykaGSM ();
else if (fails >= 3) settingGSM = 1;
if (failresets <= 4) //если MQTT активен
{
static bool last_MQTTwebasto = 0;
static bool last_startwebasto_OK = 0;
if (last_MQTTwebasto != webasto || last_startwebasto_OK != startWebasto_OK && !timerInitflag){MQTTsendDatastream(); last_MQTTwebasto = webasto; last_startwebasto_OK = startWebasto_OK;}
if (webasto && currmillis - prev_refreshMQTT > Refresh_interval_heaterON) {MQTTsendDatastream(); prev_refreshMQTT = currmillis;}
}
static uint32_t prevTestModem = 0;
if (!settingGSM && currmillis - prevTestModem > (uint32_t)interval_doprosGSM*60000UL)
{
if (failresets > 4) //если MQTT не активен
{
digitalWrite (DTR, LOW);
delay (150);
SIM800.println (F("AT"));
delay (50);
digitalWrite (DTR, HIGH);
}
else {PUBsend ("h/stat",String(webasto) + "&$",0);} //если MQTT активен
fails++;
prevTestModem = currmillis;
}
if (ProtocolSTATUS==ANALOG) {if (Temper(VyhlopC) - Temper(UlicaC) > delta) startWebasto_OK = 1;
else startWebasto_OK = 0;}
if (ProtocolSTART==IMPULSE) {webasto = !digitalRead (Sost);}
if (!ohrana) alarmSMS = false;
if (trevoga && !alarmSMS) AlarmSMS ();
readModem();
DelSMS();
Heater_BUS();
timers_and_buttons ();
izmereniya();
}
//-----------------------------------------------------------конец луп-------------------------------------------------------------
void izmereniya() {
engine = !digitalRead (Eng);
ignition= !digitalRead (IGN);
ohrana= !digitalRead (Ohrana);
trevoga= !digitalRead (Trevoga);
if (currmillis-prevVpit>7000){
//измерение напряжения борт сети
if (ProtocolSTATUS==ANALOG)
{
Vpit = (analogRead(Voltmeter_pin) * 4.13) / 1024;
Vpit = Vpit / (9700.0/(98930.0+9700.0)); // По формуле Vpit = vout / (R2/(R1+R2))
if (Vpit<0.09) Vpit=0.0; // Округление до нуля
}
// ниже измерение датчиков даллас
static bool y=0; // флаг работы: запрос температуры или её чтение
y=!y;
if (y) {ds.reset(); // сброс шины
ds.write(0xCC); // обращение ко всем датчикам
ds.write(0x44); // начать преобразование (без паразитного питания)
}
else {
for (byte i=0; i<size_arrayTemp; i++){
int Temper_ = 20; byte buff[9];
ds.reset();
ds.select(DS18B20[i]);
ds.write(0xBE); // чтение регистров датчиков
for (byte j=0; j<9; j++) buff[j]=ds.read(); // читаем все 9 байт от датчика
ds.reset();
if (OneWire::crc8(buff, 8) == buff[8]){ // если контрольная сумма совпадает
Temper_ = buff[0]|(buff[1]<<8); // читаем температуру из первых двух байт (остальные были нужны только для проверки CRC)
Temper_ = Temper_ / 16;
if (Temper_<150 && Temper_>-55) DS18B20[i][9] = Temper_;
}
else DS18B20[i][9] = -101; // если контрольная сумма не совпала, пусть t будет -101 градус.
}}
prevVpit=currmillis;
}}
int8_t Temper (const byte &addressTemp) {for(byte j=0; j<size_arrayTemp; j++){if(DS18B20[j][8]==addressTemp)return(int8_t)DS18B20[j][9];} return-99;}
void timers_and_buttons (){
// опрос допканалов от сигнализации включения/выключение котла и таймер импульса старт/стоп котла
if (GND_impulse_timer && currmillis - prevGND_impulse > 800) {digitalWrite (OutWebasto_GndImp, HIGH); GND_impulse_timer=false;}
//ниже для таймера создания импульса на старт ДВС
if (StartEng_timer && currmillis - prevStartEng > 1500) {digitalWrite (StartEng, LOW); StartEng_timer=false;}
//ниже для таймера старта котла по шине и аналогу
if (WorkCycleHeater_timer && currmillis - prevWorkCycleHeater > (uint32_t)TimeWebasto * 60000UL) {StopWebasto();}
// таймер отправки отчета об успешности запуска котла (отчёт через 6 мин после старта)
if(webasto && report && currmillis - prevReport > 360000UL)
{ report = false; Queue(NEED_SMSZAPROS); }
// таймер отправки отчета об успешности запуска ДВС (отчёт через 90сек после старта)
if(reportEngine && currmillis - prevReportEngine > 90000ul)
{reportEngine = false; Queue(NEED_SMSZAPROS);}
//если нажали на допканал - делаем соответствующее состояние котла
if (test.event_click_Dn (DopOnButton) && !webasto) {StartWebasto(); KTOreport = 1;}
if (test.event_click_Dn (DopOffButton) && webasto) {StopWebasto();}
//если нажали тактовую кнопку меняем состояние котла на противоположное
if (test.event_press_short (StartButton))
{
if (!webasto) {StartWebasto(); report = false;}
else {StopWebasto();}
}
}
void startSMS(byte stat) //__________________Цикл подготовки модуля к отправке СМС-сообщений по первому номеру
{
if (stat==0) stat = KTOreport;
digitalWrite (DTR, LOW); // выводим из спячки GSM модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.print(F("AT+CMGS=\"")); SIM800.print(TelNumber[stat]); SIM800.println("\"");
delay(200);
}
void EndSMS ()
{
SIM800.println((char)26); // Команда отправки СМС
delay(3500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
}
void NastroykaGSM () {
if (fails >= 3) Reset();
else if (currmillis - prevGSMnastr > delayATcommand)
{
fails ++;
if (gsmstatus == WaitGSM) SIM800.println (F("AT"));
else if (gsmstatus == EnergySave) SIM800.println (F("AT+CSCLK=1"));
else if (gsmstatus == Head) SIM800.println (F("AT+CIPHEAD=1"));
else if (gsmstatus == echoOFF) SIM800.println (F("ATE0"));
else if (gsmstatus == setText) SIM800.println (F("AT+CMGF=1"));
else if (gsmstatus == setProgctrl) SIM800.println (F("AT+IFC=0, 0"));
else if (gsmstatus == closeIncoming) SIM800.println (F("AT+GSMBUSY=1"));
else if (gsmstatus == newMessage) SIM800.println (F("AT+CNMI=1,2,2,1,0"));
else if (gsmstatus == delSMS) SIM800.println (F("AT+CMGDA=\"DEL ALL\""));
else if (gsmstatus == setGPRS) SIM800.println (F("AT+SAPBR=3,1, \"Contype\",\"GPRS\""));
else if (gsmstatus == setAccPoint) SIM800.print (F("AT+SAPBR=3,1, \"APN\",")), SIM800.println (ACCESSPOINT);
else if (gsmstatus == setGPRSconnect) SIM800.println (F("AT+SAPBR=1,1"));
else if (gsmstatus == setBrokerconnect) SIM800.print (F("AT+CIPSTART=\"TCP\",")), SIM800.println (SERVERNAME_PORT);
else if (gsmstatus == setAuthPack) AUTHsend ();
else if (gsmstatus == setSubPack) SUBsend("h/#");
else if (gsmstatus == setPubPack) Queue(NEED_MQTTZAPROS), MQTTsendDatastream();
delayATcommand = 5000;
prevGSMnastr = currmillis;
}
}
void DelSMS ()
{
if (currmillis - prevdelSMS > 7200000ul){ //раз в 2 часа
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGDA=\"DEL ALL\"\r")); // удаляем все смс, ки
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
prevdelSMS = currmillis;}
}
//void SMSzapros_ () {if (webasto)SMSzapros(); else {NeedTimer = 1; prevNeedTimer = currmillis; needAction = NEED_SMSZAPROS; w_bus_init = 1;}}
void Queue (const byte &actioN) {needAction = actioN;
if (ProtocolSTATUS==STATUSBUS){NeedTimer = 1; prevNeedTimer = currmillis; if (!webasto) w_bus_init = 1;}
else if (ProtocolSTATUS==ANALOG) {if (needAction==NEED_SMSZAPROS) SMSzapros(); if (needAction==NEED_SERVICEINFO) ServiceINFO();}
}
void readModem() //_____Цикл чтения входящих СМС-сообщений______________
{
static uint32_t timerMQTTreconnect = 0;
static bool TimerMQTTreconnect = 0;
// если было неудачное подключение к MQTT, ждём час и снова пробуем
if (TimerMQTTreconnect && currmillis - timerMQTTreconnect > 3600000UL)
{
TimerMQTTreconnect = 0;
fails = 3;
settingGSM = 1;
}
if (!SIM800.available()) return;
static bool SaveNumber2 = 0; // флаг когда необходима запись номера#2, он true
char currSymb = SIM800.read();
if ('\r' == currSymb || '&' == currSymb)
{
if (isStringMessage!=0&&isStringMessage!=3) //если текущая строка - SMS-сообщение, отреагируем на него соответствующим образом
{
if (!currStr.compareTo(F("ZAPROS"))) {Queue(NEED_SMSZAPROS);} // Передача параметров по СМС
else if (!currStr.compareTo(F("Service-info"))) {Queue(NEED_SERVICEINFO);} // Передача сервисной информации по СМС
else if (!currStr.compareTo(F("Erase DTC"))) {Queue(NEED_DTCCLEAR);} // Запрос на стриание ошибок
else if (!currStr.compareTo(F("ZAPROSTEL"))) {SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
else if (!currStr.compareTo(F("GSMResets-0"))) {ResetNumber=0; EEPROM.write (ResetNumber_cell, ResetNumber); Queue(NEED_SERVICEINFO);} //сброс счетчика ресетов GSM модуля
else if (!currStr.compareTo(F("Version"))) {startSMS(isStringMessage); SIM800.println (ver); EndSMS ();} //запрос версии ПО
else if (!currStr.compareTo(F("Signal-level"))) {digitalWrite (DTR, LOW); delay (150); SIM800.println(F("AT+CSQ")); digitalWrite (DTR, HIGH);} // запрос уровня сигнала GSM
else if (!currStr.compareTo(F("Webasto-ON"))) { startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (!webasto) {StartWebasto (); KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println (F("vkluchena")); EndSMS();}
else if (!currStr.compareTo(F("Webasto-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Webasto "));
if (webasto)StopWebasto (); // если получили команду на выключение и вебаста в настоящий момент включена - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("otkluchena"));EndSMS();}
// если получили команду на включение ДВС и он в настоящий момент выключен - включаем
else if (!currStr.compareTo(F("Engine-ON"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (!engine) { digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = true; prevReportEngine = currmillis; KTOreport = isStringMessage;}
else SIM800.println(F("uzhe ")); SIM800.println(F("start")); EndSMS();}
else if (!currStr.compareTo(F("Engine-OFF"))) {startSMS(isStringMessage); SIM800.println(F("Dvigatel "));
if (engine){ digitalWrite (StartEng, HIGH); prevStartEng=currmillis; StartEng_timer=true; reportEngine = false;} // если получили команду на выключение ДВС и он в настоящий момент работает - выключаем
else SIM800.println(F("uzhe ")); SIM800.println(F("ostanovlen")); EndSMS();}
else if (!currStr.compareTo(F("Impulse"))) {if (!webasto) {ProtocolSTART = IMPULSE; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk GND_impulse")); EndSMS();}}
else if (!currStr.compareTo(F("Startbus"))) {if (!webasto) {ProtocolSTART = STARTBUS; EEPROM.write(ProtocolSTART_cell,ProtocolSTART); webasto = 0;
startSMS(isStringMessage); SIM800.println(F("zapusk BUS")); EndSMS();}}
else if (!currStr.compareTo(F("Potencial"))) {if (!webasto) {ProtocolSTART = POTENCIAL; EEPROM.write(ProtocolSTART_cell,ProtocolSTART);
startSMS(isStringMessage); SIM800.println(F("zapusk +12V Potencial")); EndSMS();}}
else if (!currStr.compareTo(F("DallasAddr"))) {startSMS(isStringMessage); SMSDallasAddr(); EndSMS();}
else if (currStr.endsWith(F("Status"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= STATUSBUS && st<=ANALOG )ProtocolSTATUS = st; EEPROM.write(ProtocolSTATUS_cell,ProtocolSTATUS);
startSMS(isStringMessage); SIM800.print (F("Status: "));
if (ProtocolSTATUS == 0)SIM800.println(F("BUS"));
else if (ProtocolSTATUS == 1)SIM800.println(F("Analog"));
EndSMS();}}
else if (currStr.endsWith(F("HeaterType"))) {if (!webasto) {byte st = currStr.toInt(); if (st >= TTC_E && st<=HYDRONIC) Heater = st; EEPROM.write(Heater_cell,Heater);
startSMS(isStringMessage); SIM800.print (F("Heater: "));
if (Heater == 0)SIM800.println(F("TTC_E")), K_LINE.end(), K_LINE.begin(10400);
else if (Heater == 1)SIM800.println(F("VEVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 2)SIM800.println(F("EVO")), K_LINE.end(), K_LINE.begin(2400, SERIAL_8E1);
else if (Heater == 3)SIM800.println(F("HYDRONIC"));
EndSMS();}}
else if (currStr.endsWith(F("ADDR"))) {if (!webasto) { startSMS(isStringMessage); byte savadr = ConvertAddr();
if (savadr>=0 && savadr<=3) {SIM800.print(F("address ")); TempName(savadr); SIM800.println (F(" saved OK!"));
SMSDallasAddr();
}
else SIM800.println(F("address is incorrect!"));
EndSMS(); }}
else if (currStr.endsWith(F("Delta"))) {if (!webasto) {delta = currStr.toInt(); //
EEPROM.write(delta_cell, delta); startSMS(isStringMessage);
SIM800.print(F("DeltaT: ")); SIM800.print(delta); SIM800.print(F("*C")); EndSMS();}}
else if (currStr.endsWith(F("min"))) {if (!webasto) {TimeWebasto = currStr.toInt(); // для задания время цикла работы отправить сообщение вида "25 min", где 25 время работы в мин
if (TimeWebasto>59) TimeWebasto = 59;
if (TimeWebasto<=15) TimeWebasto = 15;
EEPROM.write(TimeWebasto_cell,TimeWebasto);
startSMS(isStringMessage); SIM800.print(F("Webasto time: ")); SIM800.print(TimeWebasto); SIM800.print(F("min")); EndSMS();}}
else if (currStr.endsWith(F("StartByte"))) {byte Z =currStr.toInt(); if (Z>=0x14 && Z<=0x17) StartByte= Z+12; EEPROM.write(StartByte_cell, StartByte);
HEATER_START[0] = StartByte;
HEATER_PRESENCE[1] = StartByte;
startSMS(isStringMessage); SIM800.print(F("StartByte: ")); SIM800.print(StartByte, HEX); SIM800.print(F("h")); EndSMS();}
else if (!currStr.compareTo(F("ResetNumbers"))) {if (isStringMessage == 1) {startSMS(isStringMessage); SIM800.println(F("Phone numbers are erased")); EndSMS();
TelNumber[1] = TelNumber[0]; TelNumber[2] = TelNumber[0]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[1][i]); EEPROM.write (i+TelNumber2_cell, TelNumber[2][i]); }}}
else if (!currStr.compareTo(F("WriteNumber2"))&& isStringMessage == 1) {
SaveNumber2 = 1; startSMS(isStringMessage); SIM800.println(F("Otpravte lyuboye SMS s nomera#2 dlya sohraneniya nomera")); EndSMS();}
else if (!currStr.compareTo(F("Balance"))) SMSbalance();
isStringMessage = 0;
}
else if (isStringMessage==3){ if (!currStr.compareTo(F("WriteNumber1"))) { TelNumber[1] = TelNumber[3]; for (int i=0; i<SizeTelNumber; i++) {EEPROM.write (i+TelNumber1_cell, TelNumber[3][i]);}
startSMS(1); SIM800.println(F("Tel Number#1 is saving in memory")); SIM800.print("Tel#1: "); SIM800.println (TelNumber[1]); EndSMS();
}
else if (!currStr.compareTo(F("ZAPROS"))) { SMSzapros();}
else if (!currStr.compareTo(F("ZAPROSTEL"))) { SMSzaprosTEL();} // Передача номеров телефонов пользователей по СМС
isStringMessage = 0;
}
else if (isStringMessage==0) { if (TelNumber[1]!=TelNumber[0] && !SaveNumber2){
if (currStr.startsWith("+CMT: \""+TelNumber[1])) { isStringMessage = 1; KTOzapros = 1; }
else if (currStr.startsWith("+CMT: \""+TelNumber[2])) { isStringMessage = 2; KTOzapros = 2; }
else if (currStr.startsWith("+CUSD: 0,")) //если текущая строка начинается с "+CUSD",то следующая строка является запросом баланса
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
else if (currStr.startsWith(F("+CSQ:"))) //если текущая строка начинается с "+CSQ",то значит был запрос на уровень сигнала GSM, отправим ответ запрашивающему
{
startSMS(KTOzapros);
SIM800.print (currStr);
EndSMS();
}
}
else if (currStr.startsWith(F("+CMT:")) && !SaveNumber2) { isStringMessage = 3; for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7];}}
else if (currStr.startsWith(F("+CMT:")) && SaveNumber2) { for (int i =0; i<SizeTelNumber; i++) {TelNumber[3][i]=currStr[i+7]; EEPROM.write (i+TelNumber2_cell, TelNumber[3][i]);} TelNumber[2] = TelNumber[3];
startSMS(2); SIM800.println(F("Vash nomer sochranyon kak Number#2 v pamyati!"));
SIM800.print(F("Tel#1: ")); SIM800.println(TelNumber[1]); SIM800.print(F("Tel#2: ")); SIM800.println (TelNumber[2]); EndSMS(); SaveNumber2 = 0; }
// ----------------------------данные для MQTT
if (currStr.indexOf(F("CLOSED")) > -1 || currStr.indexOf(F("ERROR")) > -1) {
fails = 3;
settingGSM = 1;
}
if (currStr.indexOf(F("OK")) > -1) {
if (!settingGSM){ if (failresets > 4 || (failresets <= 4 && gsmstatus >= setPubPack)) {fails = 0; digitalWrite (DTR, HIGH);}}
if (settingGSM && gsmstatus > WaitGSM && gsmstatus < setBrokerconnect) {
// если подключиться к MQTT серверу не удалось, оставляем настройки только для СМС
// и запускаем таймер на час, чтобы через час ещё раз пробовать:
if (gsmstatus == delSMS && failresets > 4)
{settingGSM = 0;
digitalWrite (DTR, HIGH);
timerMQTTreconnect = currmillis;
TimerMQTTreconnect = 1;
}
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 400;
}
}
if (currStr.indexOf(F("CONNECT")) > -1) if (gsmstatus == setBrokerconnect) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SMS Ready")) > -1) if (gsmstatus == WaitGSM) {
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
fails = 0;
delayATcommand = 300;
}
if (currStr.indexOf(F("SEND OK")) > -1) {
fails = 0;
if (settingGSM && gsmstatus >= setAuthPack && gsmstatus <= setPubPack) {
if (gsmstatus == setPubPack) {
settingGSM = 0;
failresets = 0;
} delayATcommand = 300;
gsmstatus++; if (gsmstatus>16)gsmstatus=16;
}
}
//-------------------------топики MQTT, принимаемые от брокера
if (currStr.indexOf(F("h/ctrl")) > -1)
{
bool comanda = currStr.substring(currStr.indexOf(F("h/ctrl")) + 6).toInt();
if (comanda) {StartWebasto(); report = false;}
else {StopWebasto();}
}
if (currStr.indexOf(F("h/time/ctrl")) > -1) {
String TimeW = currStr.substring(currStr.indexOf(F("h/time/ctrl")) + 11);
TimeWebasto = TimeW.toInt();
EEPROM.write(TimeWebasto_cell,TimeWebasto);
PUBsend("h/time/stat", TimeW, 0);
remTime(0) ;
}
if (currStr.indexOf(F("h/refr0")) > -1) {if (!webasto) {Queue(NEED_MQTTZAPROS); delay (100);} MQTTsendDatastream();}
if (currStr.indexOf(F("h/resresets")) > -1) {ResetNumber=0; EEPROM.write (ResetNumber_cell, ResetNumber); PUBsend("h/resets",String(ResetNumber),0);}
// ------------------------конец данных для MQTT
}
currStr = "";
}
else if ('\n' != currSymb && '$' != currSymb) currStr += String(currSymb);
if (currStr.length()>60) currStr += "\r\n";
}
byte ConvertAddr() {
byte addrbuf[9];
byte Cs=0;
for (byte i = 0; i<9; i++) {
char str[]=" ";
for (byte k = 0; k<2; k++) str[k] = currStr[3*i+k];
addrbuf[i] = strtol(str,NULL,HEX);
if (i<7) Cs+= addrbuf[i] = strtol(str,NULL,HEX);
}
if (OneWire::crc8(addrbuf, 7) == addrbuf[7] && addrbuf[8]>=0 && addrbuf[8]<=3) {
for (byte i = 0; i<size_arrayTemp; i++) {
if (addrbuf[8]==DS18B20[i][8]) {for (byte k=0; k<9; k++) DS18B20 [i][k] = addrbuf[k], EEPROM.write (DallasAddr_cell+addrbuf[8]*10+k, addrbuf[k]);}
}
return addrbuf[8];
}
else return 20;
}
void SMSDallasAddr(){
SIM800.println (F("Dallas Addresses:"));
for (byte i = 0; i<size_arrayTemp; i++) {
TempName(DS18B20[i][8]); SIM800.println();
for (byte k = 0; k<8; k++) {if (DS18B20[i][k]<=0x0F)SIM800.print(F("0")); SIM800.print(DS18B20[i][k],HEX); SIM800.print(F(" "));}
SIM800.println();
}
}
void SMSzaprosTEL(){
startSMS(isStringMessage);
PrintNumbers ();
EndSMS();
}
void SMSbalance() {
digitalWrite (DTR, LOW); // выводим из спячки SIM800 модуль
delay (150);
SIM800.print(F("AT+CMGF=1\r"));
delay(200);
SIM800.println (F("AT+CUSD=1,\"#100#\"")); // команда на замену на транслит *111*6*2# у МТС
delay(1500);
digitalWrite (DTR, HIGH); // вводим в спячку SIM800 модуль
delay (150);
}
void AlarmSMS() {for (byte i = 0; i<2; i++) {startSMS(i+1); SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!")); EndSMS();} alarmSMS = true;}
void PrintNumbers () {for (byte i=0; i<2; i++) {SIM800.print(F("Tel#")); SIM800.print (i+1); SIM800.print(F(" ")); SIM800.println(TelNumber[i+1]);}}
void TempName (const byte &_address_) { if (_address_== VyhlopC) SIM800.print(F("Vyhlop: "));
else if (_address_== EngineC) SIM800.print(F("Engine: "));
else if (_address_== UlicaC) SIM800.print(F("Ulica: "));
else if (_address_== SalonC) SIM800.print(F("Salon: "));
}
void SMSzapros()
{
startSMS(isStringMessage);
if (isStringMessage == 3){
SIM800.println (F("Tel.number#1 not has been save in memory"));
SIM800.println (F("For save Tel#1 send SMS command \"WriteNumber1\""));
PrintNumbers ();
}
else {
SIM800.print (F("Webasto ")); on_off (webasto);
if (webasto) {
SIM800.print (F("StartWebasto "));
if (startWebasto_OK) SIM800.println (F("OK"));
else SIM800.println (F("FAIL"));}
SIM800.print (F("Engine ")); on_off (engine);
SIM800.print (F("IGN ")); on_off (ignition);
SIM800.print (F("Ohrana ")); on_off (ohrana);
if (trevoga) SIM800.println (F("Vnimanie!!! Trevoga!!! Sirena Vkl!"));
SIM800.print(F("Battery: ")); if ((needAction>0 && !noData) || needAction == 0 || ProtocolSTATUS == ANALOG) {SIM800.print (Vpit,1); SIM800.println(F(" V"));}
else if (needAction>0 && noData && ProtocolSTATUS != ANALOG) SIM800.println(F(" No Data"));
SIM800.println(F("Temperatures:"));
if (ProtocolSTATUS != ANALOG){
SIM800.print(F("Heater: ")); if ((needAction>0 && !noData) || needAction == 0) {SIM800.print (HeaterC); grad ();}
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
// ниже распечатаем все температуры
for (byte i = 0; i<size_arrayTemp; i++) {
if (DS18B20[i][8]==VyhlopC && ProtocolSTATUS != ANALOG) {}
else {TempName(DS18B20[i][8]); SIM800.print (Temper(DS18B20[i][8])); grad ();}}
if (ProtocolSTATUS == STATUSBUS){
SIM800.print(F("Errors: ")); if ((needAction>0 && !noData) || needAction == 0) SIM800.println (DTC[0]);
else if (needAction>0 && noData) SIM800.println(F(" No Data"));}
}
EndSMS();
}
void on_off (const bool &stat) {if (stat) SIM800.println (F("ON")); else SIM800.println (F("OFF")); }
void grad () {SIM800.println (F("*C")); }
void ServiceINFO(){
startSMS(isStringMessage);
SIM800.print(F("Heater: "));
if (Heater==TTC_E) SIM800.println(F("TTC/E"));
else if (Heater==VEVO) SIM800.println(F("VEVO"));
else if (Heater==EVO) SIM800.println(F("EVO"));
else if (Heater==HYDRONIC) SIM800.println(F("HYDRONIC"));
SIM800.print(F("Start: "));
if (ProtocolSTART==IMPULSE) SIM800.println(F("GND Imp"));
else if (ProtocolSTART==STARTBUS) {SIM800.print(F("BUS"));
if (Heater== VEVO || Heater== EVO) {SIM800.print(F(" 0x")); SIM800.println (StartByte, HEX);}
else SIM800.println();}
else if (ProtocolSTART==POTENCIAL) SIM800.println(F("Potencial+12V"));
SIM800.print(F("Status: "));
if (ProtocolSTATUS==ANALOG) SIM800.println(F("ANALOG"));
else if (ProtocolSTATUS==STATUSBUS) SIM800.println(F("BUS"));
if (ProtocolSTART!=IMPULSE) {SIM800.print(F("Webasto Time: ")); SIM800.print (TimeWebasto); SIM800.println(F("min"));}
SIM800.print(F("Modem Resets: ")); SIM800.println (ResetNumber);
if (ProtocolSTATUS==ANALOG) {SIM800.print(F("DeltaT: ")); SIM800.print(delta);SIM800.println(F("*C"));}
if (ProtocolSTATUS==STATUSBUS){
if (!noData) {
SIM800.print(F(" BurnFAN ")); on_off (airfan);
SIM800.print(F(" WaterPUMP ")); on_off (waterpump);
SIM800.print(F(" PLUG ")); on_off (plug);
SIM800.print(F(" FuelPUMP ")); on_off (fuelpump);
SIM800.print(F(" Blower ")); on_off (blowerfan);
SIM800.print (F("Errors: ")); SIM800.println (DTC[0]);
if (DTC[0] >0) for (byte i=0; i<DTC[0]; i++) {
if (DTC[i+1]<=0x0F) SIM800.print(F("0"));
SIM800.print (DTC[i+1], HEX);
if (bitRead(DTC[6], i+1)) SIM800.println (F(" Active"));
else SIM800.println (F(" Passive")); }
}
else SIM800.println(F("Heater not answer. No Data"));}
EndSMS();
}
void StartWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StartMessageRepeat = 0;
webasto = 1; digitalWrite (OutWebasto_12V, HIGH);
prevWorkCycleHeater=currmillis;
WorkCycleHeater_timer = true;
}
report = true; prevReport = currmillis;
w_bus_init = NEED;
}
void StopWebasto()
{
if (ProtocolSTART==IMPULSE)
{
digitalWrite (OutWebasto_GndImp, LOW);
GND_impulse_timer = 1;
prevGND_impulse = currmillis;
}
else
{
StopMessageRepeat = 0;
webasto = 0; digitalWrite (OutWebasto_12V, LOW);
WorkCycleHeater_timer = false;
}
report = false;
if (ProtocolSTATUS==STATUSBUS) startWebasto_OK = 0;
}
void Heater_BUS (){
static byte header = 0; // состояние заголовка
static byte message_size = 0; // размер тела принимаемого сообщения, кол-во байт
static byte j = 2; // инкремент
static byte n = 2;
const byte bufsize = 140; // размер буфера принятого сообщения
static byte buf [bufsize] = {0}; // буфер принятого сообщения
static byte checksum = 0; // контрольная сумма входящего сообщения
static uint32_t prevRESETheader=0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
static bool RESETheader_timer = 0; // таймер сброса заголовка если в момент приёма сообщения данные оборвались
if (Heater == EVO || Heater == VEVO){
if (webasto) {
if (StartMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800) && w_bus_init >= 9){
sendMessage (HEATER_START, sizeof(HEATER_START));
StartMessageRepeat++;
Prev_PeriodW_BusStartStop = currmillis;
}
if (StartMessageRepeat>=4){ if (currmillis-Prev_PeriodW_BusMessage>4000) {
if (requiredmessage==1) sendMessage (HEATER_PRESENCE, sizeof(HEATER_PRESENCE));
else if (requiredmessage==2) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));}
else if (requiredmessage==3) sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));
requiredmessage++; if (requiredmessage > 3) requiredmessage = 1;
StopMessageRepeat = 0;
Prev_PeriodW_BusMessage = currmillis;
}}}
else if (StopMessageRepeat<4 && (currmillis-Prev_PeriodW_BusStartStop>800)){
sendMessage (HEATER_STOP, sizeof(HEATER_STOP));
StopMessageRepeat++;
StartMessageRepeat = 0;
Prev_PeriodW_BusStartStop = currmillis;
}
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = millis(); timerInitflag = 1;}
else if ( timerInitflag && (millis() - timerInit>24) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
else if ( timerInitflag && (millis() - timerInit>24) && w_bus_init==2) {K_LINE.begin (2400,SERIAL_8E1 );timerInitflag = 0; w_bus_init=9;
if (needAction>0) sendMessage (HEATER_BEGIN, sizeof(HEATER_BEGIN)); }
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (buf[0]==0x4F || buf[0]==0x44){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
else {header = 0; RESETheader_timer=0;}
}
// длина сообщения
else if (header == 1 ){buf[1]=K_LINE.read(); message_size = buf[1]; if (message_size > bufsize) message_size = bufsize; header = 4;j=2;n=2;checksum = 0;}
// пишем тело сообщения
else if (header == 4 && j< message_size+n) {
buf[j] = K_LINE.read();
if (j<message_size+n-1) checksum^= buf[j]; // подсчёт КС
if (j==message_size+n-1) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum^=buf[i]; // прибавляем к контрольной сумме старт байты
// если контрольная сумма верна:
if ( checksum == buf[message_size+n-1]) {
if (Heater == EVO){
if (buf[2]==0xD0 && buf[3]==0x30) { // если получили сообщение с текущими данными
if (buf[4] ==0x0A) {
airfan = (bool)buf[5]; // получаем флаг работы нагнетателя воздуха
plug = (buf[5] & 0x02)>>1; // получаем флаг работы штифта накаливания
fuelpump = (buf[5] & 0x04)>>2; // получаем флаг работы топливного насоса
waterpump = (buf[5] & 0x08)>>3; // получаем флаг работы циркуляционного насоса
blowerfan = (buf[6] & 0x10)>>4; // получаем флаг работы вентилятора печки автомобиля
}
if (buf[6] ==0x0C) HeaterC = buf[7]-50; // получаем температуру антифриза в котле
if (buf[8]==0x0E) {Vpit = (float)(buf[9]<<8|buf[10])/1000.0;} // получаем напряжение борт сети
if (buf[11]==0x10) {startWebasto_OK = (bool)buf[12]; last_Flame = currmillis;} // проверяем наличие пламени
if (needAction>0 && needAction<NEED_DTCCLEAR) {w_bus_init = 12; sendMessage (HEATER_DTC_REQUEST, sizeof(HEATER_DTC_REQUEST));}
}
if (buf[0]==0x44 && buf[2]==0xC4 && buf[3]==0x00) {w_bus_init = 1; delay (500); K_LINE.flush();} // если получили от котла сбой инита, делаем переинит
if (buf[2]==0xD6 && buf[3]==0x01) {if (needAction>0 && needAction<NEED_DTCCLEAR) w_bus_init = 13;
for (byte h = 0; h< sizeof(DTC); h++) DTC[h]=0;
DTC[0] = buf[4];
for (byte h = 0; h< buf[4]; h++) {DTC[h+1]=buf[h*3+5]; bitWrite (DTC[6], h+1, (buf[h*3+6] & 0x02)>>1);}
}
if (buf[2]==0xD6 && buf[3]==0x03) {startSMS (KTOzapros); SIM800.println (F("DTCs are erased!")); EndSMS(); needAction=0; w_bus_init = 9; NeedTimer = 0; }
if (buf[2]==0xD1 && buf[3]==0x0A) {w_bus_init = 10; }// если получили ответ на сообщение старта коммуникации
}
if (Heater == VEVO){
if (buf[2]==0xD0 && buf[3]==0x05) {startWebasto_OK = buf[7]; last_Flame = currmillis;} // проверяем наличие пламени у VEVO
}
}
// если контрольная сумма не совпала:
//else K_LINE.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=2; checksum = 0;
}
}// end EVO VEVO
else if (Heater == TTC_E){
if (!timerInitflag && w_bus_init==NEED) {K_LINE.end(); pinMode (TX, OUTPUT); digitalWrite(TX, 0); timerInit = currmillis; timerInitflag = 1;}
if ( timerInitflag && (currmillis - timerInit>299) && w_bus_init==NEED) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=2; }
if ( timerInitflag && (currmillis - timerInit>49) && w_bus_init==2) {timerInit = currmillis; digitalWrite(TX, 0); w_bus_init=3; }
if ( timerInitflag && (currmillis - timerInit>24) && w_bus_init==3) {timerInit = currmillis; digitalWrite(TX, 1); w_bus_init=4; }
if ( timerInitflag && (currmillis - timerInit>3024) && w_bus_init==4) {K_LINE.begin (10400); timerInitflag = 0; w_bus_init=9; sendMessage (START_SESSION, sizeof(START_SESSION));}
if (K_LINE.available()){
// первый старт байт
if (header == 0){buf[0]=K_LINE.read();
if (!bitRead (buf[0],6) && bitRead (buf[0],7)){header = 1; RESETheader_timer=1; prevRESETheader = currmillis; }
}
// второй старт байт
else if (header == 1){buf[1]=K_LINE.read(); if (buf[1]==0xF1){ header = 2;} else {header = 0; RESETheader_timer=0;}}
// третий старт байт
else if (header == 2){
buf[2]=K_LINE.read();
if (buf[2]==0x51){ message_size = buf[0];
if (buf[0] !=0x80) {header = 4; message_size&=~0x80; j=3; n=3;}
else {header = 3; j=4;n=4;}
if (message_size > bufsize) message_size = bufsize; checksum = 0;} else {header = 0; RESETheader_timer=0; }
}
// если размер сообщения указан в дополнительном байте (нулевой байт 0x80) читаем этот дополнительный байт:
else if (header == 3){
buf[3]=K_LINE.read();
message_size = buf[3];
if (message_size > bufsize) message_size = bufsize;
checksum = 0; header = 4;
}
// пишем тело сообщения
else if (header == 4 && j< message_size+n+1) {
buf[j] = K_LINE.read();
if (j<message_size+n) checksum+= buf[j]; // подсчёт КС
if (j==message_size+n) header = 5;
j++;}
} // end of K_LINE.available()
// сообщение приняли, действуем
if (header == 5) {
for(byte i = 0; i<n; i++) checksum+=buf[i]; // прибавляем к контрольной сумме старт байты
//for (byte i=0; i<message_size+n+1; i++) {Serial.print (buf[i], HEX); Serial.print(" ");}
// если контрольная сумма верна:
if (buf[message_size+n] == checksum) {
// if (buf[n]== 0xC1) Serial.println ("StartSession OK!!!");
}
// если контрольная сумма не совпала:
//else Serial.println("CRC fail!!!" );
message_size = 0; header=0; RESETheader_timer=0; j=3; checksum = 0;
}
} // end TTC_E
// таймер сброса заголовка если данные оборвались во время приёма заголовка
if (RESETheader_timer && currmillis - prevRESETheader > 500) {RESETheader_timer = 0; header = 0;}
if (currmillis - last_Flame>30000 && ProtocolSTATUS==STATUSBUS) {startWebasto_OK=0; last_Flame = currmillis;} // делаем статус "нет пламени" через 30 сек, если не получаем сообщения от котла
if (Initreset && currmillis - prevInitreset>18000) {Initreset = 0; w_bus_init = 0;} // сброс инита, если прошло более 18 сек после отправки последнего сообщения
if (needAction>0 && needAction < NEED_DTCCLEAR && w_bus_init==10) {if (Heater == EVO) sendMessage (HEATER_STATUS_EVO, sizeof(HEATER_STATUS_EVO));
if (Heater == VEVO)sendMessage (HEATER_STATUS_VEVO, sizeof(HEATER_STATUS_VEVO));
w_bus_init=11;}
if ((NeedTimer && currmillis - prevNeedTimer>11000) || w_bus_init == 13) {NeedTimer = 0;
if (currmillis - prevNeedTimer>11000) noData = 1;
if (needAction == NEED_SMSZAPROS) SMSzapros();
else if (needAction == NEED_SERVICEINFO) ServiceINFO();
else if (needAction == NEED_MQTTZAPROS) {cipsend_begin ();
PUBsend ("h/temp/heat", noData ? "fail" : String(HeaterC),1);
PUBsend ("h/napr", noData ? "fail" : String(Vpit), 1); SIM800.write (0x1A); digitalWrite (DTR, HIGH);}
else if (needAction == NEED_DTCCLEAR) {startSMS(KTOzapros); SIM800.println (F("DTC not cleared. Heater no answer!")); EndSMS();}
w_bus_init = 9; noData = 0; needAction = 0;
}
if (needAction==NEED_DTCCLEAR && w_bus_init==10) { sendMessage (HEATER_DTC_ERASE, sizeof(HEATER_DTC_ERASE)); w_bus_init = 20; }
}
void Reset() {
static bool resettimer = 0; // для таймера удерживания реле в режиме сброс питания
static uint32_t resetTimer = 0; // для таймера удерживания реле в режиме сброс питания
if (!resettimer) {
digitalWrite (ResetGSM, RelayON);
resettimer = 1;
resetTimer = currmillis;
}
else if (currmillis - resetTimer > 5000 ) {
resettimer = 0;
digitalWrite (ResetGSM, !RelayON);
digitalWrite (DTR, LOW);
delay (150);
prevGSMnastr = currmillis;
delayATcommand = 150;
fails = 0;
failresets ++; if (failresets > 4) failresets = 5;
gsmstatus = WaitGSM;
settingGSM = 1;
ResetNumber++; EEPROM.write (ResetNumber_cell, ResetNumber);
}
}
void sendMessage(const byte *command, const size_t size){
Initreset = 1; prevInitreset = currmillis; // включение таймера сброса инита
if (Heater == TTC_E){
const byte siZe = size+4;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) {Mes[i]=size; bitWrite(Mes[i], 7 , 1);}
if (i==1) Mes[i] = 0x51;
if (i==2) Mes[i] = 0xF1;
if (i==3) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum+=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum+=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
else if (Heater == EVO || Heater == VEVO){
const byte siZe = size+3;
byte Mes[siZe];
byte Checksum = 0;
for(byte i=0; i<siZe; i++) {
if (i==0) Mes[i] = 0xF4;
if (i==1) Mes[i]=size+1;
if (i==2) {for (byte t=0; t<size; t++ ) {Mes[i]=command[t]; Checksum^=Mes[i] ;K_LINE.write (Mes[i]); i++;}}
if (i!=siZe-1) Checksum^=Mes[i];
else Mes[i] = Checksum;
K_LINE.write (Mes[i]);
}
}
}
void AUTHsend ()
{
cipsend_begin ();
uint16_t timeBroker = TIMEBROKER;
SIM800.write (0x10);
SIM800.write (strlen(PROTOCOLIUS) + strlen(MQTTNAME) + strlen(MQTTUSER) + strlen(MQTTPASSWORD) + 12);
SIM800.write ((byte)0);
SIM800.write (strlen(PROTOCOLIUS));
SIM800.write (PROTOCOLIUS);
SIM800.write (0x03);
SIM800.write (0xC2);
timeBroker *=60;
byte * timeBr = (byte*)&timeBroker;
SIM800.write(*(timeBr+=1)); // время сеанса связи с брокером, сек (ст. байт)
SIM800.write(*(--timeBr)); // время сеанса связи с брокером, сек (мл. байт)
//SIM800.write (0x04); // время сеанса связи с брокером, сек (ст. байт)
//SIM800.write (0xB0); // время сеанса связи с брокером, сек (мл. байт)
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTNAME));
SIM800.write (MQTTNAME);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTUSER));
SIM800.write (MQTTUSER);
SIM800.write ((byte)0);
SIM800.write (strlen(MQTTPASSWORD));
SIM800.write (MQTTPASSWORD);
SIM800.write (0x1A);
}
void PUBsend (char topPub[30], const String Command, const bool &multidata) {
if (!multidata) {cipsend_begin ();}
SIM800.write (0x30);
SIM800.write (strlen (topPub) + Command.length() + 2);
SIM800.write ((byte)0x00);
SIM800.write (strlen (topPub));
SIM800.write (topPub);
for (byte i = 0; i < Command.length(); i++) SIM800.write (Command[i]);
if (!multidata) {SIM800.write(0x1A); digitalWrite (DTR, HIGH);}
}
void SUBsend (char topSub[20]) {
cipsend_begin ();
SIM800.write (0x82);
SIM800.write (strlen(topSub) + 5);
SIM800.write ((byte)0);
SIM800.write (0x01);
SIM800.write ((byte)0);
SIM800.write (strlen(topSub));
SIM800.write (topSub);
SIM800.write ((byte)0x00);
SIM800.write (0x1A);
}
void MQTTsendDatastream()
{
cipsend_begin ();
PUBsend ("h/time/stat", String(TimeWebasto), 1);
remTime(true);
PUBsend ("h/stat", String(webasto) + "&$", 1);
PUBsend ("h/flame", String(startWebasto_OK), 1);
PUBsend ("h/temp/dvs", String(Temper(EngineC)), 1);
PUBsend ("h/temp/heat", NeedTimer ? "wait" : String(HeaterC),1);
PUBsend ("h/napr", NeedTimer ? "wait" : String(Vpit), 1);
PUBsend ("h/temp/ul", String(Temper(UlicaC)), 1);
PUBsend ("h/temp/sal", String(Temper(SalonC)), 1);
PUBsend ("h/resets", String(ResetNumber), 1);
SIM800.write (0x1A);
prev_refreshMQTT = currmillis;
digitalWrite (DTR, HIGH);
}
void remTime ( bool s) {
int rem = TimeWebasto - ((currmillis - prevWorkCycleHeater) / 60000ul);
PUBsend ("h/remtime", webasto ? String(rem) : "выкл", s);
}
void cipsend_begin ()
{
digitalWrite (DTR, LOW); delay (150);
SIM800.println (F("AT+CIPSEND"));
delay (250);
}
void refreshDataMQTTheaterON ()
{
}
Попробую на выходные уже поставить в машину котёл и переключусь в vevo (может увижу пламя) хотя когда подключен монитор шины в wtt и был выбран котел vevo выскакивала ошибка "контрольной сумы", а когда выбран котёл evo запросы проходят без ошибок , но данных нет... в аналоге нагревая паяльником датчик выхлопа, пламя отображается, напряжение читает нормально
Нужно написать что за котел и по какому протоколу в скетче сделан запуск и чтение статусов. По поводу смс, скетч настройки еерпром сначала заливали?
А может в скетче нужно сделать для 13-значного номера... может нужно место под "+ " с которого начинается номер и потом 12 цифр ?)))
А что у вас такой номер? 12 цифр после +? Если да, то конечно нужно внести изменения
Котёл TermoTop C\Z Mazda брал Б.У , Протокол оставил EVO(ещё не успел протестить с с термотоп) байт запуска сменил в eeprom 0х21( ну и да, все настройки изменил в еепроме и залил его, а потом основной скетч) По поводу номера, да идёт + и ещё 12 цифр(+хххххххххххх) После ночи перестал управляться с MQTT(хотя светодиод на сим продолжал быстро моргать и был в сети, проверил звонком) Отправил смс , перегрузился сим и всё заработало ( понаблюдаю ещё)
Baron8, вот скетч подготовки еепром, там настраивается в том числе и количество символов в номере телефона (включая символ '+'). Там по умолчанию стоит 12. Вам надо поменять там на это количество на 13.
Заливаете этот скетч, дожидаетесь когда led 13 загорится. Потом заливаете основной скетч этот v3.81
И у вас котел то запускается с нашего девайса или нет, я так и не понял? А как с отображением параметра "наличие пламени"? Попробуйте тип котла VEVO, возможно наличие пламени начнет нормально считываться, и котел возможно запустится, если он не запускался. А вот считывание температуры по шине только для EVO пока реализовано.
Сегдня поставил девайс на авто , попробовал с mqtt запустить котел , котел включился , и температуры обновились , вроде все работает, приехал на работу , решил проверить как девайс себя чувствует, температура котла осталась той же на момент выключения котла , я его на 52 градусах заглушил, нажал обновить , обновилась только температура салона, а котел как был на 52 , так и остался , хотя двигатель был прогрет, отправил смс ЗАПРОС , на смс пришла актуальная температура, и после этого стало и на дашборде тоже правильно показывать, как будто через мктт температуру может считать только когда котел работает или после смс с запросом состояния. Ну ладно пошел работать , через час решил еще раз проверить дашборд , но данные не обновлялись, решил посмотреть через вебсокет с компа идут команды или нет, так вот команды на сервер идут, а ответных не было, но через минуту и отвечать девайс начал , но температура котла была не актуальная а последнее показание после выключения котла , попробовал запустить котел с дашборда , котел пустился и температура пошла верная , проверял смсками. Почему то через смс всегда верную температуру котла выдает,( я так проверяю косвенно температуру двигателя , т. к. на двигатель датчик отдельный не цеплял) а вот через мктт только когда котел в работе. Потом вечером попробовал запустить перед уходом с работы , так через мктт не запустился ни в какую , запустил смской , вообще через смс работает стабильно , как и на старом скетче , а вот мктт надо допиливать еще , и еще бы в дашборд добавить напряжение в бортсети , тоже важный параметр. И еще счетчик ресетов модема вырос , на старой прошивке он постоянно в нуле был , значит все стабильно работало, я девайс все лето не выключал , правда котел всего раза два за лето для профилактики запускал , но время от времени смс на девайс отправлял , все равно деньги снимали за симку, так что смысла отключать на лето не было.
ждал когда ты спросишь про обновление температуры котла при выключенном котле через мкютт. Да, я не делал ещё как это сделано через СМС. Если ты помнишь, там нужно ведь будить котёл, и проводить с ним "беседу", чтобы данные вытянуть. Это не очень просто сделать, поэтому я оставил пока так. Слишком много всего нужно было поменять для MQTT и было не до этого. Странно что mqtt через время перестаёт реагировать. У меня на тесте уже третие сутки висит и всё норм - GSM онлайн и отвечает. Правда это не в машине, а дома в уюте и от БП 12В.
Счетчик модема растёт на +1 при каждом перевключении питания всего девайса, это нормально. Т.к. перед подключением к MQTT происходит принудительный рестарт модема.
Остальное будем допиливать потихоньку.
по поводу нету связи через MQTT через некоторое время возможно происходит следующее. Ты запускаешь котёл , пиктограмма кнопка запуска становится зелёная галка. и всё, окончания работы котла ты не дожидаешься. Потом через время когда ты открываешь программу MQTT DASH там будут последние данные, которые были, т.е. кнопка так и останется зелёная. И если при этом её нажать ничего не произойдёт. Тебе нужно при каждом открытии MQTT Dash сначала нажимать кнопку "обновить данные". И должно сброситься время внизу на всех пиктограммах. и обновится ярлыки. Да бывает не сразу команда по MQTT срабатывает, можно подождать сек 10 и снова попробовать. Но у меня так ооочень редко бывает. Обычно с первого раза. Нужно перенести девайс в машину и тоже потестить в полевых условиях.
И у вас котел то запускается с нашего девайса или нет, я так и не понял? А как с отображением параметра "наличие пламени"? Попробуйте тип котла VEVO, возможно наличие пламени начнет нормально считываться, и котел возможно запустится, если он не запускался. А вот считывание температуры по шине только для EVO пока реализовано.
Да , котёл запускается, не дожидаюсь пламени - тушу его на режиме продувки перед поджигом, т.к. для экспериментов ,чистки и замены подшипника нагнетательного вентилятора снял его с авто и запускаю на столе. Раньше пользовался таймером. И впринципе только пару дней как слепил адаптер на ардуинке начал со скетча 3.65 , а тут как раз и MQTT появился))) спасибо за труд! очень интересное устройство .Продолжу эксперименты и отпишусь.
И хотел ещё сказать, что многие Китайцы халтурят с ардуинками , дома много промини от разных китайских друзей все типа 5ти вольтовые и 16мГц , но одну зашиваю заработала сразу,а одну зашил не запускает котёл, смотрю монитор порта w-bus вообще посылки непонятные идут на запуск котла потом прошил её как 3.3 вольта и 8мГц заработала ))) а ещё одна вообще не хочет работать, постоянно (раз в 30-40 сек) перегружает модуль сим800 и ни на что не реагирует, с трудом с её помощью посмотрел адреса 18b20 (пришлось занизить скорость порта до 2400)
Понятно, я не очень силен в программировании (это мягко сказано) но разве нельзя этот процесс опроса самого котла на предмет температуры , напряжения , датчика пламени и т. д. оформить какой нибудь отдельной функцией , которая будет все это делать и сохранять данные в специальные переменные а эти данные будут забирать уже функции ответа на смс ЗАПРОС или ОБНОВИТЬ mqtt ?
Dushman, вот обновлённый скетч. Исправил ситуацию. Теперь при нажатии кнопки "обновить данные" на MQTT, температура и напряжение принимают значение "wait" что значит ждите. Если через 11 сек котёл не ответил, будет надпись "fail" типа неудача. Если ответил - данные обновятся. Также добавил мониторинг и сброс рестартов модема на через MQTT. Метрики обновил в той инструкции в картинках которая выше.
Скетч v3.83 Update: (v3.82 убрал, там были косяки).
залил скетч 3.81, подправил под 13 цифр номер и чудо- смски работают . Тепер заметил такой глюк- стартбайт какой бы не отправлял(21,23) смс приходит как будто изменён, а на котёл идёт постоянно 20((( посылает нужные мне 21 , только когда в этом месте основного скетча поставлю 21(команды для котлов ЭВО byte StartByte = 0x20;)
сейчас посмотрю. да , есть такое дело. сейчас исправлю.
Update: Чуть выше выложил v3.83. Поправил косяк со стартбайтом и ещё кое что.
что то последний скетч не компилируется , пишет что скетч слишком большой.
Разобрался , установил обновление Arduino IDE и все загрузилось.
Душман брат время 2 ночи, да чтоб у тебя хрен на лбу вырос !!!
Это у тебя 2 ночи , а у меня пол восьмого утра , я с утра все обновления с утра заливаю и перед работой проверяю и отписываюсь , ты проснулся а у тебя уже полный отчет . Так что не работает последний скетч , даже модем к жпрс не подключается , мигает очень медленно , и на смс не отвечает. Где то косяк.
Надо епром опять чистить или нет, сравнивал листинги скетча , похоже опять надо епром чистить и номера заливать. Так или нет?
Почистил епром , все равно не работает скетч v.3.83
Хм я ж его проверял. Посмотри пароли мкютт все правильно вписал? позже еще раз проверю
Скорее всего ты не тем скетчем чистил еепром. Правильный для скетчей от 3.81 и выше в сообщении #954. Внимательней давайте.
Нужно теперь еще более новым скетчем чистить епром , а я тем же скетчем чистил епром,что и впрошлый раз а адреса я копипастом с прежнего скетча скопировал, я сначала подумал что новая оболочка не так компилирует ,залил скетч 3.81 , девайс заработал , снова залил 3.83 не работает , модем мигает редко , но на смс не отвечает.
После чистки епрома правильным чистильщиком все заработало, правда на столе , сейчас пойду в тачку воткну , проверю и отпишусь.
Поставил в авто всё заработало. Котел запускается и данные все даёт, даже на выключенном котле.
Дополняю , короче доехал я до гаража, прошло 8 минут, проверяю дашборд , данные не обновляются, жму все подряд кнопки ноль эмоций, отправил смс , есть ответ, зашёл через браузер в веб сокет запросы идут ответа от девайса нет , перегрузил девай всё опять заработало
ок. на столе все красиво. Я на днях подключу девайс к машине, проверим как себя ведёт. В момент проявления глюка - отсутствия ответа девайса по MQTT светодиод быстро мигает (как при GPRS соединении)?
Когда связь теряется то модем так же мигает часто , как и когда связь есть. Пришел я домой решил еще раз проверить , отправил смс на девайс , девайс ответил что есть одна ошибка 3А , эта ошибка появляется каждый раз после потери связи, я ее сбросил ,но сразу ни чего не изменилось , как нет связи так и нет, попробовал опять через вебсокет , команды от телефона идут , от модема ответа нет, потом через какое то время появился отклик от девайса , и данные пошли, но счетчик ресетов увеличился на 1 , я стал баловаться и жать обновить через разные промежутки времени через 30 сек. потом минуту , потом через 2 , 3 и так далее , последний раз через 7 минут связь была , а через 9 уже все отвалилась, отправил смс на девайс , опять ошибка 3А. Я ее стер но связь не появилась . Я думаю что надо отправлять запрос не реже раза в пару минут , а то может из-за отсутствия обмена данными канал разрывается.
У меня устойчивая связь 5-6 минут, потом устройство перестаёт отвечать на запросы, светодиод на сим800 моргает часто, как при нормальной работе при передачи данных по GPRS. За 15 часов работы устройство перегружало модем 46 раз и соответственно после перезагрузки 5-6 минут слал посылки. Если его постоянно "дёргать" раз в 30-40 сек. (я изменял время цикла работы котла) меня хватило на 10 минут, устройство исправно отвечало
Во опять связь появилась, счетчик рессетов опять на один увеличился , примерно пол часа прошло после обрыва связи.
Отправлять на девайс какую нибудь команду пустышку , что бы котел из спячки не выводить , но и сеанс связи не рвать между брокером и девайсом , может это у Мегафона опять какие нибудь заморочки , по времени .
Ага точно у меня сейчас после 6 минут тоже связь пропала . Подожду и проверю появилась ошибка 3А или нет . Эта ошибка значит короткое замыкание на шине W-bus . Надо раз в 4 минуты отправлять пустую команду на девайс и все будет нормально. Я так думаю.
ок молодцы. Инфа понятна,буду седня тестировать.
Раз в пол часа модем перегружается и работает после этого 5 минут , потом опять связь теряестя если не послать на него какую нибудь команду.
Может поможет в вашем устройстве,
1)у меня раз в минуту устройство отправляет данные на mqtt
2)после отправки сообщения на mqtt он должен вернуть сообщение send ok(вроде такое мож ошибаюсь) если не вернул считаем что связь потеряна
3)если связь теряется проверяется регистрация в сети и если она устойчива в течении 5 минут то подключаю тырнет по новой.
Примерно такой алгоритм, связь всё равно теряется неизвестно почему иногда через 20 суток иногда через 10. По расходам (оператор теле2) примерно 15р в месяц.
у меня примерно такой же алгоритм. По крайней мере так задумывал. Видимо где то косяк допустил и не отрабатывает так как задумывалось. Пока в процессе поиска косяка.
опишите точные условия потери связи. Примерно так: Подаем питание на девайс. Дожидаемся, пока в дашборде обновятся пиктограммы. Далее через 6...8 минут связь теряется. Так происходит?
Блин жду, 7,8 и 10 минут. Всё норм. как так? Может надо котёл включить и после этого начинает проявляться?
пока пробуйте в скетче v3.83 (#960) в строке 1378 вместо этого:
1378
if
(currmillis - prev_refreshMQTT > (uint32_t)Refresh_time * 1000)
вставить так
1378
if
(currmillis - prev_refreshMQTT > 122000UL)
Да какие тут условия , подключил питание , дашборд обновился, все работает , кнопки нажимаются , данные обновляются, но если ни чего не делать с котлом, не обновлять данные , не менять время работы ни запускать котел , то через 5-6 минут котел перестает отвечать на команды , ни на обновить ни на запуск , через пол часа происходит перезагрузка и все по новой 5 минут нормальной работы и опять тишина на пол часа , я засекал , через 29 минут он перегружается и все работает . Может это оператор так выставил , что если более 5 минут ни какие байты по соединению не передаются то , оно обрывается, может еще чего , но если за 5 минут произвести с дашбордом каки нибудь манипуляции , обновить или изменить время работы котла , то связь есть еще 5 минут . Как то так. Если котел запущен то данные о температуре сами раз в минуту обновляются. И еще у меня после такой перезагрузки вылазит ошибка 3A.
Еще добавлю , что бы все обновилось надо после получаса бездействия в течении 5 минут нажать обновить , если не нажать, то в плитках напряжения и температуры котла будет fail , ну типо запроса не было на котел и данные не обновились.
я к чему так спрашиваю, потому что сейчас гоняю девайс в хвост и в гриву, но работает собака вообще норм. MQTT активен при любых раскладах. Говорю, пока тогда пробуем #985
у меня сейчас опрос состояния по аналогу ( ево и вево не совпадают с протоколом моего котла ) ведёт сейчас точно так... сейчас откину котёл и проверю без него, может связано с тем , что котёл засыпает и вешает шину на корпус?
Девайс в гараже в авто стоит, скетч подправил, на флешку скинул завтра на работе прошью и проверю, у тебя какой оператор связи? Может это операторские штуки. Ни одного у меня такая байда с пятиминутками.
Я думаю тут не в шине дело, если тыркать время работы котла , обрыва связи тоже нет , хотя этот параметр не в котел записывается.
мтс
Я с Украины, сейчас стоит карта водафон (бывший мтс, он стоял у меня в GPS трекере и отлично работал по gprs) , попробую сменить на другого оператора после эксперимента с откюченным котлом
нет, котёл не причём ((( через 9 минут не отвечает... ставлю карту другого оператора
думаю ни к чему, #985 должно решить проблему. А у вас диагностика (клайн адаптер + программа WTT)имеется? это я к тому чтобы протокол связи посмотреть вашей маздовской печки.
ВОт и пладки приусплели .. Кто там в Украине хотел ?
Я с Украины
можете скооперироваться если близко, наверняка у Паши платы остались
тоже 9 шт лишних ))) ещё версии 8.6 ... В общем сменил карту на другого оператора и вылезла другая проблема - включаю , моргает на сим светодиод ( поиск сети) 8 импульсов, клацает реле и так по кругу .... вытаскиваю ардуину всё ок, находится сеть , несколько раз пробовал перегружать безтолку , воткнул обратно водафон всё ок (условно)..
Вот протокол обмена моего котла с WTT https://drive.google.com/open?id=1EfMFbD2tbpsslwa8ktMlb7OktBpvol3Q
скетч v3.84. Откорректированы временные интервалы взаимодействия с модемом и сервисом MQTT. проверяйте. Сейчас каждые 2 минуты при активном MQTT будет посылаться команда на брокер. А когда котёл включен в т.ч. на брокер шлются также параметры раз в 30 сек. Если подключение к брокеру не получилось раз в 2 минуты просто проверяется активность модема командой "AT"
Вот протокол обмена моего котла с WTT
вот описание вашего протокола W-bus 3.3, у вас правда 3.1, но многое должно совпадать. по идее выбирайте VEVO должно пламя хотя бы видеть
Попробую на выходные уже поставить в машину котёл и переключусь в vevo (может увижу пламя) хотя когда подключен монитор шины в wtt и был выбран котел vevo выскакивала ошибка "контрольной сумы", а когда выбран котёл evo запросы проходят без ошибок , но данных нет... в аналоге нагревая паяльником датчик выхлопа, пламя отображается, напряжение читает нормально