Using a Arduino with Test Controller, some examples

DSC_4220

This is the second article about using Arduino with TestController, this article will contain some practical examples on devices. The examples are usable, but very simple. I have included some ideas for improving the examples.
With TestController it is possible to mix standard lab equipment (DMM, Power supplies, Loads, etc.) with the Arduino based devices and make one common logfile with all the results.

Contents
    Requirements
    Digital input and output module
        Arduino program
        Commands
        TestController definition
        How do it look in TestContoller
        Ideas for improvement
    Servo control
        Arduino program
        Commands
        TestController definition
        How do it look in TestContoller
        Ideas for improvement
    DS1820 temperature device
        Arduino program
        Commands
        TestController definition
        How do it look in TestContoller
        Ideas for improvement
    DHT22/AM2302 temperature and humidity device
        Arduino program
        Commands
        TestController definition
        How do it look in TestContoller
        Ideas for improvement
    Scripting
    Conclusion
    Notes & links
Using a Arduino with Test Controller
TestController main page


Requirements

The basic requirement is a Arduino with a (virtual) serial port, nearly all Arduinos and clones qualifies for that. I often uses a Nano or ProMicro (This is not a real Arduino), they are both fairly small and can easily do simple data acquisition and control.
The inputs and outputs are not isolated from the controlling PC, in some cases this may be an issue.



Digital input and output module

DIOConnectionsNano DIOConnectionsProMicro

This Arduino module will provide four digital inputs and four digital outputs on Arduino voltage levels. It is possible to add external signal condition and/or change the number of IO's.

Arduino program

Code:
#define BRAND "HKJ"
#define PRODUCT "TestDigital"
#define SW_VERSION 1
#define SERIAL_NUMBER 1

// Maximum command length and buffer
const int BUF_SIZE = 50;
char cmdBuf[BUF_SIZE];

// Pin assignment
const byte in0_pin=2;
const byte in1_pin=3;
const byte in2_pin=4;
const byte in3_pin=5;
const byte out0_pin=6;
const byte out1_pin=7;
const byte out2_pin=8;
const byte out3_pin=9;

// Pullup status is read only, this means I need a place to store the current values.
byte inputPullup=0x0f;


// Accessing the different IO pins can be done with a switch or a array, 
// with low number of IO pins switch can be used, with high number of IO pins array and loops is preferred.

byte readIO() {
  byte v=0;
  v|=digitalRead(in0_pin)?0x01:0;
  v|=digitalRead(in1_pin)?0x02:0;
  v|=digitalRead(in2_pin)?0x04:0;
  v|=digitalRead(in3_pin)?0x08:0;
  v|=digitalRead(out0_pin)?0x10:0;
  v|=digitalRead(out1_pin)?0x20:0;
  v|=digitalRead(out2_pin)?0x40:0;
  v|=digitalRead(out3_pin)?0x80:0;
  return v;
}

byte readOutputs() {
  byte v=0;
  v|=digitalRead(out0_pin)?0x01:0;
  v|=digitalRead(out1_pin)?0x02:0;
  v|=digitalRead(out2_pin)?0x04:0;
  v|=digitalRead(out3_pin)?0x08:0;
  return v;
}

void writeOutputs(byte v) {
  digitalWrite(out0_pin,v&0x01?HIGH:LOW);
  digitalWrite(out1_pin,v&0x02?HIGH:LOW);
  digitalWrite(out2_pin,v&0x03?HIGH:LOW);
  digitalWrite(out3_pin,v&0x04?HIGH:LOW);
}

void setInputPullup(int n,boolean on) {
  if (on) inputPullup|=1<<n;
  else inputPullup&=~(1<<n);
  pinMode(in0_pin,inputPullup&0x01?INPUT_PULLUP:INPUT);
  pinMode(in1_pin,inputPullup&0x02?INPUT_PULLUP:INPUT);
  pinMode(in2_pin,inputPullup&0x04?INPUT_PULLUP:INPUT);
  pinMode(in3_pin,inputPullup&0x08?INPUT_PULLUP:INPUT);
}


boolean readOutput(int n) {
  switch (n) {
    case 0: return digitalRead(out0_pin);
    case 1: return digitalRead(out1_pin);
    case 2: return digitalRead(out2_pin);
    case 3: return digitalRead(out3_pin);
  }
  return false;
}

void writeOutput(int n,byte v) {
  switch (n) {
    case 0: digitalWrite(out0_pin,v?HIGH:LOW);break;
    case 1: digitalWrite(out1_pin,v?HIGH:LOW);break;
    case 2: digitalWrite(out2_pin,v?HIGH:LOW);break;
    case 3: digitalWrite(out3_pin,v?HIGH:LOW);break;
  }
}

void setup() {
  Serial.begin(9600);
  // The next two lines are only needed on processors with build in USB
  unsigned long t = millis();
  while (!Serial && millis() - t < 3000); // Wait for USB connection to get ready, but do not hang when running without USB
  pinMode(in0_pin,INPUT_PULLUP);
  pinMode(in1_pin,INPUT_PULLUP);
  pinMode(in2_pin,INPUT_PULLUP);
  pinMode(in3_pin,INPUT_PULLUP);
  inputPullup=0x0f;
  pinMode(out0_pin,OUTPUT);
  pinMode(out1_pin,OUTPUT);
  pinMode(out2_pin,OUTPUT);
  pinMode(out3_pin,OUTPUT);
}

// The loop must not use delay() or delay in any other way, it has to run through in a small fraction of a second for best performance
void loop() {
  if (Serial.available() > 0) {
    int n = Serial.readBytesUntil('\n', cmdBuf, BUF_SIZE - 1);
    if (n > 0) {
      *(cmdBuf + n) = 0;
      strlwr(cmdBuf);
      char* cmd = strtok(cmdBuf, " ");
      if (strcmp(cmd, "*idn?") == 0) {
        Serial.print(BRAND);
        Serial.print(',');
        Serial.print(PRODUCT);
        Serial.print(',');
        Serial.print(SERIAL_NUMBER);
        Serial.print(',');
        Serial.print(SW_VERSION);
        Serial.print('\n');
      } else if (strcmp(cmd, "values?") == 0) {
        // This command returns all values that has to be saved in the table and used to draw curves from
        Serial.print(readIO());
        Serial.print('\n');
      } else if (strcmp(cmd, "outputs?") == 0) {
        Serial.print(readOutputs());
        Serial.print('\n');
      } else if (strcmp(cmd, "output?") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          Serial.print(readOutput(atoi(p)));
        }
        Serial.print('\n');
      } else if (strcmp(cmd, "pullup?") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          Serial.print(inputPullup&1<<atoi(p)?1:0);
        }
        Serial.print('\n');
      } else if (strcmp(cmd, "outputs") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          writeOutputs(atoi(p));
        }
      } else if (strcmp(cmd, "output") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          byte n=atoi(p);
          p = strtok(NULL, " ");
          if (p!=NULL) {
            writeOutput(n,atoi(p));
          }          
        }
      } else if (strcmp(cmd, "pullup") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          byte n=atoi(p);
          p = strtok(NULL, " ");
          if (p!=NULL) {
            setInputPullup(n,atoi(p));
          }          
        }
      }
    }
  }
}

There are defined commands for handle both a single output bit or all bits at once.


Commands


TestController definition

The definition must be placed in "...\doc\TestController\Devices" (On a Windows PC) in a file that has a .txt extension. It is recommended to use the BRAND and NAME for the file name.

Manual for definitions is here

Code:
; TestController must be restarted before any changes in this file will be used.

; Manual is here: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html

#idString HKJ,TestDigital,
#name HKJ Test digital IO
#handle DIO
#port comfixedbaud
; Devices with 9600 baud will be automatic found if "Scan serial ports" are checked
#baudrate 9600

; A list of possible column name with unit and formatter (SI, Time, Int, D0..D9, X1..X9) 
; See here for all formatter types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Data_type_definitions
#value IO _ Digital(i0,i1,i2,i3,o0,o1,o2,o3)

; All outputs must turn on when the output off button is pressed.
#outputOff outputs 0

; How to poll for data, this is used for table and #values?
; a #askMode, #cmdMode and #prepareSample is used before this is string is used.
; Number of returned values must match number of columns defined with #value
; This is a single line command
#askValues values?

; See here for all control types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Configuration_menu
#cmdSetup checkbox Output
:read: output? 0
:write: output 0
o0 0 1

#cmdSetup checkbox Output
:read: output? 1
:write: output 1
o1 0 1

#cmdSetup checkbox Output
:read: output? 2
:write: output 2
o2 0 1

#cmdSetup checkbox Output
:read: output? 3
:write: output 3
o3 0 1



#cmdSetup checkbox Pullup
:read: pullup? 0
:write: pullup 0
i0 0 1

#cmdSetup checkbox Pullup
:read: pullup? 1
:write: pullup 1
i1 0 1

#cmdSetup checkbox Pullup
:read: pullup? 2
:write: pullup 2
i2 0 1

#cmdSetup checkbox Pullup
:read: pullup? 3
:write: pullup 3
i3 0 1

The datatype is defined as digital, this way it is possible to name each IO bit.
Because this module contains outputs the #outputOff tag is defined to turn all outputs off.
The settings of output state is done with checkboxes.



How do it look in TestContoller

DIOValues

The IO bits are shown by name.

DIOChart

On the chart all IO bits uses the same scale, i.e. it is possible to have more curves visible at the same time.

DIOSettings

The settings are done with checkboxes

There are many other ways to see and control the Arduino device in TestController.



Ideas for improvement

The hardware could be improved with opto isolated inputs and signal condition and the output could be connected to relays or mosfet maybe with isolation.
The software could and more input measurement like frequency, puls width, pulse counter, etc. For the output it could be support for timed outputs or PWM.
Number of inputs/outputs could be adjusted.
Error checking on input values.
Add interface to support generic scripting to use the module.



Servo control

ServoceConnectionsNano

ServoConnectionsProMicro

This Arduino module will control one standard servo, it can move the servo either at full speed or slowly. The slow movement can be used to log data from some item that is moved by the servo. The servo could also be used to active buttons during a automated test.



Arduino program

This code uses the standard servo library.

Code:
#include <Servo.h>
#define BRAND "HKJ"
#define PRODUCT "TestServo"
#define SW_VERSION 1
#define SERIAL_NUMBER 1

// Commands:
// pos position
// move time position
// off
// on?
// pos?
// move?

// Maximum command length and buffer
const int BUF_SIZE = 50;
char cmdBuf[BUF_SIZE];

// Pin assignment
const byte servo_pin = 2;

// Make a servo class
Servo servo;
boolean moveOn = false; // A move command is active, the off or pos command will cancel it
double pos; // Current position with decimals
double delta; // How much to change pos for each step
int steps;    // Number of steps
double targetPos = 90; // Final position when moving
double moveTime = 30; // Time to read final position when moving
long timer;     // Timer to handle 100ms background update interval

void setup() {
  Serial.begin(9600);
  // The next two lines are only needed on processors with build in USB
  unsigned long t = millis();
  while (!Serial && millis() - t < 3000); // Wait for USB connection to get ready, but do not hang when running without USB

  timer = millis();
}

// Background function to handle active move command
void move() {
  if ((millis() - timer) < 100) return; // Step 10 times each second
  timer += 100;
  if (!moveOn) return;
  if (!servo.attached()) return;
  if (steps > 1) {  // Step slowly
    pos += delta;
    servo.write(pos);
    steps--;
  } else {  // Last step is always to specified target
    pos = targetPos;
    servo.write(pos);
    moveOn = false;
  }
}

// The loop must not use delay() or delay in any other way, it has to run through in a small fraction of a second for best performance
void loop() {
  move();
  if (Serial.available() > 0) {
    int n = Serial.readBytesUntil('\n', cmdBuf, BUF_SIZE - 1);
    if (n > 0) {
      *(cmdBuf + n) = 0;
      strlwr(cmdBuf);
      char* cmd = strtok(cmdBuf, " ");
      if (strcmp(cmd, "*idn?") == 0) {
        Serial.print(BRAND);
        Serial.print(',');
        Serial.print(PRODUCT);
        Serial.print(',');
        Serial.print(SERIAL_NUMBER);
        Serial.print(',');
        Serial.print(SW_VERSION);
        Serial.print('\n');
      } else if (strcmp(cmd, "pos?") == 0) {
        Serial.print(servo.read());
        Serial.print('\n');
      } else if (strcmp(cmd, "on?") == 0) {
        Serial.print(servo.attached() ? 1 : 0);
        Serial.print('\n');
      } else if (strcmp(cmd, "move?") == 0) {
        Serial.print(moveTime);
        Serial.print(' ');
        Serial.print(targetPos);
        Serial.print('\n');
      } else if (strcmp(cmd, "off") == 0) {
        servo.detach();
        moveOn = false;
      } else if (strcmp(cmd, "pos") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          if (!servo.attached()) {
            servo.attach(servo_pin);
          }
          moveOn = false;
          pos = atoi(p);
          servo.write(pos);
        }
      } else if (strcmp(cmd, "move") == 0) {
        char *p = strtok(NULL, " ");
        if (p != NULL) {
          double time = atof(p);
          p = strtok(NULL, " ");
          if (p != NULL) {
            moveTime = time;
            targetPos = atof(p);
            if (!servo.attached()) {
              servo.attach(servo_pin);
            }
            delta = (targetPos - pos) / moveTime * 0.1; // Calculate step size based on time
            steps = (int) abs((targetPos - pos) / delta); // Calculate steps based in step size and how much to move
            moveOn = true;
          }
        }
      }
    }
  }
}

The code do not support the "values?" command, instead a "pos?" command is used to read the servo position, both in the setup and when showing values.



Commands


TestController definition

The definition must be placed in "...\doc\TestController\Devices" (On a Windows PC) in a file that has a .txt extension. It is recommended to use the BRAND and NAME for the file name.

Manual for definitions is here

Code:
; TestController must be restarted before any changes in this file will be used.

; Manual is here: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html

#idString HKJ,TestServo,
#name HKJ Test Servo
#handle Servo
#port comfixedbaud
; Devices with 9600 baud will be automatic found if "Scan serial ports" are checked
#baudrate 9600

; A list of possible column name with unit and formatter (SI, Time, Int, D0..D9, X1..X9) 
; See here for all formatter types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Data_type_definitions
#value Pos Deg x1

; All outputs must turn on when the output off button is pressed.
#outputOff off

; How to poll for data, this is used for table and #values?
; a #askMode, #cmdMode and #prepareSample is used before this is string is used.
; Number of returned values must match number of columns defined with #value
; This is a single line command
#askValues pos?

#cmdSetup indicatornum Status
:read: on?
:updatealloff:
Off 99 red
On 1 red

; See here for all control types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Configuration_menu
#cmdSetup number Position
:read: pos? 
:write: pos 
:tip: Position will not be updated after a slow move.
:update: Status
Deg 0 180

#cmdSetup Multi Slow_move
:read: move? 
:write: move 
:update: Status
:buttontext: Start
number Time s 0.5 600
number Pos Deg 0 180

#cmdSetup button Off
:write: off
:update: Status

The only information to show is the servo position.



How do it look in TestContoller

ServoChart

The servo, if some parameter depend on the angle, the angle can be moved to the x-axis.

ServoAdjust

The adjuster makes it easy to slide the servo position around.

ServoSettings

For more precise control or slow movement the setup menu can be used.

There are many other ways to see and control the Arduino device in TestController.



Ideas for improvement

More servos could be added.
The software could add calibration and range check for each servo.
Adding a poke command, i.e. pulse the servo to a position and back again, could be useful for activating a button during tests.
Add interface to support generic scripting to use the module.
Error checking on input values.



DS1820 temperature device

DS1820ConnectionsNano

DS1820ConnectionsProMicro

This Arduino module will read one DS1820 temperature sensor. This is a digital sensor, i.e. no ADC is required, it is included in the sensor.



Arduino program

This code uses the onewire and DallasTemperature libraries.

Code:
#include <OneWire.h>
#include <DallasTemperature.h>

#define BRAND "HKJ"
#define PRODUCT "TestDS1820"
#define SW_VERSION 1
#define SERIAL_NUMBER 1

// Maximum command length and buffer
const int BUF_SIZE = 50;
char cmdBuf[BUF_SIZE];

const byte Temperature_pin = 2;

OneWire oneWire(Temperature_pin);
DallasTemperature dsSensor(&oneWire);
DeviceAddress dsAddress;
boolean isDsSensor = false;
double temp = 0;
long timer = 0;

void setup() {
  Serial.begin(9600);
  // The next two lines are only needed on processors with build in USB
  unsigned long t = millis();
  while (!Serial && millis() - t < 3000); // Wait for USB connection to get ready, but do not hang when running without USB
  dsSensor.begin();
  findSensor();
  timer = millis();
}

void findSensor() {
  temp = 0;
  if (dsSensor.getDS18Count() == 1 && dsSensor.getAddress(dsAddress, 0)) {
    isDsSensor = true;
    dsSensor.setResolution(dsAddress, 12);
    dsSensor.setWaitForConversion(false);
    dsSensor.requestTemperatures();
  } else {
    isDsSensor = false;
  }

}

void updateTemp() {
  if (!isDsSensor) return;
  if ((millis() - timer) < 900) return; //Update slower than conversion, sensor uses up to 0.75 second for a conversion
  temp = dsSensor.getTempC(dsAddress);
  if (temp == DEVICE_DISCONNECTED_C ) {
    isDsSensor = false;
    return;
  }
  dsSensor.requestTemperatures();
  timer = millis();
}

void loop() {
  updateTemp();
  if (Serial.available() > 0) {
    int n = Serial.readBytesUntil('\n', cmdBuf, BUF_SIZE - 1);
    if (n > 0) {
      *(cmdBuf + n) = 0;
      strlwr(cmdBuf);
      char* cmd = strtok(cmdBuf, " ");
      if (strcmp(cmd, "*idn?") == 0) {
        Serial.print(BRAND);
        Serial.print(',');
        Serial.print(PRODUCT);
        Serial.print(',');
        Serial.print(SERIAL_NUMBER);
        Serial.print(',');
        Serial.print(SW_VERSION);
        Serial.print('\n');
      } else if (strcmp(cmd, "find") == 0) {
        findSensor();
      } else if (strcmp(cmd, "sensor?") == 0) {
        Serial.print(isDsSensor ? 1 : 0);
        Serial.print('\n');
      } else if (strcmp(cmd, "value?") == 0) {
        Serial.print(temp);
        Serial.print('\n');
      }
    }
  }
}

I use a "value?" command to get the sensor value, the actual sensor reading is performed in the background.



Commands


TestController definition

The definition must be placed in "...\doc\TestController\Devices" (On a Windows PC) in a file that has a .txt extension. It is recommended to use the BRAND and NAME for the file name.

Manual for definitions is here

Code:
; TestController must be restarted before any changes in this file will be used.

; Manual is here: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html

#idString HKJ,TestDS1820,
#name HKJ Test DS1820
#handle DS1820
#port comfixedbaud
; Devices with 9600 baud will be automatic found if "Scan serial ports" are checked
#baudrate 9600


; A list of possible column name with unit and formatter (SI, Time, Int, D0..D9, X1..X9) 
; See here for all formatter types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Data_type_definitions
#value Temperature °C D2
; Due to the ° (grad) symbol this file must be saved in unicode with a BOM marker to be reliable on all OS's

; How to poll for data, this is used for table and #values?
; a #askMode, #cmdMode and #prepareSample is used before this is string is used.
; Number of returned values must match number of columns defined with #value
; This is a single line command
#askValues value?

#cmdSetup indicatorNum Sensor_valid
:read: sensor?
No 0 red
Yes 1 green

#cmdSetup button Find_sensor
:write: find
:update: Sensor_valid
:tip: Search the one wire bus for a sensor, code will only work with one sensor



How do it look in TestContoller

DS1820Values

A single temperature sensor will show a single a single temperature and some statistic.

DS1820Settings

There is not really any settings on a DS1820, but it is possible replace the sensor and find it.

There are many other ways to see and control the Arduino device in TestController.



Ideas for improvement

A offset compensation that is stored in EEPROM would probably improve accuracy slightly.
Display to show temperature (Use my library for LED displays).
More than one sensor.
Add interface to support generic scripting to use the module.




DHT22/AM2302 temperature and humidity device

DHT22ConnectionsProMicro

This Arduino module will read one DHT22/AM2302 temperature and humidity sensor, this can be used to log the ambient conditions during a test.
The connection above only shows the ProMicro, because I have only checked with that processor, the nano may have problems with the serial interrupt disrupting the data transfer and giving wrong values.
This project is very simple and do not really need any improvements for practical usage.



Arduino program

This code uses my DHT22 sensor library. I have included the DHT22 files in the zip file, if these files are place in the same directory as the program it will work. It is also possible to download the DHT22 library from my website and include it.

Code:
#include 

#define BRAND "HKJ"
#define PRODUCT "TestDHT22"
#define SW_VERSION 1
#define SERIAL_NUMBER 1

// Maximum command length and buffer
const int BUF_SIZE = 50;
char cmdBuf[BUF_SIZE];

const byte Sensor_pin = 2;

DHT22 dht(Sensor_pin);

float temperature = 0;
float humidity = 0;

void setup() {
  Serial.begin(9600);
  // The next two lines are only needed on processors with build in USB
  unsigned long t = millis();
  while (!Serial && millis() - t < 3000); // Wait for USB connection to get ready, but do not hang when running without USB
}

void updateTemp() {
  if (!dht.ready()) return;
  dht.measure(temperature, humidity);
}

void loop() {
  updateTemp();
  if (Serial.available() > 0) {
    int n = Serial.readBytesUntil('\n', cmdBuf, BUF_SIZE - 1);
    if (n > 0) {
      *(cmdBuf + n) = 0;
      strlwr(cmdBuf);
      char* cmd = strtok(cmdBuf, " ");
      if (strcmp(cmd, "*idn?") == 0) {
        Serial.print(BRAND);
        Serial.print(',');
        Serial.print(PRODUCT);
        Serial.print(',');
        Serial.print(SERIAL_NUMBER);
        Serial.print(',');
        Serial.print(SW_VERSION);
        Serial.print('\n');
      } else if (strcmp(cmd, "values?") == 0) {
        Serial.print(temperature);
        Serial.print(' ');
        Serial.print(humidity);
        Serial.print('\n');
      } else if (strcmp(cmd, "sensor?") == 0) {
        Serial.print(dht.isError() ? 0 : 1);
        Serial.print('\n');
      }
    }
  }




Commands


TestController definition

The definition must be placed in "...\doc\TestController\Devices" (On a Windows PC) in a file that has a .txt extension. It is recommended to use the BRAND and NAME for the file name.

Manual for definitions is here

Code:
; TestController must be restarted before any changes in this file will be used.

; Manual is here: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html

#idString HKJ,TestDHT22,
#name HKJ Test DHT22
#handle DHT22
#port comfixedbaud
; Devices with 9600 baud will be automatic found if "Scan serial ports" are checked
#baudrate 9600


; A list of possible column name with unit and formatter (SI, Time, Int, D0..D9, X1..X9) 
; See here for all formatter types: https://lygte-info.dk/project/TestControllerConfigDevice%20UK.html#Data_type_definitions
#value Temperature °C D1
#value Humidity %RH D1
; Due to the ° (grad) symbol this file must be saved in unicode with a BOM marker to be reliable on all OS's

; How to poll for data, this is used for table and #values?
; a #askMode, #cmdMode and #prepareSample is used before this is string is used.
; Number of returned values must match number of columns defined with #value
; This is a single line command
#askValues values?

#cmdSetup indicatorNum Sensor_valid
:read: sensor?
No 0 red
Yes 1 green



How do it look in TestContoller

DHT22Values

This sensor shows temperature and humidity.

DHT22Settings

There is no settings for this example, but I shows the error flag.

There are many other ways to see and control the Arduino device in TestController.



Ideas for improvement

A offset compensation that is stored in EEPROM would probably improve accuracy slightly.
Display to show temperature (Use my library for LED displays).
Add interface to support generic scripting to use the module.



Scripting

One improvement for all the examples is to add generic scripting, this makes it possible for scripting to use any module with that functionality, usually without any changes to the script.

This is not necessary to use the module from scripting, it can always be used:
1) Right click in log window and select "Generate scripts, Mode & Setup, Clear saved commands" (If "Mode & Setup" is missing this is not necessary).
2) Use the "Setup" popup and select the commands
3) Right click in log window and select "Generate scripts, Mode & Setup, In log window" and the scripts for the setup will be put into the log window (It can also be saved as a menu entry).

Values from the devices can be found in variables while logging is active, these variables have the same name as the columns on the "Table" page or use the #values? command, it will list them all, including their current value.

But scripts generated that way may only work with that module, not with other models or brands.



Conclusion

These devices are for test and demonstration purpose, for this reason they are made fairly simple. All devices can be build and used, but as can be seen above I have a lot of ideas about how to improve them.
Checking the "Scan serial ports" on the "Load devices" page and storing the definitions in the specified directory is enough to make TestController find the devices, they do not need to be loaded.


Some Arduino devices will show up on my project page over time, they will include schematic, gerber files, Arduino program and TC definition and be considerable more complex than the examples shown here.



Notes & links

TestController main page
TestController manual for SCPI definitions
Projects, including Arduino library and devices for TestController
Temperature and humidity (DHT22/AM2302) library

Arduino code & TC definition