RF-link

Fra HTX Arduino
Spring til navigation Spring til søgning
Modul til sending af 433 MHz radiosignal
Modul til modtagelse af 433 MHz radiosignal

RF-link modulet er baseret på to billige moduler der kan købes sammen i Kina f.x. ved denne nethandel[1]

Modulet er udviklet uden at kigge på databladet, men blot ved at gætte sig frem omkring de betegnelser der er på printet, hvor man kan se hvor den skal have stel og +5V, og hvor data skal ind/ud.

Det er ikke så let at finde egentlige data på enhederne, men der ligger lidt på sendermodulet [2], godt nok i en 315 MHz version, men der er ikke den store forskel.

Modtagermodulet er fundet på en lidt sjov side [3], men man kan læse hvilke data den burde overholde.

Er man interesseret i kommunikationen til Arduino, så er der et eksempel på den her[4].

Teori for RF-link modulet

Der er ikke så meget teori for RF-link modulet, da det princip der udnyttes blot er at transmittermodulet kan styres til at lave en frekvens på 433 MHz som den sender, eller der kan slukkes for den, så den ikke sender noget. Modtagermodulet giver så et højt signal ud når den modtager 433 MHz og et lavt når den ikke modtager noget.

For at teste om der er "hul igennem" på det helt simple plan, så er der lavet et lille program, der ligger i send1.jal, og som består af følgende kode

forever loop
   tx = ! tx
   if kontakt then
      delay_10us(10) -- giver en puls/pauselængde på ca. 127 us
   else
      delay_10us(2)  -- giver en puls/pauselængde på ca. 47 us
   end if
end loop

Ved at sætte forsyning på modtageren kan man måle med et oscilloscop hvordan modtageren reagerer.

Med et en frekvens på ca. 10 kHz (47 us puls og 47 us pause) vil det svare til en bitrate på 20.000 bit/sekund, her kan man se at puls og pause ikke modtages ens, som vist her:

Rf-send1.png
Måling med 47 us puls-tid og 47 us pausetid

Denne forskel kan blive problematisk, hvis man ønsker at kommunikere så hurtigt. I den videre udvikling nedsættes hastigheden for at lette transmissionen.

Hvis man i stedet sender med 127 us puls og pause, så bliver det modtagne signal noget tættere på en 50% duty-cycle, som det kan ses her:

Rf-send2.png
Måling med 127 us puls-tid og 127 us pausetid

Princip for RF-link

Opbygningen af RF-link består af to hardware-moduler: et sendermodul med en tilhørende software og et modtagermodul med en tilhørende software. De to moduler skal være indstillet til samme frekvens, hvor de viste moduler arbejder på 433 MHz.

Sender-modul

Opbygningen af sendermodulet er en færdig opbygget oscillator, der arbejder på 433 MHz, hvor man ved hjælp af en transistor styrer om oscillatoren skal være tændt eller slukket ved hjælp af en transistor på modulet. Styringen sker ved hjælp af databenet, hvor det skal have 5V for at sende og 0V for at være slukket.

Ben Navn Betydning PIC-ben
1 GND Stel 10
2 Vcc +5V forsyning 9
3 Data Sende signal - når den er høj, sendes 433 MHz Valgfrit 1-8

Modtager-modul

Modtagermodulet består af en diskret opbygget modtager omkring to transistorer og en svingningskreds hvor der indgår en trimmekondensator som er afstemt til den ønskede frekvens. Herefter bliver signalet ensrettet og sammenlignet med et niveau, så modulet giver høj ud når det modtager noget på den ønskede frekvens, og det giver lavt niveau ud når der ikke modtages noget.

Sammenligningen foretages med en LM358 comperator inde i modulet.

Ben Navn Betydning PIC-ben
1 GND Stel 10
2 Data Det modtagne signal - høj, når der modtages 433 MHz Valgfrit 1-8 kant-interrupt
3 Data Samme signal Valgfrit 1-8 kant-interrupt
4 Vcc +5V forsyning 9

Specifikationer for RF-link

Specifikationerne er sakset fra de hjemmesider det har været muligt at finde, som dokumenterer funktionen.

Sender modul[2]
Parameter Data
Forsynings spænding 2.5V - 12V DC (nom. 5V)
Krævet forsynings-strøm 4mA @ 5V, 15mA @ 9V
Hvile-strømforbrug 10 uA
Modulationsform ASK
Temperatur område -10 til 60 grader C
Maks datarate 9600 k Baud
Data input TTL niveau (0/5V)
Output effekt 20 mW @ 5V


Modtager modul[3]
Parameter Data
Forsynings spænding 3V - 8V DC (nom. 5V)
Krævet forsynings-strøm < 3mA @ 5V
Modtage frekvens 315 / 433 MHz (433 MHz i vores version)
Frekvens område 260 - 440 MHz justerbart (undlad dette)
Modulationsform ASK / OOK
RF følsomhed -105 dBm (50 Ω)
Data rate < 5 kbps (315 MHz, -95 dBm)
Temperatur område -20 til 70 grader C
Data output TTL niveau (0/5V)
Antenne længde 24 cm (315 MHz), 18 cm (433,92 MHz)

Målinger på RF-link sending

For at udvikle en transmission, der kan overføre en byte korrekt, så opbygges der en datapakke ud fra følgende principper:

  • Pakken starter med en marker på 1 ms sending og en kort pause.
  • Der sendes en adresse, for at kunne sende til flere modtagere
  • Efter adressen sendes det inverterede, for sikre at pakken går godt
  • Den ønskede byte sendes
  • Der sendes det inverterede, igen for at sikre at data kommer korrekt over
  • Der sendes med mindst betydende bit først
  • En lav bit (0) sendes som 120 us lavt efterfulgt af 70 us højt
  • En høj bit (1) sendes som 120 us lavt efterfulgt af 170 us højt

Sendingen efter disse principper, som også går igen i den endelige version er implementeret i send2.jal og en samlet sending ser ud som følger:

Rf-send3.png
Sending af en samlet pakke med marker og 4 bytes

Pakken tager totalt ca. 9 ms at sende som vist.

De 4 bytes består af byte med en adresse efterfulgt af den inverterede for at sikre at det modtages korrekt, derefter en byte med data og igen den inverterede for sikkerhed i modtagelsen.

Adressen sendes for at kunne identificere hvilken enhed man sender til, og data er for at kunne sende noget konkret indhold – de to bytes kan ikke forveksles, da de altid kommer i den rækkefølge efter en marker på 1 ms.

Byten sendes med mindstbetydende bit først, og et 0 er kodet som ca. 120 us lav og 70 us høj, mens et 1 er kodet som ca. 120 us lav og 170 us høj, så det er let at se forskel på dem i modtagelsen. Ved at der først sendes en byte og derefter den inverterede vil der altid sendes 8 lav og 8 høj, så pakketiden er konstant.

Herunder ses en måling hvor man kan se slutningen af markeren efterfulgt af 9 bit. Første bit er høj, de næste 4 bit er lave, næste igen er høj og de to sidste bit der hører til byten er lave, og da mindst betydende bit sendes først, så er adressen udtrykt binært 0010_0001, hvilket svarer til 33, som det også kan ses i send2.jal.

Rf-send4.png
Slutningen af marker og den første byte (adressen)

Målinger på RF-link modtagelse

For at teste modtagelsen er der opbygget en relativ simpel modtagelse modtag1.jal, hvor man venter aktivt på at modtage de enkelte bit.

På modtagersiden ser det ud som følger, hvor man kan se at de lave pauser er noget kortere end målingerne på sendersiden (selvom det er en anden timebase det er målt i) :

Rf-modt1.png
Modtagelse af den første del af en pakke.

Her kan man se at den første byte består af en lang, fire korte en lang og to korte, hvilket svarer til det binære tal 0010_0001 (laveste bit kommer først), så tallet er 33, som er den faste sendeadresse i send2.jal

Den røde kurve er bare et signal for at scopet kan trigge ved modtagelse.

Praktiske problemer ved transmission

Ved modtagelsen er man nødt til at tage højde for at der kan være en del støj ved modtageren. Det viste sig ved test at det hjalp en hel del at anvende ekstern forsyning, for at fjerne noget af støjen.

Hvis man ikke opbygger softwaren robust, så kan man kan miste enkelte pakker, fordi man misforstår noget af støjen som en del af en pakke.

Rf-modt2.png
Måling på modtager-siden mellem pakkerne, der viser støjpulser

For at teste robustheden af softwaren er en del af testen foretaget ved at bruge forsyning via PICkit 2, hvor man så får væsentligt mere støj.

I den endelige version er modtagelsen er alligevel så robust at over halvdelen af pakkerne kommer uskadt igennem.

Rf-modt3.png
Måling på modtagersiden mellem pakkerne, når man bruger PICkit 2 som forsyning

Simpel test af rækkevidde

For at teste hvordan rækkevidden er, så er senderen blevet placeret to etager over modtageren, så der skal transmitteres gennem to etageadskillelser, hvilket normalt ville dæmpe signalerne væsentligt. For at optimere er der monteret ca 18 cm antenne på både sender og modtager. Både sender og modtager forsynes med ekstern forsyning, for ikke at bidrage med ekstra støj.

I denne konfiguration gik alle pakker igennem ved en test over 256 pakker.

Hvis det kun er senderen der har monteret antenne, så går ca. 220 ud af 256 pakker igennem.

Hvis modtageren forsynes med PICkit 2, så reduceres antallet af pakker der kommer igennem til ca. 6 ud af 256 uden antenne på modtageren, og dette kan igen forbedres ved hjælpe af en antenne på modtageren, så der kommer ca. 16 igennem ud af 256.

Andre versioner af RF-link

Man kunne bygge sit eget modul, men det er normalt ikke tilrådeligt, da det kan være svært at sikre sig at man ikke sender noget på uønskede frekvenser, hvilket kan forstyrre anden kommunikation, som kan være kritisk (f.x. flykommunikation).

Simpel anvendelse af RF-link

For at kunne foretage en transmission med forskellige værdier kan man opbygge et sender-program med f.x 3 taster, hvor man lader den sende de 3 funktioner afhængigt af tasterne. Det kunne gøres med følgende kode,laver ud fra send2.jal:

forever loop
   if kontakt1 then
      send(33, 1)
      delay_1ms(500)
   end if
   if kontakt2 then
      send(33, 2)
      delay_1ms(500)
   end if
   if kontakt3 then
      send(33, 3)
      delay_1ms(500)
   end if
   delay_1ms(10)
end loop

Koden er lavet så den fanger tasten, og sender pakken en gang, og det skal modtageren så opfange for at det kan bruges som tænd/sluk af 3 udgange.

For at modtage dette kan der laves en modificeret version af modtag2.jal, hvor der reageres på det der modtages:

var byte modtaget
forever loop
   if rf_get_byte(modtaget) then
      if modtaget == 1 then
         udgang1 = ! udgang1
      end if
      if modtaget == 2 then
         udgang2 = ! udgang2
      end if
      if modtaget == 3 then
         udgang3 = ! udgang3
      end if
   end if
end loop

Dette forudsætter at alle pakker modtages korrekt, hvilket det ikke kan garanteres at de gør, men dette er blot et simpelt eksempel på en anvendelse, hvor man godt kunne leve med at man evt. skulle trykke på knappen igen.

Softwaren med send2.jal og modtag2.jal kan hentes i denne ZIP-fil.

Software modulet RF-link.jal

Modulet indeholder både sender-software og modtager-software, hvilket man vælger ud fra to bit-constanter, som kan gøres som følger inden man includer RF-link modulet:

const bit receiver = false
const bit transmitter = true
include RF-link

Interface-fil til RF-link

I interface-filen ligger definitioner til både sende-modulet og modtager-modulet. Begge dele skal ligge der for at modulet kan fungere, men det er kun den ene del der oversættes, afhængigt af hvilken del man vælger.

const byte rf_my_address = 33

const byte rf_bytes_send = 4

alias rf_rx      is pin_a2
alias rf_rx_dir  is pin_a2_direction

alias rf_tx      is pin_a2
alias rf_tx_dir  is pin_a2_direction

Der er defineret hvilken adresse modulet har, og det skal være den samme for sending og modtagelse.

Antallet af bytes der sendes er defineret som det totale antal der reelt sendes, selvom det kun er en byte der reelt kan anvendes, så sendes der 4 byte. Dette kan lette en udvidelse til at sende flere bytes, hvis man har det behov.

Der skal selvfølgelig også defineres et ben til sende-bittet og et til modtage-bittet. Der er her valgt samme ben, hvilket ikke betyder noget, da det alligevel skal fungere i hvert sit program.

Der er også definitioner som går på interruptet:

if target_chip == PIC_16F690 then
alias     rf_rx_int              is IOCA_IOCA2   -- Skal rettes med pin_a2
alias     rf_intcon_ie           is intcon_rabie
alias     rf_intcon_if           is intcon_rabif
elsif target_chip == PIC_16F684 then
alias     rf_rx_int              is IOCA_IOCA2   -- Skal rettes med pin_a2
alias     rf_intcon_ie           is intcon_raie
alias     rf_intcon_if           is intcon_raif
end if

Der anvendes et kanttrigget interrupt, som er navngivet forskelligt på de forskellige procesorer. Derfor oversættes interruptets navn til samme alias, så man kan aktivere interruptet i softwaren og man kan arbejde med interrupt-flaget inde i intrerruptrutinen.

Enedelig skal interruptet for det aktuelle ben slås til ved at man aktiverer det, derfor skal der defineres hvilket interruptben det er man anvender.

Demo-eksempler til RF-link

Der er lavet 2 demo-eksempler som illustrerer hvordan modulet RF-link.jal fungerer. Der er koderne send_demo.jal og modt_demo.jal, der kan det samme som send2.jal og modtag2.jal, men med den forskel, at de anvender modulet RF-link.jal

Koden i send_demo.jal ser ud som følger:

const bit receiver = false
const bit transmitter = true
include RF-link

var byte tael = 1

forever loop
   rf_send(33, tael)
   delay_100ms(10)
   tael = tael + 1
end loop

koden i modt_demo.jal ser ud som følger:

const bit receiver = true
const bit transmitter = false
include RF-link

var byte modtaget
forever loop
   if rf_get_byte(modtaget) then
     ALCD_cursor_position(0,0) -- start of first line
     -- Skriv det aflæste direkte
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_arr[0])
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_arr[1])
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_arr[2])
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_arr[3])
     ALCD_cursor_position(1,0)
     ALCD_write_char(" ")
     ALCD_Dec_3(modtaget)
   else
     ALCD_cursor_position(1,4)
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_error)
     ALCD_write_char(" ")
     ALCD_Dec_3(rf_rx_tid)
   end if

   delay_1ms(10)
end loop

Modtage-demoen fungerer ved at den forsøger at modtage en pakke, og hvis det lykkes, så skrives der i første linje de 4 bytes der er modtaget, og i starten af anden linje skrives den byte som funktionen returnerer i parameteren. Hvis pakken ikke modtages korrekt bliver der i næste linje udskrevet to interne variabler, der indeholder en fejlstatus og en den tæller der anvendes til at bestemme tiderne på det modtagne.

Modtage-demoen viser følgende skærmbillede:

Rf-modt-screen.jpg
Skærmvisning af modtagelse

Opbygningen af sendingen

Princippet i at sende en byte er relativt simpelt, da det er baseret på aktivt venten i koden, så de 9 ms det tager at sende en marker og de 4 byte sker aktivt i sende-rutinen, og der returneres ikke før hele byten er sendt.

Sendingen er opbygget ud fra følgende pseudokode:

Send en marker
Send adressen
Send det inverterede af adressen
Send data
Send det inverterede af data

Dette er implementeret ved hjælp af følgende kode:

Procedure rf_send(byte in adr, byte in data) is
   rf_tx = high
   delay_10us(100)   -- 1 ms marker til start
   rf_tx = low
   delay_10us(10)
   rf_send_byte(adr)    -- send adresse
   rf_send_byte(! adr)  -- inverteret adresse for sikkerhed
   rf_send_byte(data)   -- send data
   rf_send_byte(! data) -- inverteret data for sikkerhed
end procedure

Det at sende en byte er implementeret ved hjælp af følgende pseudokode:

Løb gennem alle 8 bit
   send lav i 100 us
   hvis aktuel bit er 0
      send høj i 50 us
   ellers
      send høj i 150 us
   skift til næste bit

Dette er implementeret ved hjælp af følgende kode:

Procedure rf_send_byte(byte in data) is
   for 8 loop
      delay_10us(10)
      rf_tx = high
      if (data & 1) == 0 then
         delay_10us(5)
      else
         delay_10us(15)
      end if
      rf_tx= low
      data = data / 2
   end loop
end procedure

Opbygningen af modtagelsen

Hele modtagedelen er opbygget omkring et interrupt, hvor man anvender det kanttriggede interrupt af port A hvis det er PIC16F684 eller hvis det er PIC16F690, så kan det være både port A og port B. Desuden anvendes timer 0 og interruptet til den, ved håndtering af fejl.

Selve det at modtage pakken og få den godkendt sker i en servicefunktion, der kan beskrives ud fra følgende pseudokode:

Hvis der er en pakke klar
   Hvis der ikke er opstået en fejltilstand
      Hvis adressen er til mig og der ikke er fejl i adressen
         Hvis der ikke er fejl i data
            Returner data
   Registrer at pakker en modtaget
ellers
   Returner fejl

Dette er implementeret ved hjælp af følgende kode:

function rf_get_byte (byte out data) return bit is
   var bit retval = true
   if rf_data_ready then
      if rf_rx_error == 0 then
         if rf_rx_arr[0] != (! rf_rx_arr[1]) then
            retval = false
         elsif rf_rx_arr[2] != (! rf_rx_arr[3]) then
            retval = false
         elsif rf_rx_arr[0] != rf_my_address then
            retval = false
         else
            data = rf_rx_arr[2]
         end if
      end if
      rf_data_ready = false
   else
      retval = false
   end if
   return retval
end function

Selve tidsregistreringen sker i interruptet, men ikke ved hjælp af timer-interruptet, men derimod at timer 0's register (TMR0) sættes til at tælle med en takt på 8 us, så man kan måle tiden ved at se på indholdet af registeret TMR0.

Kommunikationen er indrettet så det udelukkende er de høje pulser der er interessante tidsmæssigt, så hvis man beskriver interruptet der kommer på alle kanter, så kan det gøres ud fra følgende pseudokode:

hvis input et højt så
   nulstil TMR0 (tiden i den lave periode måles ikke)
ellers
   husk TMR0
   hvis der er modtagelse i gang
      aflæs en bit
   ellers
      hvis tiden er ca. 1 ms og sidste pakke er aflæst
         sæt op til modtagelse af en ny pakke
         nulstil fejltilstand
marker at kant-interruptet er afviklet

Dette er implementeret med følgende kode:

   if rf_intcon_if then
      if rf_rx then
         TMR0 = 0
      else
         rf_rx_tid = TMR0
         if rf_receiving then
            -- Aflæs en bit
            --
         else
            if (rf_rx_tid > 124) & (rf_rx_tid < 132) & (! rf_data_ready) then
               rf_receiving = true
               rf_rx_error = 0
               rf_rx_ptr = 0
               rf_rx_bit_nr = 0
            end if
         end if
      end if
      rf_intcon_if = false
   end if

Selve det at aflæse en bit tester på mange forskellige ting, og der kan komme forskellige fejl-tilstande ud af det, da dette gerne skulle ske mens vi er i gang med at modtage en pakke, men man kan have misforstået en marker, eller man kan have mistet nogle bits i en pakke, eller der kan komme støj inde i pakken som kan forstyrre modtagelsen.

Alle disse ting skal der tages højde for i modtagelsen af bits, og hvis ellers alt går godt, så skal der modtages 32 bit, der lagres i 4 byte. Dette kan illustrares med følgende pseudokode:

Hvis der modtages i pakken så
   skift bit
   hvis tiden for høj < 48 us så
      Marker fejl 1 (for kort en høj puls)
   hvis tiden for høj < 144 us så
      Modtag 0-bit
   hvis tiden for høj < 240 us så
      Modtag 1-bit
   ellers 
      Marker fejl 2 (for lang en høj puls)
   gå til næste bit
   hvis antallet af bit = 8 så
      Gem modtaget byte
      Gå til næste byte
      hvis antallet af bytes = 4 så
         Afslut modtagelse og marker at der er en pakke klar

Denne del er implementeret inde i den foregående kode som:

   if rf_rx_ptr < rf_bytes_send then
      rf_rx_byte = rf_rx_byte / 2
      if rf_rx_tid < 6 then
         rf_rx_error = 1
         rf_receiving = false
      elsif rf_rx_tid < 18 then
      elsif rf_rx_tid < 30 then
         rf_rx_byte = rf_rx_byte + 128
      else
         rf_rx_error = 2
         rf_receiving = false
      end if
      rf_rx_bit_nr = rf_rx_bit_nr + 1
      if rf_rx_bit_nr == 8 then
         rf_rx_bit_nr = 0
         rf_rx_arr[rf_rx_ptr] = rf_rx_byte
         rf_rx_ptr = rf_rx_ptr + 1
         if rf_rx_ptr == rf_bytes_send then
            rf_data_ready = true
            rf_receiving = false
         end if
      end if
   end if

Som nævnt er det TMR0 registeret der håndterer tids-målingen. Til dette register er der et interrupt tilknyttet når den tæller over fra 255 til 0 (svarende til godt 2 ms). Da der i en gyldig pakke ikke kan være hverken høje eller lave impulser der er mere end 1 ms, så vil det enten være mellem to pakker, eller en fejl i modtagelsen, hvis den stadig er i gang med at modtage en pakke når timer 0 interrupter (den nulstiller altid i starten af en lav puls).

Timer 0 interruptet kan udtrykkes med følgende pseudokode:

   afstil timer 0 interruptet
   hvis der modtages så
      Marker fejl 3 (mere end 2 ms pause i modtagelse)
      Sæt modtagelsen passiv

Dette er implementeret ved følgende kode:

   if intcon_tmr0if then
      intcon_tmr0if = False
      if rf_receiving then
         rf_rx_error = 3
         rf_receiving = false
      end if
   end if

Anvendelse af RF-link

Man skal include modulet som vist:

const bit receiver = false
const bit transmitter = true
include RF-link

Herefter kan den valgte af de to funktioner rf_send eller rf_get_byte anvendes som vist.

Alle kodeeksempler ligger samlet i en ZIP-fil


Moduler på Holstebro HTX
Tastaturer Displays AD-konvertering I/O-ekspander Serielt Interface Færdige Andre
RC-tast - AD-tast - M_tast ALCD - LCD ADC_holst - ADC
mcp3201 - mcp3208
input - output Seriel_holst - Serial hardware
Serial hw int cts - Serial software
Stepmotor - RFID
RGB - RF-link - Afstand
Humidity - Analog temp - Dig temp
Accelerometer
Rotary Encoder

Oversigt over Hardware Moduler på Holstebro HTX

Keyes-moduler på Holstebro HTX
Simple Digitale Input Switch modul - Reedrør - Hall sensor - Optisk Skift - Photo Gate - Vibration sensor - Vibration switch - Tilt sensor - Kviksølv kontakt - Linje følger
Digitalt kodede Input IR Modtager - Humidity -Digital Temperatur
5 benede Input Rotary Encoder -XY Joystick
Digitale 4 benede Input Magic Cup Light - LED 3-farve - RGB - RF-link - Afstand
Justerbare analoge/digitale Input Reed Magnetsensor - Temperatur Niveau - Metal detektor - Flamme - Hall Kontakt - Almindelig Mikrofon - Følsom Mikrofon
Simple digitale Output LED 2-farve - Aktiv Buzzer - Blink LED - IR LED - Laser - Relæ modul - Passiv Buzzer
Analoge input Analog Temperatur - LDR - Finger Pulsmåler - Lineær Magnetfelt

Referencer