wtorek, 6 grudnia 2016

Komunikacja radiowa FS1000A / MX-RM-5V 433MHz - początki

Do jednego z planowanych projektów potrzebuję zrealizować jednokierunkową bezprzewodową wymianę danych między dwoma urządzeniami. Zaopatrzyłem się w najtańsze chyba na ten czas dostępne moduły do komunikacji radiowej mianowicie nadajnik FS1000A i odbiornik MX-RM-5V.
Jedyne dane jakie dostałem od sprzedawcy zamieszczam poniżej:

Parametry techniczne nadajnika: (FS1000A)
Zasięg ok. 200m
Napięcie zasilania 3,5V - 12V
Modulacja AM ASK/OOK
Moc 10mW
Transfer ~ 10kB/s
Pobór prądu: 40mA(12V) oraz 9mA(3,5V)
Wymiary: 30mm x 14mm x 9mm

Parametry techniczne odbiornika: (MX-RM-5V)
Napięcie zasilania 5V
Czułość 100dB
Pobór prądu ok. 5.5mA
Wymiary: 19mm x 19mm x 7mm

A same moduły, już zainstalowane na płytkach stykowych, wyglądają jak niżej:




Rozpocząłem przeglądać internet w celu zdobycia informacji jak zacząć pracę z tymi modułami. Materiałów jest sporo, niestety niemalże wszystkie opisują pracę na Arduino i to z gotowymi bibliotekami (np. VirtualWire). Udało mi się jednak znaleźć przydatne informacje i uruchomić moduły. 

Na początku warto wspomnieć, że to najtańsze (zapewne Chińskie) moduły komunikacyjne. Ja mam akurat na częstotliwość 433 MHz. Nie posiadają one zaimplementowanego żadnego protokołu komunikacji (USART, SPI itp.). Zatem taki prosty protokół, który chociaż trochę uodporni naszą transmisję na zakłócenia, musimy napisać sami. Do pierwszego uruchomienia zastosuję kodowanie Manchester (jak radzi wiele źródeł). Zasada działania samego kodowania nie jest skomplikowana i bez problemu można ją znaleźć w literaturze i internecie. Najprościej mówiąc: każdy nadawany bit jest dzielony na połowę. W czasie trwania pierwszej połowy nadawany jest faktyczny stan bitu, w czasie drugiej połowy nadawany jest stan przeciwny. Przykładowo, chcemy nadać 3 bity: 0b011 z częstotliwością 1Hz. W normalnej transmisji (bez kodowania Manchester) nadalibyśmy stan HIGH przez 1s, następnie znowu stan HIGH i na końcu stan LOW przez 1s. Jeśli zastosujemy kodowanie Manchester transmisja będzie wyglądała następująco: nadajemy stan HIGH przez 0,5s następnie zmieniamy stan na przeciwny na 0,5s - czyli nadajemy stan LOW. W tym miejscu kończy się przesył najmniej znaczącego bitu. Następnie znów nadajemy stan HIGH przez 0,5s i przez kolejne 0,5s zmieniamy stan na przeciwny - LOW. Tutaj kończy się nadanie drugiego bitu. Ostatni bit do przesłania to zero, zatem przez 0,5s ustawiamy stan LOW i zmieniamy go na przeciwny ustawiając stan HIGH. Tyle o transmisji kodowanej Manchesterem. Poniżej znajduje się kod odbiornika i nadajnika.


Kod nadajnika:
#include <avr\io.h>
#include <stdio.h>
#include <util/delay.h>

#define F_CPU 12000000UL


//do testów LED
#define LED1PIN (1<<PD5)
#define LED1PORT PORTD
#define LED1DDR DDRD
#define LED1setHIGH LED1PORT |= LED1PIN
#define LED1setLOW LED1PORT &= ~LED1PIN

//pin nadawania
#define TXPIN (1<<PB4)
#define TXPORT PORTB
#define TXDDR DDRB
#define TXsetHIGH TXPORT |= TXPIN
#define TXsetLOW TXPORT &= ~TXPIN
#define TXtoggle TXPORT ^= TXPIN



//wysylanie 8bitow (wystawienie stanu wysokiego jesli byl stan wysoki
void TxSendByte(uint8_t sendingValue)
{
	//zapalamy led
	LED1setHIGH;


	//wybudzenie odbiornika (wyslanie 20 razy naprzemiennie 1 i 0
	for(int i = 0;i<20;i++)
	{
		TXsetHIGH;
		_delay_us(500);
		TXsetLOW;
		_delay_us(500);
	}

	//synchronizacka (3ms stan wysoki, 500us niski)
	TXsetHIGH;
	_delay_us(3000);
	TXsetLOW;
	_delay_us(500);

	//nadawanie 8 bitów - wartosc przekazana do funkcji
	for(int i = 0;i<8;i++)
	{
		if( (sendingValue>>i) & 0b00000001)
			TXsetHIGH;
		else
			TXsetLOW;
		_delay_us(500);
		TXtoggle;
		_delay_us(500);
	}

	//po zakonczeniu nadawania ustawiamy 0
	TXsetLOW;

	//gasimy led
	LED1setLOW;
}

int main(void)
{
	//init ledów
	LED1DDR |= LED1PIN;	//pin LED1 jako wyjscie
	LED1setHIGH;

	//init nadawania
	TXDDR|= TXPIN;
	TXsetLOW;

	//pętla główna
	while(1)
	{
		//wysyłanie na przemian (co 1s) cyfry 123 i 13
		TxSendByte(123);
		_delay_ms(1000);
		TxSendByte(13);
		_delay_ms(1000);

	}
}

Kod odbiornika:
#include <avr\io.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define F_CPU 16000000UL


//do testów LED
#define LED1 (1<<PC0)
#define LED1port PORTC
#define LED1ddr DDRC
#define LED1setHIGH LED1port |= LED1
#define LED1setLOW LED1port &= ~LED1

//pin odbierania
#define RX (1<<PD2)
#define RXport PORTD
#define RXddr DDRD
#define RXpin PIND

uint8_t isThatNoise = 1;
volatile uint8_t recievedByte=0;


ISR(INT0_vect)
{
	//sprawdzenie czy jest 1
	for(int i=0;i<100;i++)
	{
		_delay_us(20);
		isThatNoise = 0;
		if(!(RXpin & RX))
		{
			isThatNoise = 1;
			break;
		}
	}

	//jesli to nie zakłócenia to wchodzi do ifa
	if(!isThatNoise)
	{
		EIMSK &= ~(1<<INT0);		//wyłączenie przerwania od INT0
		while(1)
		{
			//czekanie jak minie 3ms stanu wysokiego, jak pojawi się stan niski to przeczekuje stan niski + zapas i zaczyna odczytywac
			if(!(RXpin & RX))
			{
				_delay_us(750);
				//petla odczytująca po kolei każdy bit
				for(int i=0;i<8;i++)
				{
					//sprawdzenie co zostało przesłane
					if(RXpin & RX)	//jesli przesłana jest 1
						recievedByte |= (1<<i);
					else			//jesli przeslane jest 0
						recievedByte &= ~(1<<i);
					_delay_us(1000);	//przed przejsciem do kolejnej iteracji odczekanie az nadajnik nada kolejny bit
				}
				break;	//wyjscie z nieskonczonej petli while(1)
			}
		}
	}


	//zezwolenie na przerwania
	EIMSK |= (1<<INT0);		//zezwolenie na przerwania od INT0
}

void usartInitialize(void)
{

#define BAUD 9600
#include <util/setbaud.h>

	UBRR0H = UBRRH_VALUE;
	UBRR0L = UBRRL_VALUE;
#if USE_2X		//jesli nie da sie uzykac BAUD bez trybu zwiekszenia predkosci to go zwieksza
	UCSR0A |= (1<<U2X0);
#else
	UCSR0A &= ~(1<<U2X0);
#endif

	UCSR0B = (1<<TXEN0);				//włącza nadajnik
	UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);	//8bitow danych, 1bit stopu, brak bitu parzystosci

}

void usartSendByte(uint8_t sendingByte)
{
	while( !( UCSR0A & (1<<UDRE0) ) );	//czekanie jak bufor nadawania bedzie pusty
	UDR0 = sendingByte;					//przypisanie danych do bufora nadawania (wysłanie)

}

int main(void)
{
	//wlacz przerwania globalne
	sei();

	//init ledow
	LED1ddr |= LED1;	//wyjscie
	LED1setHIGH;

	//init pinu RX
	RXddr &= ~RX;	//wejscie
	RXport &= ~RX;	//bez pull-up (stosuje zewnętrzny rezystor pulldown 270 omów)

	//init przerwań od INT0 (PD2)
	EICRA |= (1<<ISC01) | (1<<ISC00);	//przerwanie od zbocza narastającego
	EIMSK |= (1<<INT0);					//zezwolenie na przerwania od INT0

	//init uart
	usartInitialize();


	//petla glowna
	while(1)
	{
		if(recievedByte != 0)
		{
			usartSendByte(recievedByte);
			if(recievedByte == 13)
			{
				recievedByte = 0;
				LED1setLOW;
				_delay_ms(250);
				LED1setHIGH;
			}
			else
				recievedByte = 0;
		}
	}
}




Przeprowadziłem testy. Warto wspomnieć, że jest tutaj zastosowane jedynie kodowanie Manchester z synchronizacją na początku. Nie ma żadnej kontroli błędów w postaci sumy kontrolnej (bit parzystości/nieparzystości lub CRC). Przesyłałem co 1s na przemian liczbę 8-bitową. Najpierw 123, następnie 13. Sprawdzałem, czy odbiornik poprawnie je odbierze. Początkowo, uruchomiłem moduły bez wlutowania dodatkowej anteny. Niestety zasięg to 1 cm, czyli żaden. Zgodnie z zaleceniami znalezionymi w internecie, do poprawy zasięgu wystarczy antena w postaci kawałka drutu. Długość kawałka jest określana jako 1/4 długości fali elektromagnetycznej, co dla 433MHz wynosi ok 17,3 cm. Po zastosowaniu takiej anteny moduły mogłem już odsunąć od siebie na większą odległość. Jednak - nadal nie była to odległość mnie zadowalająca.


Do testów złożyłem dwa proste układy. Układem nadajnika steruje mikrokontroler AVR ATTiny2313. Odbiornik podłączony jest z mikrokontrolerem ATMega328P. Do testów, podłączyłem go również przez konwerter USART-USB do komputera. Dzięki temu wszystko co przyjdzie do odbiornika wysyłam przez USART i odczytuję w terminalu. Poniżej znajdują się screeny z terminala dla czterech odległości: 20 cm, 100 cm, 200 cm oraz 300 cm. Na czerwono zaznaczyłem błędnie odebrane dane. Przy większych odległościach możemy zaobserwować, że nie wszystkie transmitowane liczby są w ogóle odbierane przez odbiornik. Dzięki temu, że przesyłam naprzemiennie dwie różne liczby, nawet w przypadku kiedy przesłana będzie błędna liczba wiemy jaka powinna być przesłana kolejna.




Dane o liczbie błędnie odebranych danych zebrałem poniżej. Dla większych odległości wyniki są mniej miarodajne, ponieważ niestety nie mam wiedzy o liczbie wysłanych danych. Zatem mogę jedynie określić ile z otrzymanych danych było poprawne, a nie ile z nadanych danych było poprawne. No cóż, uwzględnię to przy kolejnych testach.

odległość % poprawnych danych
20 cm 100%
100 cm 92%
200 cm 59%
300 cm 43%

Tak jak wspomniałem, gdyby np. do odległości 300 cm doliczyć dane, które w ogóle nie zostały odebrane, to % poprawnych danych zapewne zmniejszyłby się ok. dwukrotnie. 

Brak komentarzy:

Prześlij komentarz