Using FS1000A/XY-FST RF Radio module with AVRs

Wireless data transfer can be useful for a variety of projects. I stumbled upon a very low cost emitter+receiver modules pair, and planned to use it for some time now. There are a few variants available on the market, but they do quite the same thing, just the operating frequency might be different (315MHz / 418MHz / 433MHz/ 915MHz):
fa1000a_1_avr fa1000a_2_avr fa1000a_3_avr fa1000a_4_avr
The emitter usually has 3 pins (VCC->5V, GND, DATA), with a few variants that expose the fourth pin for connecting an external antenna to increase the range. The input voltage interval is 3.5-12V (the higher the voltage, the longer the range, usually between 20-200m).Size 19x19mm, operating mode AM, transfer rate 2400bps.
The receiver also has three pins (VCC, GND, DATA) and it operates at 5V. It uses only 4mA. The sensitivity is -105DB, and the module size 30 * 14 * 7mm.

How to use the FS1000A / RF Modules

Setting the emitter's DATA pin to HIGH will result in a radio transmission that will set the receiver's DATA PIN to high. By doing so, it is obvious that a serial connection can be implemented, once a common baud rate has been defined. We'll push bits to the transmitter's DATA Pin, assuming we are in the supported baud rate interval, and the receiver will get the data we've sent over the radio (plus errors). Here is the signal on transmitter's pin (CH1/yellow) and the signal collected at the receiver (CH2 / cyan):
rf_module_atmega

This shows that all we have to do is to hook the emitter to the AVR's UART TX port, and the receiver to another AVR's UART RX Port. We write some code to initialize the UART ports using the same baud rate, and we're ready to exchange data. Here is a sample setup , with receiver connected to an Atmega8 board, showing data on a 2x16 LCD, and the emitter's board with an Atmega128 and a Nokia 5110 LCD. The numbers on the two LCD's match, as what this setup does is to increment an integer every second, and send it over the radio. So what we see on the 5110 LCD, we also see as V: number on the receiver's screen.
rf_module_atmega_2
The problem here is we are getting lots of errors, and the emitter module will not know if the transmission was successful or not (because it is unidirectional communication). The simple code implementation is:
the emitter code

  1.  
  2. uart0.Init(0,1200, false); // init uart0 for 1200bps baudrate, no RX callback
  3. while(1) {
  4. i = (i+1) % 256;
  5. led1.Toggle(); led2.Toggle();
  6. uart0.Send(i);
  7. lcd.goto_xy(0,0);
  8. lcd.send_format_string("%d ", i);
  9. _delay_ms(1000);
  10. }
  11.  

the receiver code

  1.  
  2. uint8_t v = 0;
  3. ISR(USART_RXC_vect){
  4. volatile char c = UDR; //Read the value out of the UART buffer
  5. v = c;
  6. }
  7. ...
  8. main() {
  9. uart0.Init(0, 1200, true); // init uart0 for 1200bps, with RX callback
  10. int i = 0;
  11. while(1) {
  12. i++;
  13. lcd.lcd_cursor_home();
  14. lcd.lcd_string_format("idx:%d v:%d \n2nd line ", i, v);
  15. _delay_ms(1000);
  16. }
  17.  

This works fine, but from time to time, the receiver picks up random bits, leading to wrong data. There's also the problem of multiple module pairs working in close proximity causing interference, or the problem of missing some of the data (the receiver doesn't get all the bytes transmitted over the RF link).
Like in similar serial data communications, we can assign an address to differentiate between multiple devices, and compute a checksum value to rule out some of the errors (building data packets). This is a good solution, but we still have the problem of loosing some of the packets, and because the communication is unidirectional we can't inform the emitter to retransmit. Unfortunately this is as good as it gets, so keep this in mind when using these modules, as they are not intended for precise data transmissions.
the emitter code

  1.  
  2. UART uart0;
  3. //define receive parameters
  4. #define RADDR 0x10 // common address parameter
  5. void Send_Packet(uint8_t addr, uint8_t cmd) {
  6. uart0.Send(addr);//send receiver address
  7. uart0.Send(cmd);//send increment command
  8. uart0.Send((addr+cmd) % 256 );//send checksum
  9. }
  10. ...
  11. main() {
  12. uart0.Init(0,1200, false); //uart0:
  13. int i = 0;
  14. while(1) {
  15. i = (i+1) % 256;
  16. led1.Toggle();
  17. led2.Toggle();
  18. Send_Packet(RADDR, i); // send packaged byte
  19. //uart0.Send(i); //send unpackaged byte
  20. lcd.goto_xy(0,0);
  21. lcd.send_format_string("%d ", i);
  22. _delay_ms(1000);
  23. }
  24.  

the receiver code

  1.  
  2.  
  3. #define RADDR 0x10 // common address parameter
  4.  
  5. // we can't know in which order we get the bytes of a package
  6. // so we use a circular buffer with 3 positions
  7. uint8_t buf[3], ind, v;
  8.  
  9. ISR(USART_RXC_vect){
  10. volatile char c = UDR; //Read the value out of the UART buffer
  11. //v =c; // unpackaged byte
  12. // add the byte to the circular buffer
  13. buf[ind] = c;
  14.  
  15. // we don't know which is the first byte of our package,
  16. // so we check the circular buffer, testing each byte for the start condition
  17. for (int i=0;i<3;i++) {
  18. uint8_t raddress, data, chk;//transmitter address
  19. raddress = buf[(0+i)%3];
  20. data = buf[(1+i)%3];
  21. chk = buf[(2+i)%3];
  22. if (chk == ((raddress + data)%256))
  23. v = data;
  24. }
  25. // increment the circular buffer index
  26. ind = (ind + 1) %3;
  27. }
  28. ..
  29. main() {
  30. uart0.Init(0, 1200, true);
  31. int i = 0;
  32. while(1) {
  33. i++;
  34.  
  35. lcd.lcd_cursor_home();
  36. lcd.lcd_string_format("idx:%d v:%d \n2nd line ", i, v);
  37. _delay_ms(1000);
  38. }
  39.  

The algorithm works much better than the first, as now we won't get those random bytes picked up by the receiver. And because of the RADDR identifier, we can use several such modules in close proximity, as we now know which packet is intended for which receiver.

Resources

KLP Walkthrough
Running TX433 and RX433 RF modules with AVR microcontrollers

Source code

This is only a simple UART implementation, but you might find the data package logic useful:
FS1000A_Radio

Related Post

This article has 7 Comments

  1. dear sir.
    Is it possible to use two emitter+receiver modules pair in each side to make bidirectional communication and solve the problem of transmitters errors?

  2. Hi Radu – I am new to this RF and got the transmitter and receiver and make a test, – you write “Setting the emitter’s DATA pin to HIGH will result in a radio transmission that will set the receiver’s DATA PIN to high”
    – so I put the datapin to high and the datapin on the receiver go high but only short time – then it go low again even the transmitter datapin is high – when I set it low and high again the same happen in the receiver !???
    Have I miss understand you ? I thought that while the datapin on the transmitter then the receiver pin also will be high all the time !
    Can you please clear this for me ?

    Best regards Erik

  3. Hello Erik,you can see the idea on the oscillogram included. The idea is to send sequences of 0’s and 1’s. You define the baudrate. You define how long to wait to register a 1 or a 0. As you can see, it works pretty well, since I was able to use existing UART code. Hope this helps.

Leave a Reply