Ви не увійшли.
Сторінки 1
Доброго дня! Пишу код для домашнього проєкту (самонавчання).
Код має обробляти пакети що приходять з дисплея по uart в вигляді 0x5A 0xA5 0x06 0x83 0x50 0x01 0x01 0x00 0x03.
та надсилати пакети з інформацією у вигляді 0x5A 0xA5 0x06 0x82 0x50 0x01 0x01 0x00 0x03
де
Байти Значення
5A A5 старт пакета
06 довжина
83 читання VP () дисплей прочитав значення в памяті і передав в порт, якщо дисплей отримує 0х82 він записує в память
50 01 адреса
01 кількість слів
00 03 дані
код має читати пакети данних з дисплея та періодично відправляти на дисплей пакети з данними з 5 датчиків температури, датчика потоку, та час з двох таймерів. а також оновлювати на дисплеї стан кнопок відповідно до поточного стану реле, бо в коді є автоматичне вмикання реле по досягненню температур.
я так розумію що я щось намудрував з парсингом по uart
нижче прикріпляю код мого проекту.
/* =========================================================
Arduino Nano + DWIN + DS18B20 + Flow sensor + RTC DS3231
Функції:
• 5 датчиків температури DS18B20 (1-Wire)
• 3 реле
• 2 таймери роботи
• автоматичне керування температурою
• підрахунок потоку води
• зв'язок з дисплеєм DWIN
• захист від зависання UART та датчиків
========================================================= */
#include <SoftwareSerial.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <RTClib.h>
/* ---------------- DWIN UART ----------------
налаштування uart для роботи з дисплеем */
#define DWIN_RX 10
#define DWIN_TX 9
SoftwareSerial dwin(DWIN_RX,DWIN_TX);
/* ---------------- DS18B20 ---------------- */
#define ONE_WIRE_BUS 12
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
/* --------- ФІЗИЧНІ АДРЕСИ ДАТЧИКІВ ---------
АДРЕСИ заповнити вручну
---------------------------------------------- */
DeviceAddress sensorAddr[5] =
{
{0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x01}, // датчик 1
{0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x02}, // датчик 2
{0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x03}, // датчик 3
{0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x04}, // датчик 4
{0x28,0xFF,0x00,0x00,0x00,0x00,0x00,0x05} // датчик 5
};
/* ---------------- RTC ----------------
модуль точного часу ----------------*/
RTC_DS3231 rtc;
bool rtc_ok = false;
/* ---------------- RELAYS ----------------
налаштування пінів реле--------------- */
byte relayPin[3] = {4,5,6};
bool relayState[3] = {0,0,0};
/* ---------------- FLOW SENSOR ----------------
налаштування датчика потоку----------------- */
#define FLOW_PIN 2
volatile uint16_t flow_pulses=0;
uint16_t flow_lpm=0;
/* ---------------- TEMPERATURES ----------------
масив для зберігання температур------------- */
float temp[5];
int lastTempSend[5] = {0,0,0,0,0};
/* ---------------- TIMERS ---------------- */
uint32_t heatStart=0;
uint32_t coolStart=0;
uint32_t heatTime=0;
uint32_t coolTime=0;
/* ---------------- LOOP TASK TIMERS ----------------
налаштування затримок в циклі для зменшення навантаження на порт */
uint32_t tUART=0;
uint32_t tTEMP=0;
uint32_t tFLOW=0;
uint32_t tTIMER=0;
/* ---------------- UART RX ---------------- */
byte buf[8];
byte idx=0;
byte state=0;
uint32_t uartTimer=0;
/* ----------------- FLOW INTERRUPT
рахує імпульси датчика потоку */
void flowISR()
{
flow_pulses++;
}
/* --------------SEND VALUE TO DWIN
формування пакету запису VP------*/
void sendVP(uint16_t addr,int value)
{
byte pkt[8];
pkt[0]=0x5A;
pkt[1]=0xA5;
pkt[2]=0x05;
pkt[3]=0x82;
pkt[4]=addr>>8;
pkt[5]=addr&0xFF;
pkt[6]=value>>8;
pkt[7]=value&0xFF;
dwin.write(pkt,8);
}
/*------------ RESET SOFTWARE SERIAL
автоматичний reset якщо UART завис */
void resetUART()
{
dwin.end();
delay(10);
dwin.begin(115200);
}
/* ------------------ PARSE PACKET
обробка кнопок реле----------------*/
void parsePacket()
{
if(buf[3]==0x83 && buf[4]==0x50)
{
byte val=buf[7];
relayState[0]=bitRead(val,0);
relayState[1]=bitRead(val,1);
relayState[2]=bitRead(val,2);
}
}
/*----------------- READ DWIN PACKETS
метод state machine: 0-5A,1-A5,2-6 байт
парсинг пакетів--------------------------------*/
void readDWIN()
{
while(dwin.available())
{
byte b=dwin.read();
uartTimer=millis();
switch(state)
{
case 0: // чекаємо стартовий байт 0x5A
if(b==0x5A){ buf[0]=b; state=1; }
break;
case 1: // чекаємо другий байт 0xA5
if(b==0xA5){ buf[1]=b; idx=2; state=2; }
else state=0;
break;
case 2: // зчитуємо решту пакету
buf[idx++]=b;
if(idx>=8)
{
parsePacket();
idx=0;
state=0;
}
break;
}
}
// reset UART якщо завис
if(millis()-uartTimer>500)
{
resetUART();
uartTimer=millis();
}
}
/* =-------------- READ TEMPERATURES
зчитування 5 датчиків з захистом обриву */
void readTemps()
{
sensors.requestTemperatures();
for(int i=0;i<5;i++)
{
float t=sensors.getTempC(sensorAddr[i]);
if(t==-127 || t>110 || t<-40) t=0;
temp[i]=t;
int v=t*100;
// передача лише якщо змінилося
if(v!=lastTempSend[i])
{
uint16_t addr;
switch(i)
{
case 0: addr=1005; break;
case 1: addr=1006; break;
case 2: addr=1008; break;
case 3: addr=1009; break;
case 4: addr=1010; break;
}
sendVP(addr,v);
lastTempSend[i]=v;
}
}
}
/* --------------- AUTOMATIC RELAY CONTROL
автоматичне керування реле по температурі */
void autoRelays()
{
if(temp[3]>=70) relayState[1]=1;
if(temp[1]>=25) relayState[2]=1;
}
/*----------------------- UPDATE RELAYS
оновлення фізичних реле та дисплея */
void updateRelays()
{
static bool lastRelay[3]={0,0,0};
for(int i=0;i<3;i++)
{
digitalWrite(relayPin[i],relayState[i]);
if(relayState[i]!=lastRelay[i])
{
sendVP(0x5001,relayState[0] | relayState[1]<<1 | relayState[2]<<2);
lastRelay[i]=relayState[i];
}
}
}
/*------------------- READ FLOW SENSOR
оптимізований підрахунок потоку ----*/
void readFlow()
{
noInterrupts();
uint16_t p=flow_pulses;
flow_pulses=0;
interrupts();
flow_lpm=p;
sendVP(1007,flow_lpm);
}
/* --------------------------UPDATE TIMERS
таймери роботи нагріву та охолодження */
void updateTimers()
{
uint32_t now;
if(rtc_ok) now=rtc.now().unixtime();
else now=millis()/1000;
if(relayState[0])
{
if(heatStart==0) heatStart=now;
heatTime=now-heatStart;
}
if(relayState[2])
{
if(coolStart==0) coolStart=now;
coolTime=now-coolStart;
}
uint16_t h1=heatTime/3600;
uint16_t m1=(heatTime%3600)/60;
uint16_t h2=coolTime/3600;
uint16_t m2=(coolTime%3600)/60;
sendVP(2001,h1);
sendVP(2002,m1);
sendVP(2003,h2);
sendVP(2004,m2);
}
/* ---------------------- SETUP--------------- */
void setup()
{
Serial.begin(115200);
dwin.begin(115200);
sensors.begin();
Wire.begin();
rtc_ok=rtc.begin();
for(int i=0;i<3;i++)
{
pinMode(relayPin[i],OUTPUT);
}
pinMode(FLOW_PIN,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(FLOW_PIN),flowISR,RISING);
}
/*----------------- LOOP (оптимізований планувальник задач)------------- */
void loop()
{
/* ---------------- читання UART ---------------- */
if(millis()-tUART > 5)
{
tUART = millis();
readDWIN();
}
/* ---------------- температури ---------------- */
if(millis()-tTEMP > 1000)
{
tTEMP = millis();
readTemps();
autoRelays();
updateRelays();
}
/* ---------------- потік води ---------------- */
if(millis()-tFLOW > 1000)
{
tFLOW = millis();
readFlow();
}
/* ---------------- таймери ---------------- */
if(millis()-tTIMER > 1000)
{
tTIMER = millis();
updateTimers();
}
}проблема в тому що код читає неправильно данні з порта, і відповідно не виконує обробку натискання з дисплею.
коли пишу простий код то бачу що данні приходять коректні.
Неактивний
додав для відладки в порт повідомлення отримав ось таке у відповідь
і реле не відпрацьовує
DWIN RX -> 5A A5 6 83 50 1 1 0
Relay command: 0
Sensor 0 error
Temp 1 = 0.00
Sensor 1 error
Temp 2 = 0.00
Sensor 2 error
Temp 3 = 0.00
Sensor 3 error
Temp 4 = 0.00
Sensor 4 error
Temp 5 = 0.00
UART RESET
Heat timer 0:0
Cool timer 0:0
DWIN SEND -> VP:2001 value:0
DWIN SEND -> VP:2002 value:0
DWIN SEND -> VP:2003 value:0
DWIN SEND -> VP:2004 value:0
Flow pulses/sec: 0
Остання редакція Primaangel88 (Вчора 13:57:06)
Неактивний
Якось воно складно дуже.
Я б порадив складати символи з uart в кільцевий буфер, і запускати обробку тільки коли він заповниться.
Ну і в які місця ви добавили print не очевидно, так що що насправді там відбувається ніхто крім вас не знає.
Не хочете спробувати freertos? Воно є під arduino nano
Так то esp32/rpi2040 коштує не сильно дорожче, але під rtos робота з купою датчиків виглядає значно простіше.
Ну если "простой" работает, то надо искать где "сложный" всё ломает. Скорее всего в крутилке задач. пока выполняется иная от приема задача, то получается все данные, которые надо принять улетают в трубу. правильно же сделать прием по прерыванию.
Неактивний
if(millis()-tUART > 5) { tUART = millis(); readDWIN(); }
Навіщо ці паузи між читаннями? dwin.available() просто повертає 0, якщо даних нема.
Що таке "навантаження на порт" і навіщо його "зменшувати"? SoftwareSerial, наскільки бачу, для прийому використовує PCINT. А передача, на відміну від HardwareSerial, не буферизована: write() не повертається поки не видасть весь байт.
Взагалі я би рекомендував для звʼязку з дисплеєм використовувати апаратний UART, а прошивати програматором. А краще взагалі взяти контролер з двома UART'ами, і не звʼязуватись із SoftwareSerial.
проблема в тому що код читає неправильно данні з порта
Можете показати, які саме дані неправильні? Тобто "дисплей відправив ось таку послідовність, а прочиталась ось така".
Ну если "простой" работает, то надо искать где "сложный" всё ломает.
+1
Неактивний
case 2: // зчитуємо решту пакету buf[idx++]=b; if(idx>=8)
Тут би по-хорошому читати байт довжини, а потім buf[2] байтів пакета. Бо якщо дисплей відправить щось не 8-байтове, парсер розсинхронізується.
Неактивний
якщо дисплей відправить щось не 8-байтове, парсер розсинхронізується.
Можливо підвисання дисплею з цим і пов"язані.
Взагалі, тут же плюси і ООП. Зробити лібу в arduino-style. Об"єкт з методом tick(), який викликається в loop(), методом data_available(), і всі парсери й буфери сховати всередині. Буде простіше.
Сторінки 1