< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Sunday, April 12, 2015

Quick and dirty way to measure inductance and capacitance with an arduino uno or any other micros

Measuring inductance and capacitance are not among the commonly available options in a simple multi-meter.  But often we need to measure an unknown capacitor or a hand made coil, for e.g while building oscillators and filters for qrp (radio transmitter) projects. Here is a collection of simple techniques which can be used to accurately (all-most as good as expensive tools) measure the values for hf (high frequency)  applications. But i am not going to talk about a net work analyzer here. This is a very quick and dirty one based on arduino uno.

A small rf coil (2.5-3.5micro henry)

Measuring Capacitance


This is the easiest and you can measure capacitance from 1pf to 100 micro farad by using just an ardiuno uno. The details of the project and the principle of the measurement technique are explained by Jon in his page here. It doesnt use any additional hardware, just upload the arduino sketch (which can be found here or here) and place the unknown capacitor to the analogue pin A0 and A2 of the arduino, and just read the capacitance value! Even without any calibration, it gave me accurate results :)

So the set up is as shown in figure:

Measuring Capacitance with an arduino uno

Measuring Inductance

To measure inductance we need some more componets from the junkbox. I just hooked them up on a breadboard and it worked like a charm! The concept is explained by Kerry (here). I didnt have a comparator as he used, so i used LM319 which has a slightly different pin out (Be careful as my chip heated up with a wrong connection by thinking it as LM339!)

Schematic for inductance measurement
In this case, the unknown inductor forms an lc circuit together with a 0.01 micro-farad capacitor which forms a resonance frequency, for e.g with 2 micro henry, it will resonate around 1.12 mega hertz ( A good online calculator here) and the frequency  can be measured  by  arduino uno with a frequency counter library which can be downloaded here. After installing the library by unzipping to arduino / library folder, just restart the software. Now there will be a serial example which can be loaded in to the arduino and open the serial console to read the frequency. This works fine up to 8 MHZ. The measurement is from the Digital pin 5 of the uno.

Here i used the online calculate described above to calculate the inductance from frequency and capacitance value (0.01 micro farad). It is possible to improve it for direct read out and can read more about it on Kerrys pages.

Here is my simple hookups on a breadboard and it worked!! Ideally it can be constructed on a copper-clad in a Manhattan style.

Inductance measurement



Thursday, April 2, 2015

A simple standalone antenna analyzer based on arduino and ad9850 with ili9341tft

A simple SWR analyzer with ili9341tft and ad9850 DDS

A simple antenna analyzer is a helpful gadget which will tell us about the frequencies to which a piece of wire will resonate on.  This is very useful in conjunction with a simple tuner to adjust the antenna for an optimum VSWR (voltage standing waves ratio). Usually, a good commercial antenna analyzer is an expensive device which we use once in a while. Here is a simple project which will give a rough estimation of VSWR and resonant frequencies of a random piece of wires, antennas and lc networks. For the critical reader, there is a lot of limitations within this simple design and can read more about various possibilities here (keywords: harmonics, directional coupler, superheterodyne).

This project uses a simple ad9850 module as its heart to generate the rf signals for the sweep. The controller is an Arduino pro mini (with an atmega328 running on 3.3volt) and for display, it uses an ili9341 tft.

The core circuitry for the analyzer is very simple and is based on the design from Beriks (K6BEZ) simple antenna analyzer (pdf). I added a buffer with the DDS generator (as shown on W5DOR).

To control the whole circuitry, I added an IR receiver which is useful for inputting frequencies, setting up the scan, span, etc. (basics on ir library). There is an updated version which uses a rotary encoder at the bottom of the page [Rotary Encoder]

So currently the firmware supports three things, a simple DDS VFO, a VSWR plot with a sweep from 1-30mhz and can be adjusted to smaller regions for e.g 7.000MHZ to 7.500 MHZ.

VSWR plot from Arduino antenna analyzer on an ili9341tft



There is a quick band scanner which scans all amateur radio bands and locates the best band which suits the antenna.


Scanning the bands
Simple dds function can be used to act as a signal generator for testing
Arduino DDS menu
Other future possibilities are to generate wspr signals. The circuitry is modular so that boards can be swapped to add extra functions and hence reusing the dds and TFT display.

Here is a simple video of the analyzer connected to a parallel lc circuit with a 50ohm carbon resistor to use it as a simple scalar network analyzer (filter around 7mhz)



Here is some more pictures to show the constructions

DDS and controller together with display

buffer, opamp, and detectors

building blocks

PC interface

Componets/modules

Finished assembly
Firmware is still basic and needs improvements. It can be downloaded from github. If you are building, the best way is to start in different stages as it involves several libraries. I would recommend the following sequence. It is a bit difficult for a beginner to get all this running at the first place. I will update a better writing at a later point with a compiled firmware so that it will be easier for a less experienced one to build it and calibrate.


  1. Get the Arduino and LCD communicate and display the basic test patterns. See this post on getting the ili9341 up and running. It basically uses a simple library for drawing on the TFT and I modified it a bit to get the correct display orientation (So use the ili9341 library here)
  2. Test the ad9850 module and the Arduino to work together: See this post.
  3. Now get the Timed action, Infrared Remote and ad9850 libraries.
  4. Assemble the SWR bridge board with buffer and adjust the potentiometer on the forward power to get a voltage output in the ADC range (0-3.3v). Set the DDS module to a fixed frequency and do the measurements (for e.g set 10mhz ) and do the same with a sweep. You could use the k6bez antenna analyzer script to test it first, which has the necessary functions to test and calibrate the circuit. See the link in the reference below (Note: adjust the sketch to match the pins (2,7,9,8)of your DDS module)
  5. Upload the script and open a serial console. Keep pressing a remote control (any make) and assign the keys to different function by copying the number shown for specific buttons to the relevant areas in the sketch.


A quick and dirty schematic (hand drawn) is attached below. It is basically derived from reference [1] and [3]. The construction used some simple pro-typing boards. RF sections could be better constructed on a copper clad in Manhattan style. Here I used a simple stripboard and attached a ground plane with a conductive tape (separated by a plastic film).

Crude Schematic for the analyzer. This is my first version and used an IR remote. The code is at https://raw.githubusercontent.com/riyas-org/swranalyser/master/antenna_light_tft/antenna_light_tft.ino
ALWAYS FOLLOW THE PIN NUMBER USED IN THE SKETCH as there are some typos and errors.
Rotary Encoder

Here is a simple version of the VSWR analyzer using a rotary encoder with a click. The connection is simple. Connect the center pin and one side of the click (button) to the ground. The other end of the click switch is connected to A0 pin and a library is used to detect single and double clicks (Onebutton). The other two pins on the encoder (of the three) are connected to pin 2 and 3 of the Arduino (interrupt). There is a slight change in the wiring for ad9850 DDS module which is connected to pin 9,8,7 and 10. Backlight on the TFT is directly connected to 3.3v via a current limiting resistor.

See this post for testing rotary and TFT - Adding Rotary encoder to arduino projects- quick start


Connections

Attached a simple hand drawn block schematic for several modules. The VSWR bridge circuitry is same as k6bez. Alternatively, an ad8307 based rf sense circuitry can be used. Ensure that the output of the rf sense is adjusted to fall within the ADC range (here 0-3.3v). A 5v design needs a level shifter for the TFT module.


Block schematic for the standalone analyzer with rotary encoder and Bluetooth serial (click to enlarge) Always double check the wiring as sometimes errors creep in. See the comments in the Arduino sketch to get an idea on which pin gets connected 
Additional Notes:

1) If running with a pro-mini on 3.3 volts, make sure that the fwd and rev voltages from detector fall wit in the ADC range (0-3.3volt)

2) Buffer amplifier design is actually for 12 volts. I was running it at 5.5 volts  (make sure it is 5.5 with a multimeter, do not trust the wall wart) and got good results. To test it, add a 100-ohm resistor to the antenna port and run the analyzer to get a flat line. If the SWR rolled up at the higher frequencies, it means the buffer is not working well. An alternative is to replace 1k resistors with 220 ohms and the emitter resistor (470)with 220 for lower voltage. Also, add a rf choke to the buffer power supply with a 0.1 cap to decouple.

3) An alternative solution is to run the pro mini at 5 volts and will give a wider ADC (0-5v) at the cost of a level shifter.

4) Use a directional coupler to replace the resistive bridge. Use an ad8307 log detector.

5) Adding a superheterodyne detector to get rid of harmonics and drive the mixer using a multiple clock generator (e.g Si5351) and use other channels to feed the sweeper/buffer.

Effect of inadequate power supply to buffer 

A few more pictures of the prototype in an earbud case :)



Hex File for atmega328 (Download)

Source Code (Download)


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
A simple vswr analyser using atmega328, a resistive bridge and a diode detector 
Compared to my previous one, this is modified to support a rotory encoder
Usage: Single click on the encoder: Move across menu
Double click will select
Rotation is used to change the values
More: http://blog.riyas.org
*/
#include <stdint.h>
#include <TFTv2.h> //https://github.com/riyas-org/ili9341
#include <SPI.h>
#include <avr/eeprom.h>
#include <AH_AD9850.h> //http://www.arduino-projekte.de/index.php?n=7
#include <OneButton.h> //https://github.com/riyas-org/OneButton
#include <Rotary.h> //https://github.com/riyas-org/Rotary
#define SWR_STEP 64 //set it high for more points in the plot

Rotary r = Rotary(3, 2); // Encoder connected to interrupt pins 2 and 3 on arduino promini (atmega328)
OneButton button(A0,true);// Click button on the encoder the other end is connected to ground
AH_AD9850 AD9850(9, 8, 7, 10); //AH_AD9850(int CLK, int FQUP, int BitData, int RESET);

// Pins for Fwd/Rev detectors
const int REV_ANALOG_PIN = A2;
const int FWD_ANALOG_PIN = A1;
//const int EXTRA_ANALOG_PIN = A3; // an extra detector for rf power 

const byte lcdW = 320 ;
const byte lcdH = 240 ;
const byte fontWidth = 10 ;
const byte fontHeigth = 30 ;
const byte totSpan = 9;
float spanValue[totSpan] =
{
    25000.0, 50000.0,100000.0, 250000.0,500000.0, 1000000.0, 2500000.0, 5000000.0,10000000.0
}
;
byte SWR[SWR_STEP];
//byte PWR[SWR_STEP];
float minSwr;
float maxSwr;
uint32_t next_state_sweep = 0;
byte menu_redraw_required = 0;
byte doubleClicked = 0;
byte modSpan = 0;
byte posInput = 0;
byte sign = 0; // negative
float Swr ;
float Pwr ;
struct settings_t
{
    int spanIdx;
    float freqCenter;
}
settings;
//averaged analogue read slow but solid
double analog_read_value(int pin) {
    double total = 0.0;
    double reading;
    int i;
    for (i=0; i< 80; i++) {
        reading = analogReadX(pin);
        total += reading * reading;
    }
    return total / 80.0;
}
double analogReadX(const int pin)
{
    if (pin==FWD_ANALOG_PIN) //in my case forward reading has an offset
    return analogRead(pin);
    else
    return analogRead(pin)-17;
}
void Docalcswr(){
    double FWD=0;
    double REV=0;
    double VSWR;
    // Read the forawrd and reverse voltages
    REV = analog_read_value(REV_ANALOG_PIN);
    FWD = analog_read_value(FWD_ANALOG_PIN);
    if(REV >= FWD){
        // To avoid a divide by zero or negative VSWR then set to max 999
        VSWR = 999;
        } else {
        // Calculate VSWR
        VSWR = (FWD+REV)/(FWD-REV);
        // Rx=(25+REV)/(0.5*FWD-REV-0.5);
        Swr = abs(VSWR);
       // Pwr = sqrt(analog_read_value(EXTRA_ANALOG_PIN));
    }
}
void creaGrid()
{
    byte p0 = lcdH - 2;
    for (int i = 0 ;i < 9; i++)
    {
        Tft.drawVerticalLine((i*40),fontHeigth,164,RED); //240*320
    }
    for (int i = 1 ;i < 2; i++)
    {
        Tft.drawHorizontalLine(0,i*82+fontHeigth,319,RED); //240
    }
    Tft.drawCircle(160,164+fontHeigth,5,GREEN);
    if (settings.freqCenter >9999999)
    Tft.drawFloat(settings.freqCenter/1000000,130,214,2,YELLOW);
    else
    Tft.drawFloat(settings.freqCenter/1000000,130,214,2,YELLOW);
    float s = spanValue[settings.spanIdx]/8;
    for (int i = 0 ;i < 9; i++)
    {
        double freq = (settings.freqCenter + (i-4)*s)/1000000;
        Tft.drawFloat(freq,(i*40),185,1,YELLOW);
    }
    if (spanValue[settings.spanIdx] >9999999)
    Tft.drawFloat(spanValue[settings.spanIdx]/1000000,260,214,2,YELLOW);
    else
    Tft.drawFloat(spanValue[settings.spanIdx]/1000000,260,214,2,YELLOW);
    Tft.drawRectangle(0,fontHeigth,320,164, GREEN);
    Tft.drawString("SWR",0,0,2,CYAN);
    Tft.drawFloat(minSwr,40,0,2,GREEN);
    Tft.drawFloat(maxSwr,120, 0,2,RED);
    if (!modSpan)
    {
        Tft.fillRectangle(40,210, 60,20, CYAN);
        Tft.drawString("freq",40, 210 ,2,BLUE);
        Tft.drawString("span",210, 210 ,2,BLUE);
    }
    else
    {
        Tft.fillRectangle(210,210, 50,20, CYAN);
        Tft.drawString("freq",40, 210 ,2,BLUE);
        Tft.drawString("span",210, 210 ,2,BLUE);
    }
}
void printSwr()
{
    for (int k = 0;k < SWR_STEP; k++)
    {
        int spacer=320/SWR_STEP;
        Tft.drawLine(k*spacer,SWR[k],(k+1)*spacer,SWR[k+1],WHITE); //plot vswr
        //Tft.drawLine(k*spacer,PWR[k],(k+1)*spacer,PWR[k+1],GREEN); //plot power
    }
}
void showCursor(int x0, int y0)
{
    Tft.fillRectangle(x0,y0, 50,8, BRIGHT_RED);
}
void draw(void)
{
    Tft.fillRectangle(0, 0, 320, 240, BLACK);
    creaGrid();
    printSwr();
    if (doubleClicked == 1)
    {
        switch (modSpan)
        {
            case 0 :
            {
                if (posInput <3) posInput = 3; showCursor(130,235);
                break;
            }
            case 1 :
            {
                posInput = 6;
                showCursor(260,235);
                break;
            }
            default: break;
        }
    }
}
void setup(void)
{
    Tft.TFTinit(); // init TFT library
    Tft.fillRectangle(0, 0, 320, 240, BLACK);
    analogReference(DEFAULT);
    // initialize serial communication at 9600 bits per second:
    Serial.begin(9600);
    //rotory interrupt
    PCICR |= (1 << PCIE2);
    PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
    sei();
    //set up the click on the encoder
    button.attachDoubleClick(doubleclick);
    button.attachClick(singleclick);
    eeprom_read_block((void*)&settings, (void*)0, sizeof(settings));
    if (settings.freqCenter <= 0)
    {
        settings.freqCenter = 7000000.0;
    }
    settings.spanIdx = 8;
    //settings.freqCenter = 7000000.0;
    AD9850.reset(); //reset module
    delay(200);
    AD9850.powerDown(); //set signal output to LOW
    AD9850.set_frequency(0,0,settings.freqCenter);
}
void calcParameters(int k, float stepSpan)
{
    float freq;
    float tempSwr;
    //float tempPwr;
    freq = settings.freqCenter + (k-SWR_STEP/2)* stepSpan;
    if (freq <= 0) freq = 1000000.0;
    AD9850.set_frequency(freq);
    delay(1);
    Docalcswr();
    Swr = max(1.00,Swr);
    //Pwr= max(1.00,Pwr);
    //tempPwr= min (600, Pwr);
    //Serial.print("temppwr:");
    //Serial.println(tempPwr);
    minSwr = min(minSwr,Swr);
    maxSwr = max(maxSwr,Swr);
    tempSwr = min (3.0, Swr);
    SWR[k] = 30+(164 - round(82*(tempSwr - 1))); //MAX SWR = 3.0 164+fontHeigth
    //PWR[k] = 30+(164 - round(164*((tempPwr*tempPwr)/360000))); //Pwr;
}
uint8_t update_graph(void)
{
    Tft.fillRectangle(250, 0, 70, 20, BLUE);
    Tft.drawNumber(next_state_sweep,250,0,2,CYAN);
    if ( next_state_sweep < SWR_STEP)
    {
        if(next_state_sweep == 0)
        {
            minSwr = 10.00;
            maxSwr = 1.00;
        }
        float s = spanValue[settings.spanIdx]/SWR_STEP;
        calcParameters(next_state_sweep,s);
        next_state_sweep++;
        return 0;
    }
    else
    {
        next_state_sweep = 0;
        return 1;
    }
}
float updateFreq(double r, float v)
{
    float step = round(pow(10,posInput));
    if (r == DIR_NONE )
    {
        // do nothing
    }
    else if ((r == DIR_CW) && (v + spanValue[settings.spanIdx]/2 < 54000000.0))
    {
        v = v + step;
    }
    else if ((r == DIR_CCW) && (v >= step + 1000000.0))
    {
        v = v - step;
    }
    return v;
}
void updateSpan(double r)
{
    if (r == DIR_NONE )
    {
        // do nothing
    }
    else if (r == DIR_CW)
    {
        settings.spanIdx++;
    }
    else if (r == DIR_CCW)
    {
        settings.spanIdx--;
        if (settings.spanIdx < 0) settings.spanIdx = totSpan -1;
    }
    settings.spanIdx = settings.spanIdx % totSpan;
}
void routine(void)
{
    if ( update_graph() != 0 | menu_redraw_required != 0)
    {
        draw();
    }
    menu_redraw_required = 0; // menu updated, reset redraw flag
}
void loop()
{
    // keep watching the push button:
    button.tick();
    routine();
}
ISR(PCINT2_vect) {
    //Serial.println("interrupt");
    unsigned char result = r.process();
    if (doubleClicked)
    {
        if(modSpan){
            updateSpan(result);
        }
        else
        {
            settings.freqCenter = updateFreq(result, settings.freqCenter);
        }
    }
    else modSpan = !modSpan;
    menu_redraw_required = 1;
}
void singleclick()
{
    //change position in field
    //Serial.println("Single");
    if (doubleClicked)
    {
        posInput++;
        posInput = posInput % 7;
    }
    menu_redraw_required = 1;
}
void doubleclick()
{
    //change field
    //Serial.println("double");
    doubleClicked = !doubleClicked;
    if (!doubleClicked) eeprom_write_block((const void*)&settings, (void*)0, sizeof(settings));;
    menu_redraw_required = 1;
}


Simple schematic for analyser with encoder


Here is a simple attempt to draw a schematic in eagle cad. If some one can help out with a nicer one, please drop an email or comment

Schematic for the analyser with encoder


Partlist
Part Value Package Description
C1 1u C050-025X075 CAPACITOR, European symbol
C7 1u C050-025X075 CAPACITOR, European symbol
C8 100n C050-025X075 CAPACITOR, European symbol
C9 10n C050-025X075 CAPACITOR, European symbol
C10 100n C050-025X075 CAPACITOR, European symbol
C11 10n C050-025X075 CAPACITOR, European symbol
D3 AA143 DO204-10 DIODE
D4 AA143 DO204-10 DIODE
IC2 LM358N DIL08 OP AMP also LM158; LM258; LM2904
PROMINI3.3VOLT PRO-MINI-2 PRO-MINI-2 Arduino Pro Mini Layout 2
R1 470 V234/12 RESISTOR, European symbol
R2 1k V234/12 RESISTOR, European symbol
R3 100 V234/12 RESISTOR, European symbol
R4 5k V234/12 RESISTOR, European symbol
R12 50 V234/12 RESISTOR, European symbol
R13 50 V234/12 RESISTOR, European symbol
R14 50 V234/12 RESISTOR, European symbol
R15 50 V234/12 RESISTOR, European symbol
R16 100k V234/12 RESISTOR, European symbol
R17 5k V234/12 RESISTOR, European symbol
R18 648 V234/12 RESISTOR, European symbol
R19 10k V234/12 RESISTOR, European symbol
R20 100k RDH/15 RESISTOR, European symbol
R21 5k V234/12 RESISTOR, European symbol
R22 648 V234/12 RESISTOR, European symbol
R23 47 V234/12 RESISTOR, European symbol
SW1 EC12E_SW ALPS_EC12E_SW ALPS rotary Encoder EC12E series with switch
T2 2N2222 TO18 NPN TRANSISTOR
T3 2N2222 TO18 NPN TRANSISTOR
U$2 2.2_TFT_LCD 2.2_TFT_LCD
U1 AD9850 DDS Module DDS_AD9850 AD9850 DDS Module
X2 BNC AMP_227161 JACK, RIGHT ANGLE, 50 OHM, PCB, BNC


References

[1]  http://www.hamstack.com/hs_projects/k6bez_antenna_analyzer.pdf

[2]  http://iphone-atom1945.blogspot.com/2013/08/antenna-analyzer-arduino-uno-dds-ad8307.html

[3]  http://www.zl2pd.com/digitalZmeter.html

[4] https://github.com/devzendo/antenna-analyser-c