Welcome to the new FlexRadio Community! Please review the new Community Rules and other important new Community information on the Message Board.
If you are having a problem, please refer to the product documentation or check the Help Center for known solutions.
Need technical support from FlexRadio? It's as simple as Creating a HelpDesk ticket.

Magnetic Loop Controller

Steve - KD8QWT
edited August 2019 in SmartSDR API
I built a transmitting magnetic loop several years ago.  I used an Arduino UNO with an ethernet shield to control the 25 turn vacuum variable capacitor.  I wrote a piece of VB code that interfaced with HDSDR to get the VFO and send the appropriate stepper position to the Arduino via TCP.  When using my Kenwood TS-530s I had to use my VB code to manually enter the VFO.  It worked, but not as well as I would have liked.

Enter client/server radio.  I bought my 6300 a couple months ago and decided to update my controller.  I had already decided to upgrade to the Arduino MEGA 2560 several months before because I had to remove some important pieces due to the limited space on the UNO.  I also 3D printed the vacuum variable mounts and switched to a geared stepper and accessory shaft to determine the starting point of the cap.  It was much better than aluminum angle and tie straps.  After looking at the API and playing around with telnet sessions to the 6300, I figured it wouldn't be too difficult to eliminate my PC software and move it directly to Arduino and have it follow the 6300.  I set it up so that it only follows the ANT1 antenna, and only if the slice is set to TX.  A simple subscribe to the the slice events gave me what I needed.  I initially had trouble reliably parsing the incoming text from the 6300 until I dumped everything to a FIFO queue first, then process the queue later.  It has been sitting on my desk for a couple weeks with the latest updates, following the 6300 as I follow the stepper DIR on my scope.  Recent non-stop rain has prevented it's deployment, not to mention the constant tweaks that I can't ever seem to be "done" with.  At this point I think it's pretty solid, although I can still think of several things I could add.  At some point I have to plug it back in.  The further enhancements I want to make will have to wait until I change the platform to a raspberry pi and reuse my UNO for stepper control.

I want a built-in tune function.  I don't think the Arduino has enough power to read the SWR in real time, so the pi will do that.  Since I'm using a mapping of frequency to stepper position, this tends to drift slightly with various conditions, such as weather and even objects near the antenna.  Frequencies in between the mapping points have their stepper positions calculated.  I can still use my VB software to manually tweak the position for a lower SWR, but I don't want to rely on any desktop software.  The pi will include a tune function that will adjust the cap to the lowest SWR when the TUNE button is pressed in SSDR.  When the tune power is 2 watts it will tune the slice specific frequency.  When it's set to 1 watt, the controller will change the frequency on the Flex to each map point and determine the lowest SWR and record the stepper position.  I haven't figured this part out yet, but I'm sure it's possible. 

With HDSDR the controller worked perfectly.  I could click a few bands over and watch the bump in the noise floor walk across the band to the set frequency.  Based on my testing it works just as well, except it won't matter what client I use.  It can be SSDR, or one of the excellent clients I've seen people working on in the forums.  Please don't forget to add the TUNE button to those custom clients.  When I get more antennas, I may add an automatic switcher and switch based on frequency and antenna port.  Perhaps something like switching to the APRS frequency on 30m will automatically switch in the dedicated 30m receive loop.  No need to limit to just switching based on band.

So here's my loop controller code for the Arduino.  It's not pretty and was never intended to be seen by anyone, but it may prove useful for someone that just wants to setup something simple that will follow their Flex 6000 SDR.  It was easier than I thought, but the limitations of the platform does present some challenges.




  • L.Kubis
    L.Kubis Member ✭✭
    edited August 2019
    Have a look at Loftur's site --

    Loftur also runs a Yahoo Group site where he publishes more data on his controller.


  • Steve - KD8QWT
    edited June 2015
    I've seen that site before.  Very cool controller, but I had very different design goals.  I wanted off the shelf components with no circuit boards to make.  I wanted it remotely controlled via ethernet, sending simple strings via TCP.  I wanted to use an Arduino because I was looking for a project to use one.  I also did't want any knobs or buttons.  In retrospect I should have built a "knob" to move the tune point a few hundred hz to either side the center frequency.  The tighter the tune the better the noise reduction by de-tuning it slightly.  The plan was to add that to VB code, but I may build an accessory control unit that can do that.

    There are many many different mag loop controllers out there.  In the end I wasn't drawn to any of them and just preferred to build my own.  Sometimes the journey is more enjoyable than actually reaching the destination.  Just have to keep adjusting the destination.

  • Steve - KD8QWT
    edited June 2015
    An unforeseen benefit to my original design was the ease of using the Flex as the server.  The controller is still a server to telnet while using the Flex as a server for the info I needed for the loop.  It only cost me time to add the code.
  • VA3AE
    VA3AE Member ✭✭
    edited January 2016
    Steve, did you ever implement the PI for the built-in tune function?
    I've built a couple of Loftur's controllers and integrated a bank of relays to switch the control lines. 

    His is a great little design and works fine with the traditional Icom transceiver. There is an swr/pwr meter tap in the coax, reporting back to the controller, which is redundant with the flex, especially with two antennas.  I've played around with a couple of ethernet shields and an outboard arduino using Enzo's work as a starting point, but it is a bit too much of a kludge; now it's time to build a Flex specific solution.

    As winter approaches, I would like to use the extra indoor time to do what you described.  I have two loops, (160-30M / 20M-6M) and want them both to track, according to their designed frequency range. I think a crossover frequency, and having the correct ant selected in the slice, toggles the relay between steppers.  Multiple slices and stuff is too confusing for me just yet... maybe that will be easy, maybe not.

    As you point out, I also notice the loop resonance drifts a few Kc over time + temp, so a quick re-tuning regime would be a dandy thing.

    If I'm thinking about this correctly, an initial config routine would build/fill a look up table for the relative positions of the steppers on ant1/2 with tune updating the table on demand. 
    I haven't fully fleshed out the polling; I think that would require a bit of trial and error work to come up with something responsive but not too chatty.
    If the steppers and tuning response is fairly linear, the flex frequency info would continue to track/drive the steppers through the no transmitting spectrum.  It will be interesting to see how accurate and usable this would be. I've already built and use a PA0RDT active antenna on RXa so as to see the band and then drive the loop(s) to points of interest manually, so out of band accuracy isn't a show stopper.

    73 de Dave, VA3AE

  • Steve - KD8QWT
    edited November 2015
    Unfortunately I've not had the time to do much with this project.  All I was able to accomplish was to use the existing Arduino Mega and have it follow the Flex slice with the transmit enabled for the antenna port I was watching.  At that point I had already started my satellite rotator project and didn't want to be working on two development projects at the same time.  I decided at the time to stick with Arduino Mega rather than the pi, but I wasn't having much luck with it keeping up with the ethernet packets coming from the Flex.  It just wasn't as reliable as I had hoped and have since been relying on manual positioning via telnet.  Once the rotators are complete and outside rotating I'm going to jump back on this project.  I may have to try the pi or even switch to a Due.  It will be a good winter project and can't wait to really get into it.

    Yes, an initial config containing a position to frequency map works quite well.  I have used this for a few years.  Unfortunately the tuning is not linear.  The best bet is to have specific map points at frequencies you use often, then rely on calculations to figure out the position based on the the next highest and next lowest mapped position.  It's not linear, but it can be linear enough, especially if you have several map points within the band.  Just using a map and calculating the in between positions has proven to be quite accurate.  Out of band accuracy can be useful for short wave listening.  It can make a huge difference on receive, especially if you can manually drift the tune point around.  Where mappings prove problematic is when the Q starts to become extremely narrow.  With my loop on 80m for example, just a few steps will jump the swr from excellent to unusable on transmit. 

    For 2 steppers, I don't think I would use a relay to switch power between the steppers.  It would be better to use 2 separate stepper drivers and drive them off different pins.  A good driver will allow you to power down the stepper to save power.  You could even have them both run on automatic based on their frequency ranges and use a remote switch to switch the coax between the two antennas.  Maintain 2 sets of maps, one for each antenna.  Subscribing to the slice events will send notifications when something changes, like the frequency the slice is set to.  Then position each stepper based on it's mapping, assuming the frequency is inside it's range. 

    I think the reliability issues i had was due to a flaky router I had at the time.  When it worked it was awesome.  Once I get it solid, I'll start watching the SWR and see if the Mega has enough horsepower to keep up.  If not, then the Due will prob be plan B.  Plan C would be a pi.  Right now I'm Flexless because I'm waiting on the 6500 I traded up to.  Then I'll have 4 slices I can watch.
  • W9OY
    W9OY Member ✭✭
    edited November 2015
    Nice article!

    73  W9OY
  • IW7DMH, Enzo
    IW7DMH, Enzo Member ✭✭
    edited December 2016
    Hello Steve,

    you did a great job with your controller code! I just want to confirm that the Due board has enough power to read the SWR in real time and provide immediate feedback to your controller.
    From your first post, six months ago, I made a lot of improvements to my Due libraries: once you have an instance of you rig you can use one of the exposed methods in the FlexRig class. You can read the following meters as well:


    They are stored in a structure array calculated about 10 times per second so I think it can be used in your project.
    I use these meters in my controller Gui code and, as you can see from youtube videos, meter bars are very reliable and responsive.

    If you like to test or inspect in depth my Due libraries I'll be glad to help you.

    73' Enzo


        void set_headphone_gain(int val);   
        void set_sidetone_onoff(int value);
        void set_breakin_onoff(int value);
        void set_iambic_onoff(int value);
        void set_vox_onoff(int value);
        void set_dexp_onoff(int value);
        void set_proc_onoff(int value);
        void set_monitor_onoff(int value);   
        void setRxAntenna(int sliceId, String value);
        void setTxAntenna(int sliceId, String value);
        void setPreampGain(int panId, int value);
        void setTxEqMode(int value);
        void setRxEqMode(int value);   
        void setBand(int panId,int value);
        void setMode(int sliceId,String value);   
        void setTx(int sliceId,int active);
        void setActiveSlice(int sliceId,int active);
        void removeSlice(int sliceId);
        void createSlice(float freq,String antenna,String mode);
        void removePanadapter(int panId);
        void createPanadapter(float freq);
        void setFreq(int sliceId, int freq);   
        void setAgcMode(int sliceId, String value);   
        void setDisplayPanWeightAverage(int panId,int value);
        void setPanafallAutoBlack(int panFallId,int value);   
        void setNr(int sliceId,int active);
        void setWnb(int sliceId,int active);
        void setApf(int sliceId,int active);
        void setAnf(int sliceId,int active);   
        void setRit(int sliceId,int active);
        void setXit(int sliceId,int active);   
        void setAudioMute(int sliceId,int active);   
        void setAgcOffLevel(int sliceId,int active);
        void setAgcThreshold(int sliceId,int active);   
        void setLineoutMute(int active);
        void setHeadphoneMute(int active);   
        void setBandWidth(int sliceId,int low,int high);   
        //Encoders methods
        void setWnbLevel(int sliceId,int value);
        void setNrLevel(int sliceId,int value);
        void setApfLevel(int sliceId,int value);
        void setAnfLevel(int sliceId,int value);
        void setPanAverage(int panId,int value);
        void setRitFreq(int sliceId,int value);
        void setXitFreq(int sliceId,int value);
        void setPanFps(int panId,int value);
        void setRxFiltLow(int sliceId,int value);
        void setRxFiltShift(int sliceId,int value);
        void setWatBlackLevel(int watId,int value);
        void setRxFiltHigh(int sliceId,int value);
        void setRxFiltWidth(int sliceId,int value);
        void setWatGradientId(int watId,int value);
        void setAudioGain(int sliceId,int value);
        void setAudioPan(int sliceId,int value);
        void setWatLineDuration(int watId,int value);   
        void setWatColorGain(int watId,int value);   
        void setCwSpeed(int value);
        void setCwPitch(int value);
        void setCwBreakinInDelay(int value);
        void setCwMonitorGain(int value);
        void setCwMonitorPan(int value);   
        void setMicLevel(int value);
        void setSbMonitorGain(int value);
        void setCompanderLevel(int value);
        void setVoxLevel(int value);
        void setVoxDelay(int value);
        void setAmCarrier(int value);
        void setTransmitLow(int value);
        void setTransmitHigh(int value);
        void setPanBandwidth(int panId,float value);
        void setLineoutGain(int value);
        void setHeadphoneGain(int value);
        void setRfPower(int value);
        void setTunePower(int value);
        void setPanMinDbm(int panId,float value);
        void setPanMaxDbm(int panId,float value);   
        void setPanCenter(int panId,float value);   
        void setEqControl(String type,String freq,int value);
        void setMoxState(int value);
        void setTuneState(int value);
  • Steve - KD8QWT
    edited November 2015
    Thanks Enzo.  I figured the Due board has enough power to do what I want just based on your controller.  I planned on heavily relying on your excellent libraries.

    I actually bought 2 Due boards a month ago.  The second one is being used by my satellite rotator project.  I'm still Flexless as I'm waiting for the 6500 I traded up to, so I'm hoping to jump back into this in December.

    73' Steve
  • VA3AE
    VA3AE Member ✭✭
    edited January 2017
    Hi Steve et al. 
    I thought about things after all the helpful responses, and here is what I am hoping to "due".

    I've ordered and just received my Arduino due.  I've also got http://shop.protoneer.co.nz/  the protoneer shield for the 4988s.  It has room for four pololu 4988 carriers. https://www.pololu.com/product/1182 

    I believe I saw in your code you used a Big Easy, but IIUC there isn't' any difference WRT your code.  Since I want to control two loops, one on ANT1 and the other an ANT2, this multi GRBL style shield is dandy.  I also have wiznet shields, so I can get started right away.
    If this all goes well, I might try getting a board(s) made up through oshpark or dirtypcbs. 

    I've been asked by a friend to cad up another project in eagle and we are going to use an arduino  reflow-toaster to produce a run of the design, so we can potentially **** two stones with one bird.... yes it is probably unnecessary but I like to dream.

    I've been thinking about how to get the SWR. 

    From what Enzo said, the radio's delivery of the metering packets is around 10/sec. I hope this is sufficient compute the SWR on the fly.  I have a couple of bridges made up from Loftur's <TF3LJ> loop controller, so we can always compare the performance if needed.

    I'd like to begin to test.  Any suggestions?

  • IW7DMH, Enzo
    IW7DMH, Enzo Member ✭✭
    edited December 2016
    Hello Dave,

    I can answer from the code perspective only.

    If I have understood the logic of your project you could use the libraries for a pseudo-auto-tune function like this. (it is just an idea)

    int previousTunePower=fRig.transmit.tunepower;   //read the actual tune power level
    fRig.setTunePower(3);     //I suppose you just want to use 3 watts for tuning

    // here you could apply a logic to restore spepper position
    // reading current active slice frequency fRig.slice[fRig.activeSlice].RF_frequency (in my lib frequency is an integer)
    // and selected tx antenna using  fRig.slice[fRig.activeSlice].txant (equals "ANT1" or "ANT2")

    fRig.setTuneState(1);     //start transmitting using the TUNE function
    float swr=fRig.metersValue[MET_SWR];   //read the actualSWR value
    while (swr>1.5) {

        // do your stuff here with your stepper driver board   


        //use a timeout logic to stop transmission after a long interval
        //that you have to evaluate doing some tests

    fRig.setTuneState(0);     //stop TUNE function
    fRig.setTunePower(previousTunePower);   //Restore previous tune power level

    I could bet you will have a bottleneck in stepper motion speed than in read-swr response time. Flex Ethernet protocol is really really fast.

    Good luck

  • VA3AE
    VA3AE Member ✭✭
    edited January 2016
    Enzo, you have understood very well.
    I will **** a sketch together and give this a try when I get home tonight.
    grazie mille,

  • VA3AE
    VA3AE Member ✭✭
    edited January 2016
    Hi Enzo, I managed to get the due to read the radio and I put a few things in the serial monitor and trace them as I dialed around.  
    You are correct, the radio provides data quickly.  There should be no difficulty getting the data, I just have to make sure to free up enough cycles for the due to keep up!  
    I haven't used the numbers to drive the steppers, but that will happen in good time.

    Thanks for doing all the heavy lifting.

    One odd thing I don't yet understand: when I start the program on the due, I can't connect if I am already viewing the Flex with SSDR.  I disconnect the GUI, re-cycle the due and I connect.  Then I reattach the GUI and all is well.  
    It is no doubt something I've overlooked.

    BTW, the Due Ethernet library in 1.6.6 is broken, and after some digging I discovered I had to go to github and check out the latest version of the lib. (1.1.1)
    After that, compiles went well.

    Loftur has some lovely code to calculate the best position for the SWR, as well as storing a position/frequency  table.  Desole, he did it in the Teensy3.2 which has on board  EEprom! Maybe I will use the SD card on the ethernet shield as Steve did.... 

    Full disclosure: I am a poor programmer but an adequate technician..  I can feel around in the dark and find my way through the code, but no deep and elegant ideas will be emitted from this location.  I will try to offset that with willingness to experiment and provide feedback.
    Bye for now,

  • IW7DMH, Enzo
    IW7DMH, Enzo Member ✭✭
    edited December 2016
    Hello Dave,
    One odd thing I don't yet understand: when I start the program on the due, I can't connect if I am already viewing the Flex with SSDR.  I disconnect the GUI, re-cycle the due and I connect.  Then I reattach the GUI and all is well.  
    It is no doubt something I've overlooked.
    I suppose you only need to reset the due board. The library only needs a Flex Signature connected to the network. An already running instance of SSDR isn't required but, of course, you need a client to change band and/or frequency.
    BTW, the Due Ethernet library in 1.6.6 is broken, and after some digging I discovered I had to go to github and check out the latest version of the lib. (1.1.1)
    After that, compiles went well.
    Which version of IDE are you using?

    Going back to auto-tuner feature of you project, I would suggest you to adopt another strategy: as loops have very stable response (read swr), also with bad weather conditions,  you could check the swr for the whole band using an antenna analyzer. Then you could store the better stepper position and recall it each time you read a new frequency from the Flex. Just like this italian brand of magnetic loop antennas http://www.ciromazzoni.com/#!loop-antenna/cfvg
    You could use the SWR meter for protection purpose only: like disabling transmission or reducing power.
    For storage purpose you have to use a SD card beacuse the Due processor doesn't have internal EEPROM.

    73' Enzo

  • VA3AE
    VA3AE Member ✭✭
    edited February 2016
    Hi Enzo!
    I've managed to clear some other work out of the way and I have come back to the Flex-DUE project.
    Perhaps you can help me sort out a few things...
    I am able to poll the radio for various parameters!!! 
    There is an odd behaviour.
    Arduino IDE Version 1.6.7
    Your Libraries Ver 1.5
    I can only connect if the connect, if the Nickname and Callsign text boxes contain a maximum of 4 characters each.  Otherwise, I never get to the point where it returns "Connected"

    I traced the interesting stanza to FlexRig.cpp Line 528.   
    If I I let "i" be a larger value, (164 for example) I can get more on the same line, but execution seems to be unreliable ... which makes sense,
    Here's a cut and paste of how it looks with the characters flex and VA3AE entered into the radio setup text boxes.
    73 de Dave
    ==========Flex Rig=============
    IP Address:
    Model Name:FLEX-6500
    NickName:=flex callsign=VA3
    connecting to Flex rig:
    ==VERSION (connect)==
    ==HANDLE (connect)==
    C0|sub tx all
    C1|sub atu all
    C2|sub meter all
  • IW7DMH, Enzo
    IW7DMH, Enzo Member ✭✭
    edited December 2016
    If I I let "i" be a larger value, (164 for example) I can get more on the same line, but execution seems to be unreliable ... which makes sense,
    It is a not the safe way to go over this issue. In this way you most probably write some bytes over the array limit and this exaplain the bad behaviour you have experienced int the next execution.
    Data structures are defined in the FlexLib.h to store the maximun character number you can find in a UDP discovering packet.
    See the lines from 72 to 75.
    char modelName[36];      //Model Name
    char serial[36];         //Serial Number
    char nickName[36];       //Nick Name
    char softVersion[32];    //Software Version
    char handle[8];          //Handle assigned to the client

    I suggest you to sniff the UDP discover packet using Wireshark or simply uncommenting some Serial.println( statements in the :findAFlex() method.

    I suppose there is a difference in the starting offset I am using in the code because in my setup I have more than 4 characters in the NickName and Callign fields. 

    Let me know.

    73' Enzo
  • VA3AE
    VA3AE Member ✭✭
    edited February 2016
    Hi Enzo, I agree and I wasn't going to permanently change the offset there,  I might be pulling an important keystone out of your archway!

    I just saw the line and confirmed by altering the offset that was where you were reading/putting data I was interested in.

    Yes, Flexlib.h:72-75 was where I first looked, and I tried changing the length of the char variables and recompiling but i didn't' see any change or improvement, and I was a bit confused; then I started poking around further afield.

    I have also twice tried to start fresh to make sure I hadn't inadvertently buggered something up.  I have removed/deleted all the libs and references, started with a clean untar of the libs and wrote a short test with FindaFlex.  As long as I keep the nickname and callsign variables under 8 characters total, I get the exact same repeatable behaviour. 
    I am testing with these variables: (slightly formatted as it wan't pasting well in this page)
    // Serial.print("The Transmit Antenna Port is: ");Serial.println(fRig.slice[i].txant);      
    // Serial.print("The Receive Antenna Port is: ");Serial.println(fRig.slice[i].rxant);
    // Serial.print("The S-Meter Reading is: ");
    Serial.print(fRig.metersValue[MET_S_A]);Serial.println(" dBm");
    //Serial.print("The Frequency is: ");Serial.println(fRig.slice[i].RF_frequency);
    //Serial.print("The SWR is: ");Serial.println(fRig.metersValue[MET_SWR]);  

    I also observe some random characters in the beginning/top of the output and sometimes with the data I'm retrieving.  I have one DUE and two wiznet51xx's which I have swapped to see if it was a hardware thing... it doesn't change with winet51xx's.  

    <Tangent> I also have a few Teensy3.x, which I might even test as it could run your code for us, since it has the MK20DX256VLH7 Cortex-M4 32bit processor.  Less memory but some eeprom..... might work in my application, but probably not your remote head.

    Could there be some subtle differences in the flexlib API 1.6.x?  I have been running that since it came out.
    I'll set a couple of hours aside this week to delve into this a bit more.
    Enzo, Thanks very much for your support in my tinkering,


  • IW7DMH, Enzo
    IW7DMH, Enzo Member ✭✭
    edited December 2016
    Ok Dave,

    in my projects I have used only original Arduino Ethernet shield. Using wiznet shield caused me only headcaches.
    Going back to the issue I suggest you to sniff the UDP packet using Wireshark.
    Other friends are using the 6500 and the 1.6 with no problems at all.

  • VA3AE
    VA3AE Member ✭✭
    edited February 2016

    I wanted to mention that the method for tuning the antenna is evolving.

    At this time, I am writing a routine that is an initial calibration setup.
    It measures and adjusts the antenna for best SWR at as many frequencies as is reasonable in each band/antenna combination, The defaults, such as Radio Serial #, TX_antenna for upper lower ranges,  values get entered in a config.h

    The DUE tests and records those TX_ANTx/Frequency/stepper-count values in a lookup table.
    Next, when I change frequency and request the radio to TUNE, the DUE gets the frequency and TX_ANTx, drives the stepper to that previously recorded value, and confirms with the radio that we are using the correct transmit antenna. 
    Then I invoke tf3lj/Loftur's proven method "find_best_swr"
    Find Best SWR - by sampling, summing and squaring a number of SWR values and comparing with a previous similar sum-square, advancing one sample at a time. once this sum-square value is larger than the previous one, and midpoint SWR (SWR value measured at sample_buffer_size/2 samples earlier) is at an acceptable level, then return TRUE, else return FALSE 
    Relies on sample[] being seeded with 999 values before initial SWR search.
    The new value is for that TX_ANTx/Frequency/stepper-count is updated in the lookup table.   If the Frequency is not in a band I can transmit on, I will do a best guess or perhaps elect to use my Flexcontrol Knob to manually drive the steppers... haven't decided on the manual control other than in the serial console program yet.

    It is initially for my loops, but I see **** this for screwdriver antennas and even a long-wire tuner, or other exotic things....

    Hope this helps,

Leave a Comment

Rich Text Editor. To edit a paragraph's style, hit tab to get to the paragraph menu. From there you will be able to pick one style. Nothing defaults to paragraph. An inline formatting menu will show up when you select text. Hit tab to get into that menu. Some elements, such as rich link embeds, images, loading indicators, and error messages may get inserted into the editor. You may navigate to these using the arrow keys inside of the editor and delete them with the delete or backspace key.