Arduino Polska Forum

Pełna wersja: DS18S20 I RS485
Aktualnie przeglądasz uproszczoną wersję forum. Kliknij tutaj, by zobaczyć wersję z pełnym formatowaniem.
Witam.
Czy ktos moze doradzic z kodem. Sprawa w skrocie wyglada tak: Pierwsze arduino podlaczone dwa sensory temperatury RS18S20 oraz LCD 2004 ktory wyswietla temperatury z obu sensorow oraz modul RS485 ktory wysyla temperatury z obu sensorow np 19.64 stC i 22.36 stC, monitor poru pokazuje obie temperatury na ekranie PC. Drugie Arduino z wyswietlaczem LCD 2004 i RS485 i tak: monitor portu szeregowego odbiera tylko liczby czlkowite bez liczb po przecinku. I tutaj pytanie: Jak sformulowac kod, aby odebrac dane z liczbami po przecinku oraz wyswietlic obie temperatury na LCD. Jesli ktos moze pomoc i potrzebuje kod master i slave to moge wyslac. Niestety w internecie nie znalazlem nic co moglo by pomoc.
Z gory dziekuje.
Najprostsze wykorzystanie RS485 niczym nie różni się od wysyłania komunikatów po UART między dwoma Arduino, a do tego to przykładów już znajdziesz na pęczki.
Możesz mieć co prawda na czas wysyłania ustawienie kierunku linii RS485, ale są też takie moduły, które ustawiają kierunek automatycznie.
Kod można wkleić w post, im więcej osób go zobaczy, tym większa szansa, że ktoś zauważy na którym etapie gubisz dane.
Jeżeli wyświetlanie na drugim Arduino ma być jedynym, do czego wykorzystujesz dane o temperaturach to nie musisz ich w żaden sposób konwertować. W nadającej maszynie za pomocą sprintf() składasz to, co masz wysłać i co ma pokazać drugie Arduino, a na drugim tylko wrzucasz wszystko, co odebrałeś z RS485 na ekran.
Oto moje skatche...

Dla MASTER:

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
#include <OneWire.h>
#include <DallasTemperature.h>
//=====================================================================================================================================
#include "Arduino.h"
// Library to allow a Serial port on arbitrary pins
#include <SoftwareSerial.h>
// These are the pins we'll be talking to the RS485 device on
#define RS485rx 2 // RS485 Receive pin
#define RS485Tx 3 // RS485 Transmit pin
#define RS485Transmit HIGH
#define RS485Receive LOW
#define baudRate 115200
SoftwareSerial RS485(RS485rx, RS485Tx);
int buttonState = 0;

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

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 4

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Addresses of 3 DS18B20s
uint8_t sensor1[8] = { 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX };
uint8_t sensor2[8] = { 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX };
uint8_t sensor3[8] = { 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX, 0xX };
float tempC1 = 0;
float tempC2 = 0;
float tempC3 = 0;
float times =10000;


void setup()
{
lcd.init();
lcd.begin(20, 4);
lcd.backlight();
Wire.begin();
sensors.begin();
lcd.setCursor(4, 0);
lcd.print("TEMPERATURA");
Serial.begin(9600);  
sensors.setResolution(sensor1, 12);
sensors.setResolution(sensor2, 12);
sensors.setResolution(sensor3, 12);

 pinMode(5, OUTPUT);
 pinMode(A0, INPUT);
 
 sensors.requestTemperatures();
 float tempC1 = sensors.getTempC(sensor1);
 float tempC2 = sensors.getTempC(sensor2);
 float tempC3 = sensors.getTempC(sensor3);
 lcd.setCursor(1, 1);
  lcd.print("Wewnatrz: ");
  lcd.setCursor(11, 1);
  lcd.print(tempC1);
  lcd.setCursor(1, 2);
  lcd.print("Zewnatrz: ");
  lcd.setCursor(11, 2);
  lcd.print(tempC2);
  lcd.setCursor(1, 3);
  lcd.print("Sensor 3: ");
  lcd.setCursor(11, 3);
  lcd.print(tempC3);

 

//=====================================================================================================================================
 RS485.begin(baudRate);
//=====================================================================================================================================

}

void loop()
{
 
  buttonState = analogRead(A0);
  if (buttonState >= 1000)
  {
    digitalWrite(5, HIGH);
  }
  if (buttonState <= 1000)
  {
    digitalWrite(5, LOW);
  }
 
static float count = 0;
count++; if (count==times)
{
sensors.requestTemperatures();
float tempC1 = sensors.getTempC(sensor1);
float tempC2 = sensors.getTempC(sensor2);
float tempC3 = sensors.getTempC(sensor3);
float rxtempC1;
float rxtempC2;
float rxtempC3;

  lcd.setCursor(11, 1);
  lcd.print(tempC1);
  lcd.setCursor(11, 2);
  lcd.print(tempC2);
  lcd.setCursor(11, 3);
  lcd.print(tempC3);

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

Serial.print("Sending:");Serial.println(sensors.getTempC(sensor3));
 RS485.write(sensors.getTempC(sensor3));
 delay(1);

 if (RS485.available())
 {
rxtempC3 = RS485.read();
 // Display it on the Serial Monitor as a char (not an integer value)
 Serial.print(" Got back:"); Serial.println(rxtempC3);
 }
 count =0;
}
//=====================================================================================================================================
}



Oraz SLAVE:


#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
int tempC1= 0;
int tempC2= 0;
int tempC3= 0;
//=====================================================================================================================================
#include "Arduino.h"
// Library to allow a Serial port on arbitrary pins
#include <SoftwareSerial.h>
// These are the pins we'll be talking to the RS485 device on
#define RS485rx 2 // RS485 Receive pin
#define RS485Tx 3 // RS485 Transmit pin
#define RS485Transmit HIGH
#define RS485Receive LOW
#define baudRate 115200
// Define the RS485 object
SoftwareSerial RS485(RS485rx, RS485Tx);
float rxtempC1;
float rxtempC2;
float rxtempC3;
//=====================================================================================================================================

void setup()
{
lcd.init();
lcd.begin(20, 2);
lcd.backlight();
Wire.begin();
Serial.begin(9600);  


//=====================================================================================================================================
RS485.begin(baudRate);
//=====================================================================================================================================

}

void loop()
{
//=====================================================================================================================================
// Is there something on the serial pin?
 if (RS485.available())
 {
 float rxtempC3 = RS485.read();
 Serial.print("Received:");Serial.println(rxtempC3);
 lcd.setCursor(1, 0);
  lcd.print(rxtempC3);
  lcd.print("   ");
 delay(10);
 // Send back a modified value
 RS485.write(rxtempC3);
 }
//=====================================================================================================================================
}



Monitor portu dla MASTER:
Sending:19.69


Got back:19.00


Sending:19.69


Got back:19.00


Sending:19.69

Got back:19.00

Monitur portu dla SLAVE:
Received:19.00


Received:19.00


Received:19.00


Received:19.00

Received:19.00
RS485.write(sensors.getTempC(sensor3));
nie napisałeś, że chcesz wysłać jako floata, to wysłał int'a. W AVR/C czy C++ jest domyślnie do wszystkiego int, liczba całkowita, hasło dnia "promocja do integer".
Na serial drukujesz float tempC1 to on wie, że to float.
Poza tym domyślnie DS działa tak, że jak zapytasz o temperaturę to on dopiero wtedy pyta jaka jest, termometr zaczyna pomiar, to trwa 750ms, wysyła i masz. Jak w jednym loop pytasz o 3, a potem znowu, i jeszcze wysyłasz na LCD, serial, RS485 to taki loop trwa kilka sekund, a powinien kilka us.
Zrób sobie zmienną globalną float na temperatury i RAZ odczytaną temperaturę wyślij na LCD, serial i do SLAVE.
Poza tym włącz tryb asynchroniczny dla DS, sam dbaj o to by zlecać pomiar i go odczytywać w odpowiednim czasie, np. na przykład zleć pomiar w SETUP, poczekaj 1s, wejdź do loop i tu już wywal wszystkie DELAY. Zacznij mierzyć sekundy w millis, gdy jest nowa sekunda odczytaj temperatury i zleć nowy pomiar.
W kolejnej sekundzie odczytasz pomiar zlecony w poprzedniej. Cały loop skraca się do czasu transmisji z czujnikami, czyli około 20ms na odczyt x3 i raz 20ms na zlecenie pomiarów do wszystkich. Ale to jest tylko ten 1 loop gdzie jest nowa sekunda, wszystkie inne loop w tej sekundzie wykonają się w kilka us. Chcesz zauważyć wciśnięcie jakiegoś przycisku, zmianę stanu pinu - voila, teraz możesz.
Oczywiście transmisji na UART/LCD czy RS485 też nie robisz w każdym loop, tylko co wybrany okres czasu, np. gdy jest nowy odczyt temp. Niestety to będzie ten sam loop, ale możesz tak ułożyć kod, że będzie kolejny, czyli najpierw się sprawdza czy wysłać temp bo jest nowy odczyt, a dopiero potem sprawdzasz czy odczytać.
Potem można to 80ms w jednym loop dla DS również rozbić tak, by nie robić w jednym loop wszystkich czterech transmisji. Można ustawić jakąś zmienną, że jest nowa sekunda, zrobić blok programu gdzie to odblokowuje wejście, przy pierwszym odczytać 1 czujnik, przy drugim trzeci, itd, zlecić pomiar, potem wysłać na LCD, potem wysłać na UART, potem wysłać na RS, zablokować wejście w ten blok ustawiając "jakąś zmienną=0" i znowu wejdziesz tu gdy nowa sekunda ustawi ją na 1.
Witam.
Jesli chodzi o DS18S20 to zastosowalem gotowa biblioteke o nazwie NonBlockingDallas, ktora dziala w tle i nie blokuje funkcji LOOP (super sprawa, polecam). Natomiast caly czas walcze z RS485. Master wysyla floata z przecinkami w stringu np 21,44ºC a slave odbiera to bez przecinkow. Prosze wiec o jakis maly przyklad sketcha dla mastera i sleve. Z gory dziekuje za pomoc.
Nic nie wysyła, a przynajmniej niewiele. Write zapisuje 1 bajt, float ma 4. To w ogóle Ci działa? W bibliotece UART do Arduino nie chce zrobić write floata, bo widzi, że to lipa. Dostajesz mimo wszystko sensowne wyniki, niby ucięte do liczby całkowitej, ale tylko dlatego że mieszczą się w pierwszym bajcie, jakbyś wysłał 2343.234 to już zobaczysz głupoty.
W zwykłej bibliotece wystarczy napisać sensors.setWaitForConversion(0); to jest ten tryb asynchroniczny wspomniany powyżej, tylko potem trzeba samemu pilnować kiedy zlecasz pomiar i kiedy go odczytujesz. Ale ponieważ zarządzanie czasem i mierzenie jego upływu to podstawowa umiejętność niezbędna do napisania jakiejkolwiek użytecznej aplikacji, to nie jest to problem, czy nadmiarowa praca, to i tak trzeba ogarnąć.
Wtedy masz to samo co w tej nonBlockingDallas, ona też jest blocking, bo nie da się inaczej, zmiany bitów są za szybkie by to miało sens, jak nie zablokuje na te 15ms loop to zablokuje przerwaniami wszystko. Znaczy da się inaczej, jak jest wolny UART to nim można przełączać się między dwoma prędkościami i nadawać bity 1-WIRE jako bajty UART. Wtedy zestaw danych do wysłania trafia do bufora i uC jest zajmowany na us by uzupełnić bufor kolejnym bajtem. Na pewno jest biblioteka w C ogólnie dostępna, ale w Arduino na razie nie widziałem.
Zacznij od napisu.
Masz przykład tu na forum, poszukaj Serial ewent. Zresztą, masz tu: https://forum.arduinopolska.pl/watek-uar...ino?page=2
Przejrzyj jak to wygląda, generalnie w punkcie RS485.available() (czy tam serial, zasada taka sama) warunek jest spełniony, gdy w buforze czeka do odczytania BAJT, jeden, no przynajmniej jeden.
Poza tym ile trwa taki blok programu, to są co najwyżej us. wysłanie/odbiór jednego bitu to 1/9600, więc program może 1000x zajrzeć tu i nic nie widzieć, choć transmisja trwa w najlepsze, bit po bicie, bajt za bajtem, a Ty sobie wszedłeś, odebrałeś ten jeden gotowy bajt i go dawno wysłałeś dalej, nie czekając na resztę.
Nawet jeśli sobie ustawisz to 115200, co dla soft serialu nie jest raczej dobrym pomysłem, to i tak będziesz miał kilkanaście pustych odczytów między bajtami, a jak program się rozrośnie to z softserialem nie zdążysz i się to posypie.
Transmisję można zrealizować wysyłając NAPIS, wtedy odczytujesz ciąg bajtów/liter, aż odczytasz cały napis o czym świadczy znak '\n', potem z niego konwertujesz na floata.
Zaraz obok były posty gdy kolega tu na forum walczył z floatami z falownika solarnego. On z kolei odbierał napisy z przecinkami, przecinek sobie zamieniał na kropki i konwertował gotową funkcją na floata. Skorzytaj.
Więc na początek napis, a jak bardzo chcesz te floaty, co ma sens, transmisja napisów to dużo więcej bajtów dla przesłania tej samej informacji, to musisz trochę dłużej powalczyć.
Nie robiłem tego, ale jak bym miał to wymyślić to czas jest tu kluczowy.
Na początek upewnij się że wysyłasz 4 bajty, tyle ma float, 4 bajty musisz też odebrać. Jak wysłać 4 bajty z floata writem, np. można znaleźć w Internetach by zrobić unie floata i tablicy 4 bajtów. Można zrobić wskaźnik na byte, wpisać mu wartość adresu zmiennej float, potem wysłać na serial write *byte, *(byte+1), +2 i +3. Jak z literkami one przylatują co jakiś czas. Trzeba wyznaczyć sobie jakiś okres czasu, gdzie nic nie leci, a jak przyleci, to wiesz, że to jest pierwszy bajt. Ale jak sprawdzić, czy odebrany ciąg bajtów ma jakiś sens bez CRC, przy napisie masz chociaż znak końca linii.
Reset pomiaru czasu i czekanie na kolejny bajt, jak nie przyleci w czasie spodziewanym dla prędkości transmisji to masz problem do obsłużenia. Jak przyleci 5 bajtów to kolejny, problem.
W sumie to możesz być zajęty innymi rzeczami, więc pomiar czasu jest średnim pomysłem na dłuższą drogę, biblioteka w tle może napełnić bufor programowy kilkoma bajtami, gdy Ty będziesz migał ledem.
Pomysł zadziała w takim programie jak pokazujesz, ale generalnie bez sensu.
No to oznaczenie początku ramki, wysyłasz w pierwszych bajtach napis "kopytko". Jak odbierzesz taki to wiesz, że to Twoja ramka, potem 4 bajty floata i 2 CRC dla kontroli, wszystko upchane w strukturę.
Nie ma sensu się z tym bawić na tym etapie, różnica między tekstem, a danymi binarnymi nie ma znaczenia przy przesyłaniu do kilkudziesięciu znaków.
Na razie nie ma sensu tracić czasu na napis "kopytko".