Skocz do zawartości
Nocti

Bluethooth przesyła dziwne znaki.

Pomocna odpowiedź

Napisano (edytowany)

Witam.

Przepraszam za niejasny tytuł.

Zrobiłem program (używając Arduino Uno i modułu bluethooth HC-06) który ma wyświetlać w konsoli liczby, które prześle przez Bluethooth.

Problem polega na tym, że czasem zamiast liczb które chce przesłać pojawiają się dziwne znaki.

Liczby muszą być przechowywane w Stringu.

Jak to naprawić?

#include <SoftwareSerial.h>
SoftwareSerial BT(0, 1);

void setup() {
  BT.begin(9600);
}
String liczba[15];
char a[6];

void loop() {
  if (BT.available()) {
    liczba[1]=BT.readStringUntil('\n');
    BT.println(liczba[1]);
    delay(500);
  }
}

prtscr.thumb.png.ecb709c608dbe74438e8c55e6e202508.png

Edytowano przez Nocti

Udostępnij ten post


Link to post
Share on other sites

@Nocti wygląda na to, że transmisja nie jest doskonała, czasami gubione są jakieś informacje, a to sprawia, że pojawiają się krzaki. Można to rozwiązać na dwa sposoby.

Po pierwsze, można wprowadzić bardziej rozbudowaną metodę transmisji (ramki danych, sumy kontrolne itd). Taka metoda byłaby najskuteczniejsza, ale będzie pracochłonna. Jeśli jest to tylko jednorazowy, prosty projekt to możesz trochę "obejść" problem. Sprawdzaj co zawiera odebrany ciąg znaków. Jeśli jest tam coś innego oprócz cyfr i znaku kropki to ignoruj odebrane dane i czekaj na następną transmisję. Nie będzie to zbyt eleganckie rozwiązanie, ale zadziała 😉 Metoda ta ma jednak dużą wadę - nie będziesz wstanie sprawdzić czy odebrane dane są poprawne tj. czy np. zamiast 356,90 nie odebrałeś przypadkiem 999,9 (takie zakłócenie w transmisji też jest teoretycznie możliwe). 

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Przepraszam że nie odpowiadałem tak długo.

Wpadłem na pomysł aby to rozwiązać.  Polega on na tym aby zapisać sygnał do dwóch tablic, a później sprawdzać czy przesłane znaki są jednakowe.

Czy taki pomysł mógł by wypalić?

Udostępnij ten post


Link to post
Share on other sites

@Nocti czyli w praktyce chcesz przesyłać dwa razy to samo i porównywać czy otrzymałeś te same wartości? Można i tak - nie będzie to zbyt eleganckie, ale zadziała 🙂

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Tak pisząc o samych podstawach

SoftwareSerial BT(0, 1); // źle 

tak niemożna tego zapisać bo wartości w nawiasach to piny na, których ma pracować SoftwareSerial. Na pinie 0 i 1 w arduino masz Serial sprzętowy. 

SoftwareSerial BT(2, 3); //dobrze

to jest poprawny zapis i oznacza: podłącz PIN TX od BT do PIN'u nr 2 Arduino ,  PIN RX od BT do PIN'u nr 3 Arduino

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

tu jest dobrze ale zadeklarowałeś tylko prędkość przesyłu dla SoftwareSerial a gdzie hardware Serial? Przecież chcesz korzystać z dwóch Seriali.

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

 teraz masz zadeklarowane dwa Seriale ( Software i hardware). Prędkości przesyłu dla obydwu Seriali muszą być jednakowe inaczej będziesz miał krzaczki.  W uno Serial pracuje na 9600 i to jest pewne. Pozostaje kwestia z jaką prędkością pracuje BT (bo nie musi to być 9600). Trzeba to sprawdzić a do ewentualnej zmiany prędkości używa się komend AT. Aby użyć komęd AT dla BT przeba ustawić BT w tryb AT i użyć komendy zapisanej w karcie katalogowej dla HC-06

void loop() {
if (BT.available())
  Serial.write(BT.read());

można by to tak przetłumaczyć: Jeśli są dane w porcie BT (SoftwareSerial) to prześlij je na Sprzętowy Serial.

if (Serial.available())
  BT.write(Serial.read());
}

a tutaj w drugą stronę: Jeśli są dane w Serialu (sprzętowym) to prześlij je na BT SoftwareSerial.

To by było tyle jeśli chodzi o samą komunikację w dwie strony od strony programowej. Zostaje jeszcze strona sprzętowa czyli podłączenie BT do Arduino. Najlepiej jest to zrobić przez konwerter poziomów logicznych (chodzi o piny TX i RX). 

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites
6 godzin temu, slon napisał:

teraz masz zadeklarowane dwa Seriale ( Software i hardware). Prędkości przesyłu dla obydwu Seriali muszą być jednakowe inaczej będziesz miał krzaczki.

Prawie wszystko się zgadza, bardzo trafnie zauważyłeś, że zadeklarował tego soft serial na pinach sprzętowego UART (HS). Natomiast prędkości muszą być takie same w tych serialach, które są połączone kabelkami, czyli tu soft serial (SS) i serial ustawiony w BT, ten od strony PC może być inny i z tym UNO i z drugiej strony  (np. jeśli to jest PC z "serialportem" do BT czy inny BT z innym UNO), ten od strony BT-BT też może być inny, moduły sobie dopasują same, w buforach mieszczą bardzo dużo jak na możliwości UNO. No i nie używa HS w tym programie, nie ma takiego obowiązku - on sobie zrobił ping-ponga przez BT. I teoretycznie można tak zrobić z SS na pinach 0 i 1 gdy się nie używa HS. Ale lepiej używać HS tam gdzie dane trzeba odbierać, są przerwania i bufory, mniejsze szanse, że coś umknie. Skoro nie używa HS UNO do PC to mógł to zrobić, najlepiej odłączając kabelek USB by nie zasilać konwertera USB-UART (pewnie on też zakłóca SS) płytki UNO.  Na uno z mostkiem na atmega można też zrobić prosty myk, wprowadzić mostek w stan resetu (jego pin jest wyprowadzony), tylko zniknie z PC.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

  Przepraszam że nie było mnie tak długo.

A więc usunąłem całkowicie SS  i krzaki zniknęły. Połączyłem ten program z innym :

Program działa, ale mam jeszcze jedno zadanie. Sprawić aby ww. programem można było sterować również za pomocą aplikacji na telefon. (Czyli zastąpić fizyczne przyciski tymi w telefonie). Ale kiedy wysyłam jakąś informacje zapisuje się ona w Stringu. Kiedy dodaje if pojawiają się krzaki i ubytki w danych odebranych z bluetooth. 

Może ktoś wie jak to zrobić? 

Kod programu:

#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <EasyButton.h>

#define odstepKropka 5000
#define czasNacisniecia 1000
#define czasNacisniecia000 1000 //Definicje czasów
#define odstep 1000
#define czasGoraDol 200
#define oko 4000
#define infodioda 200

#define BACKLIGHT_PIN 3

LiquidCrystal_I2C  lcd(0x3F, 2, 1, 0, 4, 5, 6, 7);

EasyButton zatw(A0);
EasyButton wdol(A2);  //Zmienne Easy Button
EasyButton wgore(A1);

String a[15]; //Stringi w których zapisane są liczby
char Char[6]; //Char do którego kopiowane są znaki ze stringów
int b = -1;      //Zmienna przechowująca numer pozycji liczby (stringa)
short c = 0;

void setup() {
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  Serial.begin(9600);
  zatw.begin();
  wdol.begin();
  wgore.begin();

  zatw.onPressed(ztawierdz);
  wdol.onPressed(dol);        //Przypisywanie funkcji do zmiennych Easy Button
  wgore.onPressed(gora);

  lcd.begin (16, 2);
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);

  lcd.setCursor(0, 0);
  lcd.print("Aktualny: ");
  lcd.print(a[b]);
  lcd.setCursor(0, 1);
  lcd.print("Nastepny: ");
  lcd.print(a[b + 1]);
}

void ztawierdz() {  //Funkcja odpowiadająca za przycisk "Zatwierdź"
  if (b < 15 && b > -1) {

    char Char[6] = {' ', ' ', ' ', ' ', ' ', ' '};
    Serial.println(b);
    Serial.println(a[b]);
    a[b].toCharArray(Char, 6);

    b++;

    digitalWrite(13, HIGH);
    delay(infodioda);
    digitalWrite(13, LOW);
    delay(infodioda);
    digitalWrite(13, HIGH);
    delay(infodioda);
    digitalWrite(13, LOW);
    delay(infodioda);
    digitalWrite(13, HIGH);
    delay(infodioda);
    digitalWrite(13, LOW);
    delay(infodioda);
    digitalWrite(13, HIGH);
    delay(infodioda);
    digitalWrite(13, LOW);

    digitalWrite(2, HIGH);
    delay(czasNacisniecia);
    digitalWrite(2, LOW);

    for (int i = 0; i <= 9; i++) {

      if (Char[i] == '0') {       //Sprawdzanie liczb
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(3, HIGH);
        delay(czasNacisniecia);
        digitalWrite(3, LOW);
      }
      else if (Char[i] == '1') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(4, HIGH);
        delay(czasNacisniecia);
        digitalWrite(4, LOW);
      }
      else if (Char[i] == '2') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(5, HIGH);
        delay(czasNacisniecia);
        digitalWrite(5, LOW);
      }
      else if (Char[i] == '3') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(6, HIGH);
        delay(czasNacisniecia);
        digitalWrite(6, LOW);
      }
      else if (Char[i] == '4') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(7, HIGH);
        delay(czasNacisniecia);
        digitalWrite(7, LOW);
      }
      else if (Char[i] == '5') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(8, HIGH);
        delay(czasNacisniecia);
        digitalWrite(8, LOW);
      }
      else if (Char[i] == '6') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(9, HIGH);
        delay(czasNacisniecia);
        digitalWrite(9, LOW);
      }
      else if (Char[i] == '7') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(10, HIGH);
        delay(czasNacisniecia);
        digitalWrite(10, LOW);
      }
      else if (Char[i] == '8') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(11, HIGH);
        delay(czasNacisniecia);
        digitalWrite(11, LOW);
      }
      else if (Char[i] == '9') {
        Serial.println(Char[i]);
        delay(odstep);
        digitalWrite(12, HIGH);
        delay(czasNacisniecia);
        digitalWrite(12, LOW);
      }
      else if (Char[i] == '.') {
        Serial.println(Char[i]);
        delay(odstepKropka);
        digitalWrite(2, HIGH);
        delay(czasNacisniecia);
        digitalWrite(2, LOW);
      }
    }
    delay(oko);
    lcd.setCursor(0, 0);
    lcd.print("Aktualny: ");
    lcd.print(a[b]);
    lcd.setCursor(0, 1);
    lcd.print("Nastepny: ");
    lcd.print(a[b + 1]);
  }
}

void dol() { //Funkcja odpowiadająca za przycisk "w dół"
  if (b >= 1) {
    lcd.clear();
    Serial.println("w dol!!!");
    b--;
    lcd.setCursor(0, 0);
    lcd.print("Aktualny: ");
    lcd.print(a[b]);
    lcd.setCursor(0, 1);
    lcd.print("Nastepny: ");
    lcd.print(a[b + 1]);

    digitalWrite(13, HIGH);
    delay(czasGoraDol);
    digitalWrite(13, LOW);
  }
}

void gora() { //Funkcja odpowiadająca za przycisk "w górę"
  if (b < 13) {
    lcd.clear();
    Serial.println("w gore!!!");
    b++;
    lcd.setCursor(0, 0);
    lcd.print("Aktualny: ");
    lcd.print(a[b]);
    lcd.setCursor(0, 1);
    lcd.print("Nastepny: ");
    lcd.print(a[b + 1]);

    digitalWrite(13, HIGH);
    delay(czasGoraDol);
    digitalWrite(13, LOW);
  }
}


void loop() {
  while (Serial.available()) {
    b=0;
    //BT.write(Serial.read());
    a[c] = Serial.readStringUntil('\n');
    Serial.println(a[c]);
    //delay();
    c++;
    lcd.setCursor(0, 0);
    lcd.print("Aktualny: ");
    lcd.print(a[b]);
    lcd.setCursor(0, 1);
    lcd.print("Nastepny: ");
    lcd.print(a[b + 1]);
  }
  if (c == 15) c = 0;

  zatw.read();
  wdol.read();  //Aktywowanie funkcji zatwierdz, dol, gora
  wgore.read();

 

Udostępnij ten post


Link to post
Share on other sites

Piszesz program wielozadaniowy, więc używanie delay jest nieakceptowalne. Wróć do kursu na tej stronie, ostatnia lekcja o millis jest rozwiązaniem. A o wyłuskiwaniu danych z przylatującego ciągu tekstu wrzuciłem posta tu: 

Tym sposobem możesz przesłać nie tylko stan led 0/1 ale również wypełnienie PWM 0-255 czy wartość dowolnej zmiennej. Ale bez delay.

Przykład z prostym zarządzaniem czasem:

uint32_t czasTeraz,czasPoprzedni,tik=10; //tik musi byc mniejszy niz 1000 i dzilic 1000ms na rowne czesci
uint8_t nTik,sekundy,minuty,godziny,dni; //liczniki tikow, sekund, itd.
bool fnTik,fsekundy,fminuty,fgodziny,fdni; //flagi zdarzen nowy tik, nowa sekunda,minuta, godzina, dzien
char napis[10]; 
void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
 czas();


if(fsekundy) {
sprintf(napis,"%03d:%02d:%02d",godziny,minuty,sekundy);
Serial.println(napis); 


}

if(fnTik&&! (nTik%20)) digitalWrite(LED_BUILTIN,! digitalRead(LED_BUILTIN));//gdy nowy tik i co 20 tikow
}


void czas()
{
  czasTeraz=millis();
 fnTik=fsekundy=fminuty=fgodziny=fdni=0;
if((uint32_t)(czasTeraz-czasPoprzedni)>=tik) //tan napisany warunek jest odporny na "klątwe 50 dni millis()"
{
  czasPoprzedni=czasTeraz;
  fnTik=1;
  nTik++;
  if(nTik>=(1000/tik))
  {
    nTik=0;
    sekundy++;
    fsekundy=1;
     if (sekundy>=60)
    {
      sekundy=0;
      minuty++;
      fminuty=1;
      if (minuty>=60)
      {
        minuty=0;
        godziny++;
        fgodziny=1;
        if (godziny>=24)
        {
          godziny=0;
          fdni=1;
          dni++;
    
        }
      }
    }
  }
}
}

Pobaw się i zauważ, że rzeczy odbywają się co określony czas i nie ma nigdzie blokowania programu. Tak samo można wykonać 'COŚ" gdy jest nowa minuta, ale również tylko co piąta. W międzyczasie program może odbierać dane z UART, obrabiać je i mieć przygotowane do kolejnego wykorzystania.

Udostępnij ten post


Link to post
Share on other sites

W delay(); nie będę wnikał ale w samym kodzie jest sporo niezrozumiałych rzeczy. 

#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>

w jakim celu używasz tych trzech bibliotek? Patrząc po ilości pinów jakie deklarujesz jako wyjście to zapewne używasz wyświetlacza z konwerterem. Czyli wystarczy

#include <LiquidCrystal_I2C.h>

Pętle for raz zastosowałeś

for (int i = 0; i <= 9; i++)

więc skoro raz jest zastosowana to czemu nie zastosujesz jej do deklaracji pinów jako wyjście?

for (int nrPin=2; nrPin<=13; nrPin++)
	{
       pinMode(nrPin, Output);
	}	

jak to by było nr arduino mega i tych nrPin było by np: 25 to wystarczyło by to zmienić w pętli  a nie dopisywać kolejne linijki kodu.

digitalWrite(13, HIGH);
    delay(infodioda);

tutaj znowu w/w kod powtarza się osiem razy

for (int i=0; i<8; i++)
{
digitalWrite(13, HIGH);
    delay(infodioda);
}

to lepiej będzie tak. Wracając do pętli którą zastosowałeś

for (int i = 0; i <= 9; i++) {

      if (Char[i] == '0')

ta pętla wykona się 10 razy. I od momentu kiedy i=6 twoja tablica się rozlatuje bo masz zadeklarowane

char Char[6];

czyli 6 elementów w tablicy liczonych od 0 to ostatni ma nr 5. Jeśli zadeklarował być np: 16 elementów w tej tablicy to nic się nie dzieje ale nie może być mniej niż ilość cykli w pętli. Kolejna sprawa jest tak , że jeśli używasz

lcd.clear();

to po tej metodzie wyświetlacz ustawia ci się automatycznie na pozycji (0,0) więc nie musisz stosować

lcd.setCursor(0, 0);

tą metodę stosuj jeśli nie czyścisz całego wyświetlacza a jesteś już w drugim wierszu i chcesz wrócić do pierwszego. To by było tak ogólne nie wnikając w delay'e.

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Napisz odpowiedź...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...