• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
port szeregowy przypisanie odczytanych wartości do zmiennych
#1
    Witam.
Jestem początkujący w arduino i jest to moje pierwsze pytanie, bo niestety nie mogę sobie poradzić i utknąłem w martwym punkcie.
Chcę odczytać zmienne, które są wysyłane z instalacji solarnej, aby móc je później przeliczyć i przesłać do supli. Problemem jest obróbka zmiennych. 
Instalacja solarna wysyła co 30s  9 wartości zmiennych oddzielonych od siebie znakiem ';' . zmienne różnego typu (book, integer, float)
Napisałem prosty program, który odczytuje to, co wysyła sterownik solarów i wyświetlam to na LCD 2x16, ale nie wiem jak przepisać te wartości do konkretnych zmiennych. Znalazłem podobny przykład, ale w przypadku, gdy dane zawierają znak początkowy i końcowy transmisji, a u mnie nie ma znaku początkowego, jest jedynie znak końcowy, za każdym razem inny i nie do końca zrozumiały (zdjęcie w załączniku).


Format danych z ROTEX:
HA; BK; P1; P2; Tk; Tr; Ts,Tv; V

HA: ręczny/automat (1/0)
BK: Status styku blokującego palnik: wyłączony = 0; załączony = 1
 P1: prędkość obrotowa pompy P1 w % (63%, 75%, 83% , 100% itd)
 P2: pompa P2 włączona/wyłączona, 0=wyłączona, 1=włączona
 Tk: Temperatura kolektorów [°C] - integer
 Tr: Temperatura powrotu [°C] -integer
 Ts: Temperatura zbiornika [°C] - integer
 Tv: Temperatura zasilania [°C] - integer
 V: natężenie przepływu (l / min) - float

Poniższy program byłby idealny, gdyby nie to, że nie mam początkowego znaku, a końcowy jest za każdym razem inny


const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing variables to hold the parsed data
char messageFromPC[numChars] = {0};
boolean HA = 0;
boolean BK = 0;
int P1 = 0;
boolean P2 = 0;
int Tk = 0;
int Tr = 0;
int Ts = 0;
int Tv = 0;
float V = 0.0;

boolean newData = false;

//============

void setup() {
    Serial.begin(9600); 
}

//============

void loop() {
  recvWithStartEndMarkers();
  if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //  because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
  char startMarker = '<';
    char endMarker = '>';
    char rc;
  while (Serial.available() > 0 && newData == false){

        rc = Serial.read ();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                  ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index
 
    strtokIndx = strtok(tempChars, ";"); // this continues where the previous call left off
    HA = atoi(strtokIndx);    // convert this part to an integer

    strtokIndx = strtok(NULL, ";");
    BK = atof(strtokIndx);    // convert this part to a integer

    strtokIndx = strtok(NULL, ";");
    P1 = atoi(strtokIndx);    // convert this part to a integer
 
    strtokIndx = strtok(NULL, ";");
    P2 = atoi(strtokIndx);    // convert this part to a integer
   
    strtokIndx = strtok(NULL, ";");
    Tk = atoi(strtokIndx);    // convert this part to a integer
 
    strtokIndx = strtok(NULL, ";");
    Tr = atoi(strtokIndx);    // convert this part to a integer
 
    strtokIndx = strtok(NULL, ";");
    Ts = atoi(strtokIndx);    // convert this part to a integer
 
    strtokIndx = strtok(NULL, ";");
    Tv = atoi(strtokIndx);    // convert this part to a integer
 
    strtokIndx = strtok(NULL, ";");     
    V = atof(strtokIndx);      // convert this part to a float

}

//============

void showParsedData() {
    Serial.println("Dane ROTEX: ");
    Serial.print("HA: ");   
    Serial.println(HA);
    Serial.print("BK: ");
    Serial.println(BK);
    Serial.print("P1: ");
    Serial.println(P1);
    Serial.print("P2: ");
    Serial.println(P2);
    Serial.print("Tk: ");   
    Serial.println(Tk);
    Serial.print("Tr: ");
    Serial.println(Tr);
    Serial.print("Ts: ");
    Serial.println(Ts);
    Serial.print("Tv: ");
    Serial.println(Tv);
    Serial.print("V: ");
    Serial.println(V);
}


pomóżcie proszę, bo sam sobie nie poradzę Sad
 
Odpowiedź
#2
Ale masz ";" miedzy danymi to w czym problem??
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#3
Na początek prześlij wszystko z portu UART na SERIAL. Są przykłady do Arduino, nawet wbudowane, przekopiowywanie znaków z UART na UART1 (lub softuart), znak odebrany -> znak wysłany. Potem to samo można zrobić ale już nie jako znaki lecz rzutując je na typ uint8_t, by zobaczyć liczby zamiast poszczególnych ASCII, bo Twoja analiza wysyłania takiego zestawu na LCD nie jest dobra, możesz pominąć znaki niedrukowane. A tak będziesz miał jasne czy jest jakiś znak kończący czy nie, najczęściej jest znak CR lub NL lub oba. Bo po co sobie utrudniać życie?
Już po pierwszej części można odszyfrować oczami, czy pojawia się jeden ciąg znaków czy linie, jak są linie czy inne sekcje to już jest łatwiej.
Znak rozpoczęcia nie jest w ogóle potrzebny, po prostu odbierasz cały ciąg znaków do znaku końca i potem od nowa.
Jeśli nie ma znaków końca linii to trzeba się nauczyć mierzyć timeouty, tak by po czasie wyłapać kiedy jest transmisja a kiedy jest pauza odpowiednio długa by uznać, że poprzednia transmisja się zakończyła.
Jest mnóstwo przykładów jak robić odbiór/przesył, parsowanie, serialEvent, są przykłady wbudowane, na Strig i tablicach char. Jak będziesz chciał po prostu usiąść i wymyślić to pewnie polegniesz, jak przeanalizujesz kilkadziesiąt podobnych programów to może dojdziesz do tego jak to robić. Tu jeden z przykładów: https://forum.arduinopolska.pl/watek-ste...%C5%82-gsm
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#4
Chyba niezbyt wyraźnie opisałem problem. Ja nic nie wysyłam, tylko odbieram dane przez port RS. Nie mam wpływu na to co dostaje, to jest ciąg który przychodzi co 30sek i jestem go w stanie tylko odebrać. Nie zależy mi na wyświetlaniu czegokolwiek na LCD. Wyświetlacz podłączyłem tylko po to, żeby podejrzeć co wysyła sterownik Rotex, jak wygląda ten ciąg. Na tej podstawie porównując z danymi wyświetlanymi w sterowniku Rotex byłem w stanie zobaczyć które zmienne dostaje. Nie potrafię tylko skutecznie wyciągnąć z tego ciągu zmiennych, żeby je dalej użyć w programie. Znalazłem program zamieszczony powyżej który adaptowałem do mojej sytuacji i byłoby wszystko dobrze, gdyby nie to, ze nie mam tego nieszczęsnego stałego znaku początkowego i końcowego (w powyższym przykładzie <....>) i nie wiem jak wyeliminować tylko uzależnienie od tych znaków.
 
Odpowiedź
#5
A ja Ci napisałem jak masz się do tego zabrać, do opracowania metody musisz poznać ramkę, nie wysyłaj na LCD tylko UART.
Na UART w monitorze możesz zobaczyć po kilku przesyłach coś takiego:
1213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;4123
1213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;4123
1213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;4123
1213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;4123
albo
1213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;41231213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;41231213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;41231213;1231231;231;23123412;31241;241241241;412412412;124124;2412;412412;4123
To niewielka wiedza, ale bardzo tanio zdobyta.
Jak będzie ta druga opcja to zostaje pomiar czasu (i tak warto to robić), znak który przyleci po przerwie np.>=2000ms jest pierwszym, a poprzedni był ostatnim. Nie zobaczysz tego na LCD.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#6
Ale ma przecież między danymi ";" wiec po co odczytywać cała ramkę jak z bufora można odczytać pojedynczo od znaku do znaku. Bo i tak to musi robić.

Oczywiście mimo wszystko trzeba odczytać całą ramkę jw. bo na końcu może być niewidoczny znak dla twojego kodu.
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#7
Powinien odczytać całą ramkę i potem ją obrobić bo nie będzie wiedział jakie dane mu właśnie wleciały. Tych danych nie jest dużo, jeden bufor, potem kopia do drugiego na którym się pracuje i potem się przepisuje od ; do ; zamienia na liczbę i przypisuje do zmiennych liczbowych.
Tu dwa sposoby jak to robić ze stringa, nie pamiętam czy sam to pisałem, czy gdzieś znalazłem, chodziło o porównanie czasu operacji różnymi metodami:
Kod:
//#include <String.h>
uint32_t start1,stop1,trwalo;
//#define buffsize 64
//char input[buffsize];
String informacja_z_pliku = "&PRZEK1=0&PRZEK2=1&PRZEK3=3&TEMP1=22.6&TEMP2=25.4&TEMP3=23.7";
String informacja_z_pliku2 = "&PRZEsdafK1=0&PRZsfadEK2=1&PRZsfsdafEK3=3&TadsfasdEMP1=22.6&TadsfasMP2=25.4&TEasdfasdMP3=24.7&TEasdfasdfsdfsdMP3=25.7&TEasdsdfsdfsdfasdMP3=26.7&TEasdfsdfsdfdsasdMP3=27.7";
void   parsujpolecenia(String napis)
{
//Serial.println(F("C strtok: "));
    uint8_t index=0;
   uint8_t buffsize= napis.length()+1;
    char input[buffsize];
    napis.toCharArray(input, buffsize) ;
   char * polecenie=input; //to nie jest konieczne, kopiuje by  input był wolny na inne cele
   
//  Serial.print(F("Caly napis: "));
//   Serial.println(polecenie);

   polecenie = strtok(polecenie, "&");


while (polecenie != 0)
{
Serial.println(polecenie);
polecenie = strtok(NULL, "&"); //dzielimy reszte napisu w miejscach gdzie jest "&"
// Find the next command in input string
}
}

void substringowanie(String jakisnapis)
{
// Serial.println(F("Substringi"));

int index=0;

String tempstring,newstring=jakisnapis.substring(1);
//   Serial.print(F("Caly napis: "));
//  Serial.println(informacja_z_pliku);

  while( (index=newstring.indexOf('&'))>=0)
  {
    tempstring= newstring.substring(0,index);
    newstring=newstring.substring(index+1);
    Serial.println(tempstring);
   
  }
  Serial.println(newstring);
}

// Arduino initialization function.
void setup()
{
Serial.begin(115200);


for(int i=0; i<10;i++)
{
  Serial.println(F("Strtok"));
  delay(100);
start1=micros();
parsujpolecenia(informacja_z_pliku);
stop1=micros();
delay(100);
Serial.println((uint32_t)(stop1-start1));
delay(100);
start1=micros();
parsujpolecenia(informacja_z_pliku2);
stop1=micros();
delay(100);
Serial.println((uint32_t)(stop1-start1));
delay(100);
Serial.println(F("Substringi"));
delay(100);
start1=micros();
substringowanie(informacja_z_pliku);
stop1=micros();
delay(100);
Serial.println((uint32_t)(stop1-start1));
delay(100);
start1=micros();
substringowanie(informacja_z_pliku2);
stop1=micros();
delay(100);
Serial.println((uint32_t)(stop1-start1));
delay(100);
}
}


void loop()
{
 


// Teraz mozna cos zrobic ze zmiennymi
}
Są też gotowe biblioteki do parsowania, wiele rzeczy leci tekstem z Internetu a ArduinoJson to rozczłonkowuje wg upodobań użytkownika.
A tu przykład z odbiorem i konwersją do tablicy floatów:
Kod:
//#include <String.h>
#define buffsize 128
char input[buffsize];
float dane [10]; // jakie i ile  zmiennych odbieramy z UART
byte index = 0;
boolean stringComplete = false;  // gdy cale polecenie/napis odebrany

/*
Tarierwert (P): 42119   Tarierwert (G):        1378.3

  23.2:    26.4:     82174:    1310.8:     82160:    40041: 1310.2   S
  */
void   parsujpolecenia()
  {
    //polecenie ma wygladac tak: cmd=zmienna1,zmienna2,zmienna3,zmienna4  z zalozenia sa to 4 liczby calkowite
    // do 5 cyfr, dlatego buffsize32 ma zapas, dla innych trzeba zmodyfikowac bufor, tablice zmiennych, funkcje
    // do konwersji napisu na liczby
    //cmd=234,342,553,3432
   
     uint8_t index=0;
    char * polecenie=input;
    Serial.print("Otrzymane polecenie: ");
    Serial.println(polecenie);

    char* command1 = strtok(polecenie, ":");

//if (strcmp(command1 , "cmd")==0){  //sprawdzenie czy czesc przed znakiem = jest rowna cmd
dane[0]=atof(command1);
index++;
while (command1 != 0)
{
Serial.println(command1);
command1 = strtok(NULL, ":"); //dzielimy reszte napisu w miejscach gdzie jest ","

// Find the next command in input string
if(index<10) dane[index]=atof(command1); //konwersja napisu na INT i zapisanie do kolejnej pozycji w tablicy
index++;
}
//}
// else Serial.println("Polecenie nieprawidlowe");
  Serial.println("Aktualne parametry:");
  Serial.print("Zmienna 1 = ");
  Serial.println(dane[0]);
  Serial.print("Zmienna 2 = ");
  Serial.println(dane[1]);
  Serial.print("Zmienna 3 = ");
  Serial.println(dane[2]);
  Serial.print("Zmienna 4 = ");
  Serial.println(dane[3]);
    stringComplete = false;
  }


// Arduino initialization function.
void setup()
{
  Serial.begin(115200);
}


void loop()
{

if (stringComplete) parsujpolecenia();
// Teraz mozna cos zrobic ze zmiennymi
}
 


void serialEvent() {
  while (Serial.available()>0) {

char aChar = Serial.read();
     if(aChar == '\n')
     {
       // wykryto znak konca linii \n, koniec odbioru
       input[index] = 0;
       index = 0;
       stringComplete = true;
     }
     else
     {
        input[index] = aChar;
        if(index<buffsize) index++;
        input[index] = '\0'; // Na koncu napisu wstawiamy 0   
     }
  }
}
Jeszcze coś innego:
Kod:
//#include <String.h>
#define buffsize 32
char input[buffsize];
uint32_t dane [4]; // jakie i ile  zmiennych odbieramy z UART
byte index = 0;
boolean stringComplete = false;  // gdy cale polecenie/napis odebrany


void   parsujpolecenia()
  {

   
     uint8_t index=0;
    char * polecenie=input;
    Serial.print("Otrzymane polecenie: ");
    Serial.println(polecenie);

    char* command1 = strtok(polecenie, ":");

if (strcmp(command1 , "2.6.2")==0){  //sprawdzenie czy czesc przed znakiem = jest rowna cmd
//tylko reszta napisu za :
command1 = strtok(NULL, ",");
  Serial.print("Zmienna 1 jako napis = ");
  Serial.println(command1);
  dane[index]=atol(command1); //atol() dla long, atoi() dla int lub atof() dla float
/* To jest do dluzszych danych oddzielonych np. jak tu ,
while (command1 != 0)
{
Serial.println(command1);
command1 = strtok(NULL, ","); //dzielimy reszte napisu w miejscach gdzie jest ","

// Find the next command in input string
if(index<4) dane[index]=atoi(command1); //konwersja napisu na INT i zapisanie do kolejnej pozycji w tablicy
index++;
} */
}
  else Serial.println("Polecenie nieprawidlowe");
  Serial.println("Aktualne parametry:");
  Serial.print("Zmienna 1 jako wartosc liczbowa = ");
  Serial.println(dane[0]);

  stringComplete = false;
  }


// Arduino initialization function.
void setup()
{
  Serial.begin(115200);
}


void loop()
{

if (stringComplete) parsujpolecenia();
// Teraz mozna cos zrobic ze zmiennymi
}
 


void serialEvent() {
  while (Serial.available()>0) {

char aChar = Serial.read();
     if(aChar == '\n')
     {
       // wykryto znak konca linii \n, koniec odbioru
       input[index] = 0;
       index = 0;
       stringComplete = true;
     }
     else
     {
        input[index] = aChar;
        if(index<buffsize) index++;
        input[index] = '\0'; // Na koncu napisu wstawiamy 0   
     }
  }
}
Nie pamiętam do czego to mi było, podobna konstrukcja była w przykładzie do zegara RTC, gdzie przez uart wysyła się ciąg znaków oznaczających daną chwilę, program to parsuje i przypisuje zmiennym. Można wgrać do UNO, wysłać jakieś liczby poprzedzielane przecinkami czy floaty i popatrzeć jak to działa/czy nie działa.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#8
Kupiłem konwerter UART-RS232, podłączyłem sterownik Rotex do laptopa i podejrzałem serial monitor.
Otrzymałem dane w poniższej postaci:
   
Nadal nie wiem, jak te dane ściągnąć do ESP8266 i przypisać je do poszczególnych zmiennych. 
Działanie każdego  programu który 'umęczęczę' symuluję w Tinkercadzie, ale niż nie działa. Gdybym tylko miał chociaż znak na początku i końcu każdego ciągu byłbym w stanie skorzystać z programu, który przedstawiłem w pierwszym poście, ale niestety Sad
Pomoże ktoś?
 
Odpowiedź
#9
Wszystkiego najlepszego w kolejnym roku zabawy z Arduino.
Przecież masz znak specjalny, nie jest to ciąg znaków tylko ładne linie, więc masz przykład jak odebrać taką linię nawet w przykładach Arduino, serialEvent, a jak poszukasz w moich postach to wrzucałem tu na forum przykład na bazie serialEvent jak odebrać linię tekstu z parametrami oddzielonymi jakimiś znakami, np. ";" , przypisać je do zmiennych i użyć.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#10
Masz ładne linie tekstu. Jak program się uruchamia pierwszy raz nawiązując kontakt, to przylatują komunikaty z napisami w kilku liniach, jak linia zaczyna się od znaku innego niż '0' lub '1' to go wrzucasz w kosz. Potem lecą kolejne linie.
Jeśli za każdym razem jest taka struktura komunikatu, to robisz to wg przykładów z posta #7.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości