Category Archives: Arduino

Modifiering av 2-m-saxen ROX-2X

Den här artikeln publicerades först i QTC nr 5 2023.

Via SM0GNS Peder fick jag tag på en ganska komplett byggsats för kretskortet till en ROX-2X radiopejlmottagare [1] för 2-m-bandet. Antagligen hade Peder tröttnat på att jag lånat färdigbyggda mottagare av honom och tyckte det var dags att jag skaffade en egen, vilket jag inte var sen att nappa på.

Efter att jag lött ihop mottagaren och verifierat att den fungerade så började jag fundera på om den skulle gå att förbättra på något sätt. Främst var jag intresserad av att få till en digitalt styrd lokaloscillator för att eliminera risken att man har fel frekvens inställd, till exempel till följd av att man oavsiktligt kommer åt frekvensratten under en tävling, men jag hittade även ett par andra saker som kunde åtgärdas och några extra finesser att lägga till. Dessutom hittade jag på en egen antennkonstruktion som verkar fungera bra.

Den här artikeln beskriver hur mottagaren fungerar och vad jag gjorde för förändringar i den. Jag har inte haft tillgång till någon bygg-, komponent- eller funktionsbeskrivning utöver vad som finns på [1], så det jag skriver är mestadels baserat på schemat och egna tester.

Figur 1 visar schemat för ROX-2X med rutor som markerar vilka delar jag kommer att ändra.

Figur 1. Kopplingsschemat för ROX-2X.

Jag har ritat om schemat för att göra det aningen mer lättläst och för att enkelt kunna rita in ändringar (och för att jag inte bett om tillåtelse att återpublicera originalschemat). En liten detalj som jag fixat redan här är att den gröna lysdioden D5 som visar att spänningen är påslagen var felvänd i originalschemat.

RF-delarna sköts av en SA636 som är en krets främst tänkt för FM-mottagning i exempelvis DECT-telefoner. Tyvärr har den slutat tillverkas och är nog svår att få tag på, så inget att basera framtida konstruktioner på, men ett antal exemplar av ROX-2X lär finnas därute och många av idéerna här är mer allmänt tillämpliga, så förhoppningsvis är den här artikeln ändå av intresse.

Radiopejling på 2-m-bandet sker med sändare som skickar ut morsekod genom att AM-modulera en bärvåg. Så hur kan en krets för FM-mottagning fungera i detta sammanhang? Tricket är att man inte alls använder de vanliga utsignalerna, utan bara lyssnar på RSSI- utgången (Received Signal Strength Indicator). SA636 har nämligen en snabb RSSI som duger som AM-demodulator i denna tillämpning. En annan kreativ lösning i konstruktionen är att förstärkningen delvis ställs in genom att tappa av bias-ström på RF-ingångarna. Att detta fungerar kan man möjligen ana sig till av databladet som visar ett förenklat schema av ingångssteget, men möjligheten verkar inte alls nämnas där. Kanske är det något konstruktören experimenterat sig fram till.

Ingångsimpedansen är ganska hög, 700 Ω parallellt med 3.5 pF single-ended, dvs 1400 Ω parallellt med 1.75 pF differentiellt, så anpassningen mot Yagi-antennens impedans i trakterna av 40 ohm mår bra av en ganska rejäl upptransformering av impedansen med en faktor 9 (1:3 varvtalsförhållande) via transformatorn L2. Med trimkondensatorn C3 kan man avstämma anpassningen. Mer om det senare.

Mellanfrekvensen är på klassiska 10,7 MHz och två keramiska filter för denna frekvens vaskar fram den önskade signalen. Antennen och dess matchningskomponenter får duga som preselektorfilter före mixern för att undertrycka det oönskade sidbandet.

Lokaloscillatorn är LC-baserad och avstäms med en kapacitansdiod (D2) som biaseras av en potentiometer (RV1.2). Spolen (L3.1) har en kärna man måste trimma för att hamna i rätt frekvensområde.

RSSI-signalen innehåller dels den önskade AM-demodulerade audiosignalen (om än logaritmiskt demodulerad, men låg distorsion är inte en prioritet här) och dels en DC-nivå som indikerar signalstyrka även den tid då modulering saknas. Sändare kan antingen slå av bärvågen mellan morsepipen eller låta bärvågen ligga kvar omodulerad. I det senare fallet finns det en radiosignal att pejla mot hela tiden under ett sändningspass, alltså även när den AM-demodulerade signalen inte innehåller något hörbart, och det drar den här mottagaren nytta av som vi ska se.

RSSI-signalen förgrenas dels till en förstärkare, U4, som driver hörlurarna med den demodulerade signalen, dels via ett lågpassfilter, R8-C16, till en spänningsstyrd oscillator, U2, och dels till transistorn Q1 som ska tända lysdioden LED1 vid stark signal.

U4 har en volymkontrollingång, ben 4, som påverkas av samma volymkontroll, RV2.2, som justerar biasen på RF-ingångarna enligt tidigare beskrivning. Jag är osäker på hur mycket av känsligheten som bestäms av den justerbara biaseringen av RF-ingången respektive av U4. U4 har enligt databladet ett förstärkningsområde mellan ungefär -70 och +20 dB, men toleransen på vilken förstärkning man får för en viss styrspänning är hög, så olika exemplar av kretsen kan ha rejält olika förstärkning vid en och samma spänning. Men det är inte något större problem i den här tillämpningen.

Jag upplevde att den maximala ljudstyrkan var onödigt hög, så för att minska den kopplade jag om lite så att styrspänningen tas från en punkt som ligger ett diodfall lägre än normalt. Det krävde en kapad kopparbana vid ben 4 på U4 samt en kort bygel från C22 till ben 3 på D4. En alternativ och enklare lösning hade varit att öka värdet på R20 som ligger i serie med hörlurarna.

Ytterligare en åtgärd för att minska maximal ljudstyrka, och samtidigt göra en större del av volymrattens område användbart, var att sätta 22 k parallellt med volympotten RV2.2.

En viktig finess i mottagaren är den RSSI-styrda VCO:n U2 som används som en ”audio S-meter”. När RSSI-signalen blir tillräckligt hög börjar U2 oscillera, först med ett lågfrekvent tickande och sedan med allt högre frekvens i takt med att radiosignalen blir starkare (eller biaseringen av RF-ingången ökas). Även denna VCO-signal är kopplad till hörlurarna och den gör att det blir lättare att med hörseln avgöra små skillnader i signalstyrka eftersom de representeras med skillnader i frekvens istället för med skillnader i amplitud. Det här är väldigt användbart eftersom antenndiagrammet är ganska trubbigt, så det blir viktigt att särskilja små variationer i signalstyrka för att avgöra i vilken riktning som signalen är starkast när man sveper med antennen under pejlingen. Och som nämnts tidigare så hörs denna ton även mellan morsepipen när den vanliga audiosignalen är tyst (såvida det inte är en sändare som stänger av bärvågen mellan pipen).

Nivån på den RSSI-signal som styr U2 bestäms dels av radiosignalens styrka och dels av vilken förstärkning man ställt in på RF-ingången, men inte av den ytterligare förstärkningen i U4. Det är bara under ca halva området för volymratten som RF-förstärkningen påverkas, så när man vridit upp volymen till mer än ca hälften av max så blir inte RSSI-signalen som styr U2 högre. Vid svaga radiosignaler räcker nivån inte till att få igång U2, oavsett volyminställning, så när man är långt från sändaren får man förlita sig på den vanliga ljudstyrkan när man pejlar.

Den tredje grenen av RSSI-signalen går som sagt till Q1 och LED1. Tanken är att lysdioden ska tändas när RSSI-signalen blir stark nog, men detta fungerade inte alls. För att LED1 ska börja tändas måste nämligen RSSI bli högre än Vbe för Q1 (0,6 V) plus spänningsfallet över LED1 (ca 2 V), men den nivån uppnåddes aldrig. Antagligen fungerade det i en tidigare version av konstruktionen när R6 (som bestämmer förstärkningen hos RSSI-förstärkaren i U1) var 33k istället för 22k, men alltså inte med R6 = 22k. Med lite kreativ omkoppling så att LED1 hamnar i kollektorkretsen för Q1 istället för vid emittern, samt tillägget av ett spänningsdelande motstånd på basen så återställdes funktionen. De schemamässiga ändringarna framgår av figur 2 medan de fysiska ändringarna är utpekade i figur 3 med numrerade pilar som pekar ut följande detaljer:

  1. R100, 150 kohm från högra änden av R9 (basen på Q1) till jord.
  2. Kollektorn, ben 3, på Q1 svävar i luften. Till kollektorn är en sladd som leder till katoden på LED1 kopplad. Tunn emaljerad koppartråd användes för att inte riskera att bryta sönder Q1 när sladden rör sig.
  3. En tråd som kopplar emittern på Q1 till jord.
  4. R10 (330 ohm) har flyttats från sin ordinarie plats till mellan 5V och anoden på LED1.
  5. Sladden till anoden på LED1.

Figur 2. Modifierat kopplingsschema för ROX-2X.

Figur 3. Modifieringar för att få LED1 att fungera som avsett.

Det är tänkt att man inte ska behöva någon strömbrytare till mottagaren. Detta åstadkoms genom att gaten på PMOS-transistorn Q2 dras upp till 9 V av R18 när ingen hörlur är inkopplad. Q2 slår då av och resten av konstruktionen blir utan matning. När en hörlur är inkopplad dras gaten låg och Q2 börjar leda. Regulatorn U3 skapar en stabil 5-V-matning och lysdioden D5 indikerar att strömmen är på och att batterispänningen är högre än dryga 7 V (5 V plus spänningsfallet på ca 2 V över D5 plus lite spänning till strömbegränsaren R14). Genom att D5 sitter parallellt med spänningsregulatorn får man dels en sorts lågt-batteri-varning och dels slipper man slösa extra ström till denna diod eftersom all ström som flyter genom den också går vidare och gör nytta i resten av elektroniken. Hade dioden inte suttit där hade dess energiförbrukning istället omvandlats till värme i U3.

Tyvärr ville inte den hörlursstyrda strömbrytarfunktionen fungera. Efter lite felsökande upptäckte jag att C25, som är ritad som en polariserad kondensator i schemat, inte alls får vara polariserad. Jag hade satt dit en tantal, men i fallet att ingen hörlur är inkopplad kommer R18+R19+D6+R20 att lägga på en spänning med fel polaritet på denna, med läckström som resultat. Läckströmmen gör att gaten på Q2 inte blir hög nog för att Q2 ska slås av, så strömbrytningsfunktionen fungerade inte.

Lösningen är enkel, nämligen att ersätta C25 med en opolariserad keramisk kondensator. Om man tittar på foton på kretskortet på [1] så ser man att där sitter en keram och inte en tantal, så det är bara schemat som är lite vilseledande. Jag blev i alla fall fintad och hann konstruera in en fysisk strömbrytare som fortfarande finns kvar i mitt bygge.

Den stora förändringen jag ville göra var dock som sagt att införa en digitalt styrd lokaloscillator. Vidare ville jag hålla detta projekt någorlunda enkelt (betydligt mycket mindre ambitiöst än 80-m-mottagaren jag beskrev i en tidigare artikel [2]). Specifikt ville jag undvika att behöva konstruera (och vänta på tillverkningen av) nya mönsterkort och helst också klara mig med att använda komponenter som jag redan hade hemma. Dessa mål uppnåddes mestadels, men tidsmässigt drog det ändå ut på tiden och blev (som vanligt?) mer omfattande än jag hade tänkt mig. Inte minst mjukvaran tog tid att utveckla och har man väl en processor så är det lätt hänt att man kommer på att man kan lägga till den ena finessen efter den andra utan att behöva så värst mycket mer hårdvara.

Som digitalt styrd LO valde jag samma flexibla PLL-krets som jag använde i [2], nämligen Si5351A. Den styrs via ett I2C-interface och kan skapa tre olika fyrkantsvågor med valbar frekvens mellan ca 2,3 kHz och 200 MHz utgående från frekvensen hos en kristall. Fyrkantsvågen måste såklart filtreras för att bli en godtagbar sinus, vilket är vad som behövs för LO-ingången på SA636. Si5351A går att köpa monterad på ett litet kort tillsammans med en spänningsregulator [3] vilket gör det lätt att använda kretsen utan att göra ett eget mönsterkort.

För att skicka kommandon till Si5351A valde jag denna gång en för mig ny bekantskap, nämligen det prisvärda (runt femtilappen) och tillgängliga mikrokontrollerkortet Raspberry Pi Pico. Det innehåller en dubbelkärning ARM Cortex M0+ processor som officiellt går att klocka i upp till 133 MHz, vilket är långt mycket mer prestanda än vad som behövs här. Om man installerar rätt tillägg i senaste versionen av Arduino-miljön [4] går det bra att programmera kortet från denna för mig hemtama miljö. För att spara ström valde jag att ställa ned klockfrekvensen till 50 MHz (lägsta frekvens som är lätt inställbar i Arduinomiljön), men betydligt lägre klockfrekvenser hade också med råge gett tillräckliga prestanda.

För att det ska vara möjligt att se vad man ställt in mottagaren på för frekvens ville jag också ha någon sorts display. Jag grävde i skrotlådan och hittade en gammal 2×16 tecken alfanumerisk LCD med den vanliga HD44780 som drivkrets. Det finns färdigt stöd [5] för att prata med sådana displayer, så man behöver inte utveckla några egna lågnivårutiner.

För att styra det hela använde jag en digital ratt (encoder) och fyra membranknappar, ungefär som i [2]. Mjukvaran jag skrivit går att ladda ner från GtiHub [6]. Ett schema för kopplingarna utanför det ursprungliga kretskortet finns i figur 4.

Figur 4. Schema för kopplingarna utanför ROX-2X-kortet.

Jag byggde det hela i huvudsak på ett kopparlaminat som fick fungera både som jordplan och mekanisk support. De flesta svarta ledningarna i figur 4 utgörs därför av enbart en kort ledning ner till jordplanet. För att bygga lågpassfiltret använde jag ett litet mönsterkort ämnat för sådana filterbyggen som jag hade liggande sedan tidigare projekt.

Ett foto på hur det hela ser ut i praktiken finns i figur 5. Många av ledningarna är dragna med tunna emaljerade koppartrådar. De tar liten plats, väger inte mycket, är lätta att avisolera (genom att sticka in dem i en droppe lödtenn på spetsen av en lödpenna) och lätta att få att ligga ungefär där man vill. En nackdel är att man inte får någon färgkodning, så det kan vara svårare att reda ut vilken tråd som har vilken funktion. Ledningarna till ROX-2X-kortet drog jag dock med vanliga plastisolerade kopplingstrådar. Ofta partvinnade med en referenssignal (vanligtvis jord) för att inte få en massa onödiga jordslingor som möjligen skulle kunna skicka ut eller plocka upp störningar. LO-signalen drog jag i en tunn flexibel koax (ännu inte inkopplad i figur 5).

Figur 5. Display, filter, klockgenerator, processorkort mm, monterade på ett kopparlaminat i en 3D-utskriven låda.

Modifieringen av huvudkortet för att kunna koppla in den nya lokaloscillatorn beskrivs i figur 6. De olika pilarna anger följande:

  1. Anslutningspunkt för koaxialkabelns skärm.
  2. 2 st 100-ohmsmotstånd parallellt för att terminera koaxen i 50 ohm. Mittledaren på koaxen löds in på heta sidan av detta motstånd (ungefär där pil 3 pekar).
  3. Kapad ledning mot L3.
  4. C4 är bortplockad.
  5. C7 är bortplockad.
  6. C5 är bortplockad.

Figur 6. Modifieringar för att kunna koppla in den nya lokaloscillatorn.

I kopplingsschemorna i figurerna 2 och 4 framgår några finesser utöver vad som nämnts tidigare. Såväl RSSI-signalen som spänningen från 9-V-batteriet är indragna på analoga ingångar på processorkortet så att man kan bevaka dem från mjukvaran och presentera informationen i displayen. Batterispänningen skalas ned med en spänningsdelare för att passa området som ingångarna klarar och skyddsdioder mot 3.3 V ser till att ytterligare skydda ingångarna. En schottkydiod behövs också för att mata processorkortet från 5-V-spänningen från ROX-2X-kortet så att den matningen inte blir ihopkopplad med 5 V från USB-kabeln i de fall då man har USB inkopplad för programmering eller utläsning av information.

En aningen onödig funktion är att de två annars oanvända utgångarna från Si5351-kortet är kopplade (via dämpning i det här schemat och ännu mer dämpning i det modifierade ROX-2X-schemat i figur 2) till RF-ingången. Syftet är att kunna injicera en RF-signal med valfri frekvens så att mottagaren själv kan mäta upp karakteristiken på IF-filtret genom att svepa frekvensen på denna testsignal. Utgångarna konfigureras under testet för att drivas i motfas så att en differentiell signal skapas. Idén till den här lösningen har jag lånat från QDX-transceivern från QRP-labs [7]. Mätvärdena från RSSI-signalen, omräknade till någon sorts dB med hjälp av information i SA636-databladet, skickas ut via USB-porten till en PC där vidare analys kan göras i exempelvis Excel. Vid mätningen är det lämpligt att inte ha antennen inkopplad. Figur 7 visar inkopplingen av testsignalerna på LO-kortet medan figur 8 visar hur det ser ut där de kommer in på huvudkortet. Testsignalerna skickas i partvinnade emaljerade koppartrådar, vilket är tunnare och enklare att jobba med än koaxialkabel och fullt tillräckligt i det här fallet.

Figur 7. LO-kortet, filterkortet och inkopplingen av testsignalerna vid CLK1 och CLK2.

Figur 8. Injicering av testsignalerna på undersidan av ROX-2X-kortet. De stora seriekopplade kondensatorerna på 220 pF vardera utgör C2 som inte fick plats på ovansidan eftersom jag ville sätta transformatorn där.

Figur 9 och 10 visar resultatet från svep av såväl det önskade passbandet som spegelbandet när mottagaren var inställd på 144.75 MHz. Nivån på dämpningen utanför passbanden är inte att lita på eftersom dynamiken och brusnivån i denna test är begränsande. Inte litar jag heller fullt ut på att 1 dB skillnad på Y-axeln verkligen motsvarar 1.0 dB i verkligheten, men man får ändå en god fingervisning om hur IF-filtret beter sig.

Figur 9. Responsen i det önskade passbandet. Y-axeln är graderad i någon sorts approximativ dB-enhet.

Figur 10. Responsen i spegelbandet. Y-axeln är graderad i någon sorts approximativ dB-enhet.

Notera att den lilla asymmetrin i filtret är spegelvänd mellan signalbandet och spegelbandet, precis som väntat. Bandbredden är runt 50 kHz, vilket är lite onödigt mycket för en AM-signal. En lägre bandbredd skulle ge mindre brus, men också större risk att man måste ställa om frekvensen för att få in sändare som inte ligger så exakt i frekvens. Kristallfilter skulle kunna ge lägre bandbredd, men skulle behöva lite anpassningar eftersom de normalt har betydligt högre impedans än de 330 ohm som SA636 är gjord för.

En funktion som inte kräver någon speciell hårdvara, utan bara mjukvara, är att svepa upp 2-m-bandet och leta efter störningar för att se om mottagaren stör sig själv. Det är ju alltid en risk att de digitala delarna skapar någon ton på en olycklig frekvens som dränker signalen från en svag sändare. Figur 11 visar resultatet av två sådana svep utan inkopplad antenn. Dels ett svep som gjordes i en någorlunda störningsfri miljö (ute i trädgården några meter från huset) och dels inomhus nära en del datorutrustning. Man kan se att brusmattan från datorutrustningen överröstar eventuella interna störningar, samt att det inte finns några kraftiga interna störningar på någon viss frekvens, vilket är positivt.

Figur 11. Uppmätt brus utan antenn. Den blå kurvan är från en relativt tyst omgivning (ute i trädgården) medan den orangea är uppmätt inomhus nära diverse datorutrustning. Y-axeln är i godtyckliga dB.

Lådan har inte någon skärmning, utöver att mycket av elektroniken och sladdarna ligger nära jordplan eller är partvinnade med jordade ledningar, så kanske skulle man kunna vinna något på mer skärmning. Å andra sidan är det antennen som är klart bäst på att plocka upp störningar och jag har inte hört någon sänkning av störnivån när jag håller inne processorns resetknapp. När man gör det fortsätter Si5351 att generera LO-signalen och de analoga delarna fortsätter fungera, men processorn upphör att köra kod och prata med displayen. Så det är en rimlig test av hur mycket självstörning som förekommer, även om man egentligen skulle behöva upprepa testet vid många frekvenser över hela bandet för att vara helt säker.

Elektroniken byggdes in i en 3D-utskriven låda med display och processorkort närmast framsidan och RF-kortet på nivån bakom. I en mellannivå på sidan om klämde jag in ett batterifack byggt av kopparlaminat. Eftersom jag var tvungen att rita upp lådan i ett CAD-program inför 3D-utskriften lade jag in även övriga detaljer där och fick på så vis bra ritningsunderlag även för hur kopparlaminaten skulle sågas till. Figur 12 visar hur det hela ser ut innan botten skruvas på. Notera att en SMA-kontakt (högst upp i bilden) används för att koppla in antennen.

Figur 12. ROX-2X-kortet sitter en nivå ovanför/bakom processor och display.

Framsidan av mottagaren visas i figur 13. Övre raden i displayen visar dels batterinivån och dels RSSI-nivån via en stapel. Genom att dynamiskt skapa specialtecken i displayen kan man få fram de tecken man för tillfället behöver för att visa en viss batterinivå eller fylla ett visst antal pixlar i den högsta rutan i stapeldiagrammet. På undre raden visas aktuell frekvens samt numret på vilken av ett antal förprogrammerade frekvenser som för tillfället är vald. Om man inte tryckt på någon knapp eller vridit på frekvensratten på 10 sekunder så slutar processorn bry sig om ratten och de flesta knapparna och man kan inte ändra något. För att låsa upp måste man trycka på ”1”. I upplåst läge kan man justera frekvens med ratten, stega mellan vilken av siffrorna i frekvensinställningen man ska justera med ”1” respektive ”2”, skifta till nästa förprogrammerade frekvens med ”3” eller gå in i ett menysystem med lite fler funktioner genom att hålla inne ”4” i minst en sekund. I menysystemet kan man bland annat förprogrammera upp till 9 olika frekvenser och starta frekvenssvepen som diskuterats ovan. Under en normal tävling behöver man förhoppningsvis varken justera frekvensen eller alls titta på displayen, så det är bara volymkontrollen och hörlurarna som kommer till användning. Möjligen kan man vilja byta till beaconfrekvensen genom att trycka på ”3” när man tagit sista räven.

Figur 13. Lådan med mottagaren.

Ett par nackdelar med att bygga ut ROX-2X på det här sättet är att vikten blir lite högre (totalt knappt 600 g inklusive antenn), samt att strömförbrukningen blir mycket högre. Jag har mätt upp strax under 50 mA och med tanke på att 9-V-batterier verkar ha en kapacitet på ca 300 mAh vid denna last så kan man med marginal köra en tävling och kanske till och med två eller möjligen tre innan det är dags att byta. Att byta till ett laddningsbart LiPo-batteri med mycket högre kapacitet och kanske lägre vikt borde inte vara alltför svårt eftersom alla kretsar förutom nuvarande displayen borde kunna fungera på 3 V. Förutom displaybyte skulle det kräva att man lägger till en laddarkrets samt möblerar om runt en del spänningsregulatorer och vid volymkontrollen. Att ha två LiPo-batterier i serie för att få högre spänning skulle kräva mindre anpassning av elektroniken, men att laddningen blir lite mer komplicerad.

En mottagare är inte mycket att ha såvida man inte har en passande antenn. Om man letar runt lite på nätet kan man hitta flera måttritningar på olika varianter av pejlantenner för 2-m-bandet. Den vanligaste är nog en enkel Yagi med en direktor och en reflektor utöver den ”drivna” dipolen. En annan populär variant är HB9CV som har fördelen att bara ha två element och vara betydligt kortare, men nackdelarna att vara lite mer komplicerad med sina gamma-matcher och inte simuleringsbar med något program jag har tillgång till. Jag valde Yagi-spåret och efter att ha hittat flera olika förslag på mått så upptäckte jag programmet ”YagiCAD” där man kan simulera och optimera olika Yagi-konstruktioner. Efter en del optimerande från olika ursprungsdesigner fastnade jag för måtten i figur 14.

Figur 14. Måttritning för antennen. Spröten består av 25 mm brett stålmåttband; bommen av 16 mm elrör.

Enligt YagiCAD ska den ha strålningsdiagrammen i figur 15 vid 144.75 MHz och ett fram/bak-förhållande på över 40 dB.

Figur 15. Simulerade strålningsdiagram för antennen.

Spröten är tillverkade av 25 mm brett stålmåttband (köpt på Biltema) och som bom använde jag ett 16 mm elrör. För att fästa elementen konstruerade jag passande klämmor och skrev ut i PETG på en 3D-skrivare. Lådan för mottagaren fästs också med klämmor på bommen. För att få ett bra grepp när man håller antennen högt gjorde jag även ett handtag som fästs på bommen. Se figur 16 och 18.

Figur 16. Komplett pejlmottagare.

Antennen är ganska otymplig när man ska transportera mottagaren, så efter lite funderande konstruerade jag 3D-utskrivbara ”papiljotter” som man kan rulla upp spröten på. För att det ska fungera spetsas ändarna på spröten något och ett hål görs nära spetsen. De vassa kanterna oskadliggörs genom att strimlor av sporttejp tejpas över dem. Spetsen sticks in i mitten av papiljotten där det finns en M2.5-skruv som kan haka fast i hålet. Sedan är det bara att rulla ihop sprötet och avsluta med att sätta över en klämma som håller det hela på plats. Handtaget går också att vika åt sidan för att bli mindre skrymmande. Se figur 17 och 18.

Figur 17. Spetsat spröt med hål samt närbild på papiljotter.

Figur 18. Mottagaren hopfälld och klar för transport.

Modeller för de olika 3D-utskrivna delarna till antennen går att ladda ned från [8].

Enligt YagiCAD ska impedansen på antennen vara 33 – j5 Ω vid 145 MHz. Jag mätte upp den med en nätverksanalysator (VNWA) och fick den till 35 + j0 Ω (såklart lite beroende på antennens omgivning; i vissa positioner blev det nästan exakt 50 Ω). Eftersom jag mätte i änden på ca 6 cm koax (ca 0,2 ns) som vrider impedansen en aning medsols i Smith-diagrammet så är resultatet ännu närmare det teoretiska. Nästan lite för bra för att vara sant.

Jag mätte även inimpedansen i mottagaren och med hjälp av C3 kunde jag trimma den till 38 + j13 Ω. Av en lycklig slump (?) matchar alltså den resistiva delen nästan perfekt, medan det skulle behövas lite kapacitiv reaktans i serie med mottagarens ingång. -j13 Ω motsvaras vid 145 MHz av 84 pF. Jag hade 47 pF i lager och satte två sådana parallellt i serie med SMA-kontakten på ingången och hamnade på 32 – j0 Ω vid nästa mätning. Efter dessa övningar borde matchningen mellan antenn och mottagare vara utmärkt. Att den differentiella signalen från antennen skickas en kort bit i en koax utan föregående balun har förhoppningsvis inte alltför stor negativ inverkan.

Ännu har mottagaren inte testats i någon tävling, men preliminära tester jag gjort ser lovande ut. Jag satte i vardagsrummet, nära ett fönster, upp en enkel dipol som drivs av en signalgenerator som moduleras med lämpliga morse-signaler. Vid -10 dBm (0.1 mW) från generatorn så hörs sändaren tydligt på 500 m avstånd med nästan fri sikt. Inom 150 m hoppar audio-S-metern igång om man har volymen tillräckligt uppskruvad. Riktverkan och fram/back-förhållande upplever jag som goda. Som vanligt på 2-m-bandet så går det att hitta platser där signalen är betydligt svagare eller kommer från ett oväntat håll, men i den ganska snälla testmiljön blir det till allra största delen rättvisande bäringar och om mottagningen är konstig räcker det oftast att flytta sig en meter eller två. Jämfört med en annan, lånad, mottagare baserad på ROX-2X så upplever jag min som något mindre brusig (trots den digitala elektroniken) och lite mer känslig. Den bättre känsligheten tror jag beror på en mer omsorgsfull anpassning mellan antenn och mottagare och möjligen också på skillnader i antennkonstruktionen. Den stora skillnaden i användarupplevelse var dock att man slipper ratta på frekvensen för att hitta signalen.

Jag satte även upp en riktig 2-m-räv (gissningsvis 0,5 – 1 W) 500 m och 500 kHz från min svaga testsändare för att testa fallet att man befinner sig nära mål-beacon och vill pejla mot någon avlägsen räv. Med både min och den lånade mottagaren var jag tvungen att ta mig drygt 100 m från räven för att kunna pejla på ett meningsfullt sätt mot den svaga signalen. Det verkar alltså som att SA636 har rätt risiga blocker-prestanda och störs ut av en stark signal, även om den ligger 500 kHz från den frekvens som är inställd. Kanske en anledning att framöver konstruera en ny rävsax som baserar sig på en annan RF-kedja?

Referenser

[1] ”2m Receiver ROX-2X”, http://open-circuit.co.uk/wp/receivers/rox-2x/

[2] ”FoxScope, Rävsax för 80 m med dubbla mottagarkedjor”, QTC nummer 10 och 11, 2022.

[3] “Adafruit Si5351A Clock Generator Breakout Board”, https://www.adafruit.com/product/2045

[4] ”Arduino-Pico”, tillägg till Arduinomiljön för att programmera Raspberry Pi Pico, https://github.com/earlephilhower/arduino-pico

[5] ”LiquidCrystal”, bibliotek till Arduino, https://www.arduino.cc/reference/en/libraries/liquidcrystal/

[6] ”ROX-2X_Si5351”, programkod https://github.com/per-magnusson/ROX-2X_Si5351

[7] ”QDX – Digital Transceiver”, https://qrp-labs.com/qdx.html

[8] ”2 m Fox Hunting Antenna”, blog post with 3D models https://axotron.se/blog/2-m-fox-hunting-antenna/

Head lamp charging timer

This article describes a simple timer that disconnects a number of lithium ion batteries from their chargers after a predetermined time. The timer consists of a Teensy LC (Arduino compatible) processor board, a 2×16 alphanumeric LCD, MOSFETs to connect/disconnect the chargers and batteries and a few 3D-printed parts.

The timer with connections for four chargers and batteries.

Why it is needed

The youth section of my local orienteering club has a few head lamps to lend out during trainings to kids who do not have their own. The batteries need to be charged after each use, but it might be several days or even weeks before the leaders are back at the club house to disconnect the batteries from the chargers. There are reports of accidents where the chargers or batteries have caught fire in situations like this, so it would be good if the charging time could be limited.

The first idea was to put a timer on the mains supply to the chargers. After a quick investigation, I noticed that the chargers actually consume current from the batteries when they are not plugged into mains (an LED on the chargers is even lit in this case), so this was not a good solution as the batteries would be empty after a week or two in this state.

Instead I decided to build a timer that interrupts the DC connection between the chargers and batteries rather than the mains supply to the chargers.

Circuitry

As the processor board I decided to use a Teensy LC board, which is a pretty small and inexpensive “Arduino compatible” board (i.e. it can be programmed in the Arduino environment) with an ARM Cortex-M0+ processor. It has 27 I/O pins, which is more than enough for this simple application. The Arduino/Teensy environment provides useful and time-saving libraries for controlling the alphanumeric LCD, doing switch debouncing and keeping track of the time.

As the user interface, I decided to use a 2×16 LCD together with four push buttons. The LCD can be used in either 8-bit or 4-bit mode and I opted for the 4-bit mode since it requires fewer wires to be soldered between the Teensy and the LCD. The LCD also needs a potentiometer to control the contrast. Without that, or with the wrong setting of the pot, it will probably not show any characters at all.

I connected four momentary closing push buttons between pins on the Teensy and GND. By enabling internal pull-ups on these pins, no resistors are needed – reducing the amount of soldering required.

All of the pieces described so far are mounted to the back of a 3D-printed front panel as shown in the photo below.

Back side of the front panel with push button board (top), Teensy floating above the LCD and a USB connector (bottom left).

The USB signals are brought out to a separate mini-B connector mounted so that it is close to the side of the enclosure. The LCD is powered directly from the USB 5-V line. The 10-kohm potentiometer that provides the contrast signal to the LCD is connected between the LCD VCC pin (5 V) and GND. The push buttons are soldered to a scrap piece of perf board which is screwed to the front panel. Since the Teensy LC board is so small and lightweight, it works fine to just have it hanging in its short wires behind the LCD. An alternative would be to solder a few unused pins of the Teensy to the perf board.

The connection between the pieces are so few that they can rather easily be determined from the photo above. The code shown at the end of this post also defines which pins are used for what on the Teensy.

One pin of the Teensy controls the MOSFETs via the orange wire in the photo above. The MOSFETs are located on a separate piece of perf board, where most of the soldering in this project takes place:

The board with the six MOSFETs and associated circuitry.

I used MOSFETs I had on hand, but unfortunately, I did not have six identical ones, so one of six channels is equipped with a different kind of transistor. The schematic for each of the channels is shown below:

Schematic of one channel.

I did not want to control the MOSFET (M1) with the 3.3 V signal coming directly from the Teensy, since it might not turn on well enough with just 3.3 V. Instead I used a pull-up (R3) to the roughly 8-V voltage coming from the charger. When the NPN transistor Q1 is on, it pulls the gate of M1 low and thus turns it off and disconnects the battery from the charger. When the Teensy pulls the DISCONNECT signal low, the base current of Q1 (coming via R1) is diverted so that Q1 turns off and R3 can pull the gate of M1 high. Thus M1 turns on and connects the battery to the charger.

There is just a single DISCONNECT signal from the Teensy, connected to all the channels. The channels also share the same GND.

The Teensy is powered via a separate USB cable, so there could be a situation where the Teensy is unpowered, while there are chargers and batteries attached to the timer. I wanted the batteries to be disconnected from the chargers in this case and that is the reason for the diode D1. When the Teensy is unpowered, any of its pins will probably be about a diode drop (ESD diode) above GND if a small current is flowing into the pin. This might be a low enough voltage to prevent Q1 from turning on and M1 would be on, which I do not want. By adding the diode D1 in series with the DISCONNECT signal, we can be pretty sure Q1 will have enough base current to be on even when the Teensy is unpowered, and thus the batteries will be safely disconnected from the chargers in this case.

The MOSFETs I used (mostly STD35NF06T4) as M1 are vastly overkill in that they can handle much larger currents and much higher voltages than what will actually be needed here, so cheaper and smaller devices would work just as well. But I used what I had on hand that would fit the bill and I found no smaller suitable devices in the junk bin.

The processor connects to the MOSFET board with just a GND wire (black) and a DISCONNECT wire (orange).

To connect the chargers and batteries to the timer, I used extension cords that were supplied with the headlamps, but which are not really needed when the lamps are used for running at night. I cut these cables in half and soldered them to the MOSFET board. Unfortunately only four such cables were available, so I will have to add two more in the future to enable channels five and six.

Mechanics

As the main part of the enclosure, I used a CU-1874-B box from Bud Industries, but as mentioned above, I replaced the lid with a custom 3D-printed part I designed in Fusion 360. It has screw posts for the display and for the board with the switches, as well as rectangular walled holes for 3D printed buttons that push on the switches. The front also has some reinforcements to make it more sturdy, although it gets pretty sturdy anyway when the LCD and the switch board have been screwed into it.

Back side of front panel.

I designed the screw posts so that M2.5 (for the LCD) and M3 (for the switch board) screws could be self-threaded into them. The holes are square with sides of 2.1 mm for M2.5 and 2.6 mm for M3. The entrances of the holes are chamfered to make it easier to start the threads.

To mount the MOSFET board to the bottom of the box, I 3D-printed a frame (also with screw posts) that I then glued to the bottom of the box using CA glue. This way I did not have to drill any screw holes in either the front or the bottom of the box.

The frame for holding the MOSFET board.

I wanted to have proper strain reliefs on the cables and I 3D-printed those to fit all six cables at once. Again with square holes to allow M3 screws to self-tap into the material.

Strain relief for six cables.

Even though no screw holes were needed in the box, there still had to be holes for the cables and for the USB connector to power (and if necessary reprogram) the Teensy. Since I have a CNC machine, I used that to mill these holes. The cable holes could easily have been made with simpler tools, but the wall around the USB hole had to be thinned for a proper fit of the connector and this was easier done with the CNC than with alternative methods.

The box with holes.

Code

The program running in the Teensy is a simple Arduino “sketch”. The LiquidCrystal library is used for communication with the LCD and the Bounce2 library is used for button debouncing. Time is kept by an elapsedMillis timer, a variable that automatically increments every millisecond.

The setup() routine – which is automatically called once at the beginning of an Arduino sketch – defines a custom LCD character (a right-arrow, play-symbol) used on the start screen to refer to the button to press to start the charging. It then sets up the I/O lines used for the four buttons; start, stop, increment and decrement. The line for the DISCONNECT pin and the on-board LED are set up as outputs.

The loop() routine is automatically called over and over again, indefinitely, in an Arduino sketch. In this case, it contains a simple state machine that keeps track of whether the timer is running or if it is stopped. If it is stopped, it only reacts to the start (or “play” if you will) button. By default, it starts a 15 hour countdown when play is pressed.

If instead the timer is already running, it will react to either increment, decrement or stop. Stop is self explanatory, while increment and decrement increase or decrease the remaining time. Initially the steps are 1 hour, but when the remaining time is lower, the steps are reduced accordingly. A maximum time of 48 hours have been defined earlier in the program.

When the timer is running, the LCD shows the remaining time in the format HH:MM:SS and this is updated every second. The LED is also toggled every second, although this will not be visible to the user as it is embedded deep in the opaque box.

Finally the program contains a few functions to help with showing information on the LCD.

Here is the complete code:

/* Head lamp charge timer.

   Disconnects a charger from a (head lamp) (LiIon) battery after a given time
   to reduce the risks of having a charger connected for a long time to 
   a LiIon battery.

   The user interface consists of a 2x16 LCD panel and some buttons.

   Target: Teensy LC
   
   Written by Per Magnusson, http://www.axotron.se
   v 1.0 2021-01-08
   This program is public domain.
*/

#include <Arduino.h>
#include <LiquidCrystal.h>
#include <Bounce2.h>

static const int32_t DEFAULT_TIME = 15*60*60*1000; // In milliseconds
static const int32_t MAX_TIME     = 48*60*60*1000; // In milliseconds
static const int32_t TIME_STEP    =  1*60*60*1000; // In milliseconds
static const int32_t TIME_STEP2   =    10*60*1000; // In milliseconds
static const int32_t TIME_STEP3   =     1*60*1000; // In milliseconds
static const int32_t TIME_STEP4   =       10*1000; // In milliseconds


// Stated
static const int32_t STATE_OFF = 0; // Not charging
static const int32_t STATE_ON  = 1; // Charging


// Pins
static const int START_PIN = 2;       // Start timer (and turn charging on)
static const int STOP_PIN = 3;        // Stop timer (and turn charging off)
static const int INC_PIN = 4;         // Increment timer by 1 hour
static const int DEC_PIN = 5;         // Decrement timer by 1 hour
static const int DISCONNECT_PIN = 12; // High disconnects charger from battery

// LCD           RS  E D4 D5  D6  D7
LiquidCrystal lcd(6, 7, 8, 9, 10, 11);

static const int LED_PIN = 13;

elapsedMillis timer;
uint32_t end_time;
int32_t state;

Bounce b_start = Bounce();
Bounce b_stop = Bounce();
Bounce b_inc = Bounce();
Bounce b_dec = Bounce();

// Custom character
byte play_glyph[] = {
  B00000,
  B10000,
  B11000,
  B11100,
  B11110,
  B11100,
  B11000,
  B10000
};
const byte play_char = 0;



void setup() 
{
  Serial.begin(57600);
  lcd.begin(16, 2);
  lcd.createChar(play_char, play_glyph);
  
  b_start.attach(START_PIN, INPUT_PULLUP);
  b_stop.attach(STOP_PIN, INPUT_PULLUP);
  b_inc.attach(INC_PIN, INPUT_PULLUP);
  b_dec.attach(DEC_PIN, INPUT_PULLUP);
  b_start.interval(25);
  b_stop.interval(25);
  b_inc.interval(25);
  b_dec.interval(25);

  timer = 0;
  end_time = 0;
  state = STATE_OFF;

  pinMode(DISCONNECT_PIN, OUTPUT);
  digitalWrite(DISCONNECT_PIN, HIGH);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  lcd_splash();
  digitalWrite(LED_PIN, LOW);
  Serial.println("Charge timer");
}


void loop() 
{
  static uint32_t last_update = 0; // The last time the display was updated

  b_start.update();
  b_stop.update();
  b_inc.update();
  b_dec.update();
  
  if(state == STATE_OFF) {
    // Timer is off
    if(b_start.fell()) {
      // Start timer
      timer = 0;
      end_time = DEFAULT_TIME;
      state = STATE_ON;
      digitalWrite(DISCONNECT_PIN, LOW);
      digitalWrite(LED_PIN, LOW);
      lcd_update();
      last_update = 0;
    }
  } else {
    // Timer is on
    if(timer > end_time) {
      // Time has run out
      state = STATE_OFF;
      digitalWrite(DISCONNECT_PIN, HIGH);
      lcd_done();
    } else if(b_stop.fell()) {
      // The stop button was pressed
      state = STATE_OFF;
      digitalWrite(DISCONNECT_PIN, HIGH);
      lcd_stop();
    } else if(b_inc.fell()) {
      // Increment time
      if(end_time - timer < TIME_STEP3) {
        end_time += TIME_STEP4;
      } else if(end_time - timer < TIME_STEP2) {
        end_time += TIME_STEP3;
      } else if(end_time - timer < TIME_STEP) {
        end_time += TIME_STEP2;
      } else {
        end_time += TIME_STEP;
      }
      if(end_time > MAX_TIME) {
	      end_time = MAX_TIME;
        timer = 0;
        last_update = 0;
      }
      lcd_update();
    } else if(b_dec.fell()) {
      // Decrement time
      if(end_time - timer < TIME_STEP3 + 3 * TIME_STEP4) {
        end_time -= TIME_STEP4;
      } else if(end_time - timer < TIME_STEP2 + 5 * TIME_STEP3) {
        end_time -= TIME_STEP3;
      } else if(end_time - timer < TIME_STEP + 3 * TIME_STEP2) {
        end_time -= TIME_STEP2;
      } else {
        end_time -= TIME_STEP;
      }
      lcd_update();
    }
    if((state == STATE_ON) && (timer > last_update + 1000)) {
      lcd_update();
      last_update = (timer / 1000) * 1000;
      if(digitalRead(LED_PIN)) {
        digitalWrite(LED_PIN, LOW);
      } else {
        digitalWrite(LED_PIN, HIGH);
      }
    }
  }
}


// Draw the splash screen
void lcd_splash()
{
  lcd.clear();
  lcd.noCursor();
  lcd.noBlink();
  lcd.setCursor(0, 0);
  //         0123456789012345
  lcd.print(" Laddningstimer "); // Charging timer
  lcd.setCursor(0, 3);
  //         0123456789012345
  lcd.print("    Tryck "); // Press
  lcd.write(play_char);    // >
}


// Draw the screen showing that charging has finished
void lcd_done()
{
  lcd.clear();
  lcd.noCursor();
  lcd.noBlink();
  lcd.setCursor(0, 0);
  //         0123456789012345
  lcd.print("    Laddning    "); // Charging
  lcd.setCursor(0, 3);
  //         0123456789012345
  lcd.print("      klar      "); // done
}

// Draw the screen showing that charging has finished
void lcd_stop()
{
  lcd.clear();
  lcd.noCursor();
  lcd.noBlink();
  lcd.setCursor(0, 0);
  //         0123456789012345
  lcd.print("    Laddning    "); // Charging
  lcd.setCursor(0, 3);
  //         0123456789012345
  lcd.print("    avbruten    "); // interrupted
}

// Convert a positive number between 0 and 99 to a nul-terminated
// string with two digits. The first character is 0 for numbers below 10.
void int_to_00str(uint32_t num, char *str) {
  str[2] = '\0';
  str[1] = (num % 10) + '0';
  str[0] = (num - (num % 10))/10 + '0';
}


// Convert a positive number between 0 and 999 to a nul-terminated
// string with three digits. Leading zeros are replaced by spaces.
void int_to_3str(uint32_t num, char *str) {
  str[3] = '\0';
  str[2] = (num % 10) + '0';
  num -= (num%10);
  num /= 10;

  str[1] = ' ';
  if(num > 0) {
    str[1] = (num % 10) + '0';
    num -= (num%10);
    num /= 10;
  }

  str[0] = ' ';
  if(num > 0) {
    str[0] = (num % 10) + '0';
  }
}

// Update the countdown timer on the LCD
void lcd_update()
{
  uint32_t time_left;
  uint32_t seconds;
  uint32_t minutes;
  uint32_t hours;
  char sec_str[3];
  char min_str[3];
  char hour_str[4];

  time_left = end_time - timer; // ms
  if(time_left < 0) {
    time_left = 0;
  }
  time_left /= 1000; // s
  seconds = time_left % 60;
  time_left -= seconds;
  time_left /= 60; // minutes
  minutes = time_left % 60;
  time_left -= minutes;
  time_left /= 60; // hours
  hours = time_left;
  
  int_to_00str(seconds, sec_str);
  int_to_00str(minutes, min_str);
  int_to_3str(hours, hour_str);

  lcd.setCursor(0, 0);
  //         0123456789012345
  lcd.print("    Tid kvar    ");
  lcd.setCursor(0, 1);
  //         01234567890123456789
  lcd.print("   ");
  lcd.print(hour_str);
  lcd.print(":");
  lcd.print(min_str);
  lcd.print(":");
  lcd.print(sec_str);
  lcd.print("    ");
}

Adding Arduino libraries manually

I am writing some code for a Teensy (which is a much higher performance alternative to e.g. an Arduino Nano) and found a non-standard library that I would like to use. This post is primarily a short reminder to myself  on how to install an Arduino library manually. There are lengthier and more complete instructions here.

Basically, just put the library code in a suitably (but quite arbitrarily) named folder under Documents\Arduino\libraries, which in my case is C:\Users\Per\Documents\Arduino\libraries.

The folder name should probably not have spaces, but otherwise the name does not seem to matter. The Arduino/Teensyduino environment searches these catalogs for files included with #include <MyLibrary.h>  syntax (less-than, greater-than rather than citation marks around the file name) and automagically compiles other files in the same directory as necessary.