Before you start.
The AA-30.ZERO is intended for users who require flexibility, low-cost, and small size. It comes without on-board USB or pin headers to keep the cost down. It is the best choice for a measuring core you want to leave embedded in a project.
Please note that the AA-30.ZERO operates at 5V (like most Arduino or Arduino compatible boards).
Be sure to provide the correct power and use components/adaptors whose operating voltage matches the AA-30.ZERO.
Connecting the AA-30.ZERO to your PC
The AA-30.ZERO comes without a built-in USB circuitry, so an off-board USB to UART-TTL serial converter should be used to communicate with the analyzer.
The AA-30.ZERO can communicate with other devices through any of two built-in UART interfaces: UART1 (pins 0/TX1 and 2/RX1) and UART2 (pins 4/TX2 and 7/RX2). By default, UART2 is used.
To make our first project, we could get, for example, the SparkFun USB UART Serial Breakout.
The USB2UART adapter is connected to the AA-30.ZERO by using colored wires.
NOTE: connect USB adapter’s TX pin to the RX pin of the analyzer PC board, then connect USB adapter’s RX pin to the TX pin of the analyzer.
Connect the USB2UART to your PC by using a standard USB cable.
Please make sure that the operating system recognizes your USB-to-UART adapter and installs the driver:
Installing AntScope software
Please download the AntScope software, open the archive and extract files (with subdirectories) into your workplace, then run AntScope.exe:
If the AntScope software does not detect the AA-30.ZERO automatically, please select the type of the connected device in the Configure menu:
Please make sure that the COM port number is set correctly:
Congratulations! Now everything is ready for the first measurement!
Start measuring
Connect AA-30.ZERO to your antenna (or some other load you wish to measure) using a flexible cable adapter, then click the Scan range icon right under the menu bar:
Click the Set full range button and click OK to start:
Notice the flashing LED at the PC board if the AA-30.ZERO…
A few seconds later, the result is displayed:
AntScope2 software
A newer version of the desktop software, called AntScope2, is available; instructions are similar to the above ones.
Pairing the AA-30.ZERO with the Arduino Uno
Installing headers
The AA-30.ZERO is supplied with a straight breakaway headres kit:
If you want to connect your analyzer to an Arduino board, you should first solder the breakaway headers. After that, simply plug your AA-30.ZERO into your Arduino board:
Pins usage
- D0 – UART interface 1, TX, data out
- D1 – UART interface 1, RX, data in
- D4 – UART interface 2, TX, data out
- D7 – UART interface 2, RX, data in
You can choose which UART interface to use, by re-soldering jumpers:
By default the AA-30.ZERO is using the UART2 interface.
Installing Arduino IDE and compiling your first project
Compile and run a very simple sketch on your Arduino board. Download and install Arduino IDE.
// RigExpert AA-30 ZERO antenna & cable analyzer and Arduino Uno
//
// Receives from the Arduino, sends to AA-30 ZERO.
// Receives from AA-30 ZERO, sends to the Arduino.
//
// 26 June 2017, Rig Expert Ukraine Ltd.
//
#include "SoftwareSerial.h"
#define RX0_Pin 0
#define TX0_Pin 1
#define RX1_Pin 4
#define TX1_Pin 7
#define HW_SERIAL
#ifndef HW_SERIAL
SoftwareSerial ZERO(RX1_Pin, TX1_Pin); // RX, TX
#endif
void setup() {
#ifdef HW_SERIAL
pinMode(RX0_Pin, INPUT);
pinMode(TX0_Pin, OUTPUT);
pinMode(RX1_Pin, INPUT);
pinMode(TX1_Pin, OUTPUT);
#else
ZERO.begin(38400); // init AA side UART
ZERO.flush()
Serial.begin(38400); // init PC side UART
Serial.flush();
#endif
}
void loop() {
#ifdef HW_SERIAL
//digitalWrite(TX0_Pin, digitalRead(RX1_Pin));
//digitalWrite(TX1_Pin, digitalRead(RX0_Pin));
if (PIND & (1 << 4)) PORTD |= (1 << 1); else PORTD &= ~(1 << 1);
if (PIND & (1 << 0)) PORTD |= (1 << 7); else PORTD &= ~(1 << 7);
#else
if (ZERO.available()) Serial.write(ZERO.read()); // data stream from AA to PC
if (Serial.available()) ZERO.write(Serial.read()); // data stream from PC to AA
#endif
}
This simple “serial repeater” code provides two-way communication between your computer and the analyzer board. By the way, since the Arduino board now acts as a repeater, you can implement any code in it and moderate the exchange of data between your computer and the AA-30.ZERO.
Serial communication protocol
Since the AA-30.ZERO was made for amateurs who are always wishing to construct something intersting, we had to take care of giving more freedom to the users of the analyzer.
Using the commands listed below, you can not only measure parameters of your antennas and cables, but also automate your analyzer, such as make it automatically perform periodic measurements immediately after the power is applied. This could be useful, for instance, in a case when the instrument is a part of a more complex system, for example, an antenna tuner.
Communication protocol
Command | Description | Response |
---|---|---|
ver | returns analyzer type and firmware version | AA-30 ZERO XXX |
fqXXXXXXXXX | set center frequency to XXXXXXXXX Hz | OK |
swXXXXXXXXX | set sweep range to XXXXXXXXX Hz | OK |
frxNNNN | perform NNNN measurements in the specified range | output frequency (MHz), R and X for every measurement |
Example:
SW1000000\r\n
FRX10\r\n
14.000000,58.84,17.28\r\n
14.100000,69.74,16.79\r\n
14.200000,68.52,5.62\r\n
14.300000,62.49,2.79\r\n
14.400000,57.51,4.62\r\n
14.500000,55.38,9.11\r\n
14.600000,56.52,13.56\r\n
14.700000,59.40,17.41\r\n
14.800000,64.12,20.05\r\n
14.900000,71.13,22.01\r\n
15.000000,81.57,21.63\r\n
Data visualization
One of the simplest ways to visualize measurement results is to make your own application 🙂
Install Processing IDE
First, download and install the Processing IDE. After the IDE software is installed, let’s compile this quite simple sketch:
// For RigExpert AA-30 ZERO antenna & cable analyzer with Arduino Uno
//
// Receives data from AA-30 ZERO and makes a surface
// which is a visualization of SWR as a function of time
//
// 26 June 2017, Rig Expert Ukraine Ltd.
//
import processing.serial.*;
Serial ZERO;
int step; // Communication protocol steps (0 - set Freq; 1 - set Range; 2 - start measurements)
int maxSamples = 100; // Number of point to measure
int maxSets = 50; // Time depth
float points[][]; // Measurements data
int sample; // current sample
int set; // current data set
int colors[]; // curve color
int total; // total samples acquired
boolean ready; // screen redrawn if True
int Frequency; // current Frequensy
int Range; // current Range
// Code to send command to Analyzer
void serialize(String cmd) {
int len = cmd.length();
int charPos = 0;
while (len-- != 0) {
ZERO.write(cmd.charAt(charPos));
charPos++;
}
}
// SWR computation function
// Z0 - System impedance (i.e. 50 for 50 Ohm systems)
// R - measured R value
// X - measured X value
float computeSWR(float Z0, float R, float X) {
float SWR, Gamma;
float XX = X * X;
float denominator = (R + Z0) * (R + Z0) + XX;
if (denominator == 0) {
return 1E9;
} else {
float l = (R - Z0) * (R - Z0);
float t = (l + XX);
t = t / denominator;
Gamma = sqrt(t); // always >= 0
// NOTE:
// Gamma == -1 complete negative reflection, when the line is short-circuited
// Gamma == 0 no reflection, when the line is perfectly matched
// Gamma == +1 complete positive reflection, when the line is open-circuited
if (Gamma == 1.0) {
SWR = 1E9;
} else {
SWR = (1 + Gamma) / (1 - Gamma);
}
}
// return values
if ((SWR > 200) || (Gamma > 0.99)) {
SWR = 200;
} else if (SWR < 1) {
SWR = 1;
}
return SWR;
}
void setup() {
Frequency = 115000000;
Range = 230000000;
sample = 0;
set = -1;
step = 0;
points = new float[maxSets + 1][maxSamples + 1];
colors = new int[maxSets + 1];
ready = false;
total = 0;
background(0);
stroke(120, 240, 255, 255);
strokeWeight(1);
size(640, 480, P3D);
printArray(Serial.list());
// Replace COM name with one that matches your conditions
ZERO = new Serial(this, "COM21", 38400);
ZERO.bufferUntil(13);
delay(1000);
serialize("SW0\r\n");
}
void drawSurface() {
ready = false;
lights();
float sp = 0.001 * frameCount;
camera((width / 3) * sin(sp), 0, 800, width / 2, height / 2, 0, 0, 1, 0);
background(0, 0, 0);
textSize(30);
fill(255, 255, 255);
// ---------------- Axis ---------------------
stroke(255, 255, 255, 128);
line(0, height, 0, width, height, 0);
line(0, 0, 0, 0, height, 0);
line(width, 0, 0, width, height, 0);
line(0, height, 5 * maxSets, 0, height, 0);
line(width / 2, height, 5 * maxSets, width / 2, height, 0);
line(width, height, 5 * maxSets, width, height, 0);
// ---------------- Freq. markers ----------------
stroke(255, 255, 255, 128);
line(width / 2, 0, 0, width / 2, height, 0);
textAlign(CENTER);
text(Frequency / 1E3 + " kHz", width / 2, height, 5 * maxSets);
text(((Frequency / 1E3) - (Range / 2E3)) + " kHz", 0, height, 5 * maxSets);
text(((Frequency / 1E3) + (Range / 2E3)) + " kHz", width, height, 5 * maxSets);
// ----------------- Mode title ------------------
textAlign(LEFT);
textSize(36);
text("SWR as a function of time Graph", 0, -100, 0);
textSize(30);
if (mouseY < height / 5) {
if (mouseX < width / 2) {
fill(255, 0, 0);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
fill(255, 255, 255);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
} else {
fill(255, 255, 255);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
fill(255, 0, 0);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
}
} else {
fill(255, 255, 255);
textAlign(RIGHT);
text("F = " + Frequency / 1E3 + " kHz", width / 2 - 50, -50, 0);
textAlign(LEFT);
text("Range = " + Range / 1E3 + " kHz", width / 2 + 50, -50, 0);
}
// Get extremums
float minV = 1E9;
float maxV = -1E9;
for (int i = 0; i < set; i++) {
for (int j = 0; j < maxSamples + 1; j++) {
if (points[i][j] > maxV) maxV = points[i][j];
if (points[i][j] > maxV) minV = points[i][j];
}
}
println("Min = " + minV + "; Max = " + maxV);
minV = 1;
if (maxV < 2) maxV = 2;
else if (maxV < 5) maxV = 5;
else if (maxV < 10) maxV = 10;
else maxV = 100;
float hK = width / maxSamples;
float vK = height / (maxV - minV);
float zK = 2;
// ----------------- Draw horizontal markers -----------------
fill(255, 255, 255);
textAlign(RIGHT);
line(0, height - vK, 0, width, height - vK, 0); // SWR = 2
text("SWR = 2.0", 0, height - vK, 0);
line(0, height - 2 * vK, 0, width, height - 2 * vK, 0); // SWR = 3
text("SWR = 3.0", 0, height - 2 * vK, 0);
line(0, height - 4 * vK, 0, width, height - 4 * vK, 0); // SWR = 5
text("SWR = 5.0", 0, height - 4 * vK, 0);
// Plot the lines
for (int i = 0; i < set; i++) {
if (colors[i] % 5 == 0) stroke(255, 0, 0, 255 * i / set);
else stroke(120, 240, 255, 255 * i / set);
for (int j = 1; j < maxSamples + 1; j++) {
// draw only if SWR < 100.0
if (points[i][j - 1] < 100) {
line((j - 1) * hK, height - (points[i][j - 1] - 1) * vK, i * zK, j * hK, height - (points[i][j] - 1) * vK, i * zK); // Process incoming data void serialEvent(Serial p) { String inString; inString = p.readString(); if (inString.indexOf("OK") >= 0) {
}
}
}
} void draw() {
if (ready) {
drawSurface();
}
}
switch (step) {
case 0: serialize("FQ" + Frequency + "\r\n");
step = 1;
break;
case 1: serialize("SW" + Range + "\r\n");
step = 2;
break;
case 2: serialize("FRX" + str(maxSamples) + "\r\n");
step = 0;
sample = 0;
if (set == maxSets) {
// shift curves back
for (int i = 1; i < maxSets + 1; i++) {
colors[i - 1] = colors[i];
for (int j = 0; j < maxSamples + 1; j++) {
points[i - 1][j] = points[i][j];
}
}
} else {
set++;
}
colors[set] = total++;
ready = true;
break;
}
} else {
float[] nums = float(split(inString, ','));
if (nums.length == 3) {
float SWR = computeSWR(50, nums[1], nums[2]);
points[set][sample] = SWR;
sample++;
}
}
}
// Change Frequency & Range values by the Mouse Wheel
void mouseWheel(MouseEvent event) {
float e = event.getCount();
if (mouseY < height / 5) {
if (mouseX < width / 2) { // Change Freq. if (Frequency > 1E5) {
Frequency += e * 100000;
drawSurface();
}
} else {
// Change Range
if (Range > 1E5) {
Range += e * 1E5;
drawSurface();
}
}
}
}
Important notice
Please make sure that the correct COM number is used here:
Running the Processing sketch
After the sketch is copied to the IDE editor, press the RUN button:
A few seconds later, measurement results will be displayed on your screen, like this:
Let’s compare the resulting drawings with the chart that the AntScope program draws:
To get the 100% similarity, you will have to play a little with a logarithmic scale.
Source files to start from
You can download source files from the GitHub repository.
Our friend Edward March (WB9RAA) made a sketch for Arduino, that parses the F,R,X line and adds in SWR on the fly.
// RigExpert AA-30 ZERO antenna & cable analyzer and Arduino Uno
//
// Receives from the Arduino, sends to AA-30 ZERO.
// Receives from AA-30 ZERO, sends to the Arduino.
//
// 26 June 2017, Rig Expert Ukraine Ltd.
//
#include <SoftwareSerial.h>
SoftwareSerial ZERO(4, 7); // RX, TX
//
// Modified by WB9RAA Ed March
// January 6, 2018
//
// talks to AA-30 Zero at 38,400 baud and
// make data suitable for a spreadsheet plot
//
// Sweep all ham bands in about 10K Freq. steps
// Computes SWR on the fly so each line of text
// sent is: "Freq,R,X,SWR \r\n"
// intermixed with OK and fq sw frx commands too
// Compiled with Arduino IDE 1.8.5
// total sweep time is about 25 seconds for 160M-10M bands.
//
// Faster plots with "frx3" on all bands so Only Edge and Center Freqs are printed.
// Other ideas, sweep 1..30 Mhz and only print F,R,X,SWR when swr is under 2.0
//
int j;
char HamBands[][35] = {
{ "\r\nver\r\n" } , //0
{ "fq1900000\r\nsw200000\r\nfrx20\r\n" }, //1
{ "fq3750000\r\nsw500000\r\nfrx50\r\n" }, //2
{ "fq5331900\r\nsw2800\r\nfrx3\r\n" }, //3
{ "fq5347900\r\nsw2800\r\nfrx3\r\n" }, //4
{ "fq5358900\r\nsw2800\r\nfrx3\r\n" }, //5
{ "fq5404900\r\nsw2800\r\nfrx3\r\n" }, //6
{ "fq7150000\r\nsw300000\r\nfrx35\r\n" }, //7
{ "fq10125000\r\nsw50000\r\nfrx10\r\n" }, //8
{ "fq14150000\r\nsw300000\r\nfrx30\r\n" }, //9
{ "fq18118000\r\nsw100000\r\nfrx10\r\n" }, //10
{ "fq21225000\r\nsw450000\r\nfrx45\r\n" }, //11
{ "fq24940000\r\nsw100000\r\nfrx10\r\n" }, //12
{ "fq28985000\r\nsw1970000\r\nfrx50\r\n" }, //13
{ "" }, // End marker //14
//
};
void setup()
{
ZERO.begin(38400); // init AA side UART
ZERO.flush();
ZERO.setTimeout(2500);
Serial.begin(38400); // init PC side UART
Serial.flush();
delay(50);
j = 0;
Serial.println("\r\n\nThe AA-30Zero Project Ver.20180107A by Ed March WB9RAA\n");
Serial.println("\nUsing the RigExpert 'AA-30 Zero' Antenna Analyzer\nScan all 1..30Mhz Ham bands.\nWhen done Press / to run again. Otherwise type commands to AA-30 Zero\n");
Serial.println("AA-30 Zero Commands");
Serial.println("ver : GET VERSION OF AA FIRMWARE");
Serial.println("fq1234567 : SET CENTER FREQUENCY IN HERTZ ex. fq7150000");
Serial.println("sw300000 : SET SWEEP RANGE IN HERTZ ex. sw300000 sweeps 300Khz from Begin To End");
Serial.println("frx123 : START SWEEPING returning F,R,X Total of 123 lines. frx3 returns 3 lines BEGIN,CENTER,END\n");
}
long tmo = 250;
void loop()
{
if (ZERO.available())
{
String s = ZERO.readStringUntil('\n');
s.replace("\r", "");
Serial.write(s.c_str()); // data stream from AA to PC
int i = s.indexOf(',');
if (i > 0)
{
// Parse string into floats R & X
i++;
float R = s.substring(i).toFloat();
int ii = s.substring(i).indexOf(',');
float X = s.substring(i + ii + 1).toFloat();
//
// Compute SWR from R & X
//
float XX = X * X;
float Rm = (R - 50) * (R - 50);
float Rp = (R + 50) * (R + 50);
float N = sqrt(Rm + XX);
float D = sqrt(Rp + XX);
float G = N / D;
float vswr = (1 + G) / (1 - G);
// Since we can not print floats we get int & fraction as INT's
// if swr is 4.12 then v1=4 and v2=12 -- 1.04 then 1 and 4 printed as 1.04 using %d.%02d
int v1 = vswr;
int v2 = (vswr - v1) * 100;
if (v1 < 0)
{
v1 = 99;
v2 = 0;
}
char z[50];
sprintf(z, ",%d.%02d", v1, v2); // comput swr as string
Serial.write(z); // append to string
}
Serial.write("\r\n"); // and terninate as it was with CR LF
tmo = 250;
}
if (Serial.available())
{
char c1 = Serial.read();
if (c1 == '/')
{
j = 0;
}
else
{
ZERO.write(c1); // data stream from PC to AA
}
tmo = 250;
}
delay(1);
if (--tmo < 0)
{
if (HamBands[j][0] != 0)
{
for (int i = 0; HamBands[j][i] != 0; i++)
{
int ch = HamBands[j][i];
Serial.write(ch);
ZERO.write(ch);
if (ch == '\n')
{
for (int d = 0; d < 50; d++)
{
delay(1);
if (ZERO.available())
{
Serial.write(ZERO.read()); // data stream from AA to PC
}
}
}
}
tmo = 250;
j++;
}
}
}
To be continued…