Getting Started with .ZERO

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’s a best choice for a measuring core you want to leave embedded in a project.
Please note that the .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 .ZERO.

Connecting .ZERO to the PC
The .ZERO comes without built-in USB circuitry, so an off-board USB-to-TTL serial convertor must be used to communicate with the .ZERO.
.ZERO communicates with external devices throug UART interface.
For these purposes two UART interfaces are provided: UART1 (pins 0/TX1 and 2/RX1) and UART2 (pins 4/TX2 and 7/RX2).
By default .ZERO uses UART2 interface.
To make our first project we could get, for example, the SparkFun USB UART Serial Breakout.

Fig.1.ZERO plus USB2UART adaptor wiring diagram
connecting-zero-to-the-pc

Using colored wires connect USB2UART adaptor to .ZERO.

NOTE: connect USB adaptor’s TX pin to the RX pin of the .ZERO. Then connect USB adaptor’s RX pin to the TX pin of the .ZERO.
zero-plus-usb2uart-demo
Then connect USB2UART to your PC using standard USB cable.

Please be sure that the Operating System recognizes your USB-to-UART adaptor and drivers are installed correctly:
snapcrab_noname_2017-10-20_12-17-20_no-00

Installing AntScope software
Then download AntScope software to measure anything you want.
You may use software installer from AA-30 product page or download standalone AntScope software using this link.
Suppose you downloaded the archive with standalone AntScope software.
Open the archive and extract files (with subdirectories) into your workplace. Then run AntScope.exe:
snapcrab_antscope_2017-10-20_15-21-53_no-00

The AntScope program may not detect the AA-30.ZERO connected automatically.
In this case, you have to select the type of the connected device in the menu yourself:
selecting-aa-30

Please be sure that the right COM port selected:
choosing-comport

Congratulations! Now everything is ready for the first measurements.

Start measuring
First connect AA-30.ZERO to the antenna (or something else you want to measure) using flexible cable adaptor.
Then click Run Icon right under the menu bar:
running-measurements

then press “Set full range” button and press “OK” button:
snapcrab_scan_2017-10-20_15-46-42_no-00

The .ZERO starts blinking…

and after a few seconds the measurement results are displayed:
snapcrab_1-antscope_2017-10-20_16-8-45_no-00

 

Pairing .ZERO with the Arduino Uno

Installing headers
The .ZERO supplied with traight breakaway headres kit:
Top side view

If you want to pair your .ZERO with the Arduino board you should solder breakaway headers first.
After the heareds soldered simply connect .ZERO with the Arduino board:
.ZERO paired with Arduino Uno

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 should be used by unsoldering jumpers:
uart-selector-jumpers
By default the .ZERO uses UART2 interface.

Installing Arduino IDE and compiling your first project
After that you should compile and run very simple sketch on your Arduino board.
Download and install Arduino IDE.

// UART bridge for data exchange between
// 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

void setup() {
    ZERO.begin(38400); // init AA side UART
    ZERO.flush();
    Serial.begin(38400); // init PC side UART
    Serial.flush();
}

void loop() {
    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
}

This simple “serial repeater” code provides two-way communication between the computer and the .ZERO.
By the way, since the Arduino borad acts as a repeater, you can implement any code in it and moderate the exchange of data between the computer and the .ZERO analyzer.

Serial communication protocol

Since the .ZERO is intended for the amateurs to construct something intersting, we had to take care to give more freedom to the users of the .ZERO device.
Using the commands below, you can not only measure the parameters of antennas and cables, but also make the .ZERO automatically perform periodic measurements immediately after power is applied.
This is convenient when the instrument is part of a more complex system, for example, an antenna tuner.

Communication protocol

CommandDescriptionResponse
verreturns analyzer type and firmware versionAA-30 ZERO XXX
fqXXXXXXXXXset center frequency to XXXXXXXXX HzOK
swXXXXXXXXXset sweep range to XXXXXXXXX HzOK
frxNNNNperform NNNN measurements in the specified rangeoutput frequency (MHz), R and X for every meqasurement

Example:

FQ14500000\r\n
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

Quite simplest way to visualize measurement results is to make your own application 🙂
 
Install Processing IDE
By following this way, first we will download and install Processing IDE.
After the IDE software is installed, let’s compile this quite simple sketch:

// 3D visualization for SWR measurements
// 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);
        }
      }
    }
}


void draw() {
  if (ready) {
    drawSurface();
  }
}


// Process incoming data
void serialEvent(Serial p) {
  String inString;  
  inString = p.readString();
  if (inString.indexOf("OK") >= 0) {  
    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 note
Please be sure that the right COM number used here:

ZERO = new Serial(this, "COM16", 115200);

 
Running the Processing sketch
After the sketch copied to the IDE editor press RUN button.
running-processing-sketch
Some time later measurement results will be displayed on your screen:
snapcrab_aa_30_zero_surface_2017-10-24_14-35-6_no-00

 

Let’s compare the resulting drawings with the chart that the AntScope program draws:

antscope-vs-processing-comparison

To get 100% similarity, you have to play a little with a logarithmic scale.
 

Source files to start from
You can download source files from GitHub repository.

 
To be continued…