Open Arduino

Enter Your Project for a chance to win a grand prize for the most innovative use of Arduino or a $200 shopping cart! The Birthday Special: Arduino Projects for Arduino Day!

Back to The Project14 homepage

Project14 Home
Monthly Themes
Monthly Theme Poll

 

Arduino in Test Instrumentation

 

To celebrate Project 14's birthday, I'm going to make a programmable switch with an Arduino UNO.

This post describes the firmware and SCPI api.

 

SCPI Commands

 

The instrument supports the following commands:

CommandDescriptionExample
*IDN?instrument identity

*IDN?

element14,Arduino SCPI Switch,1,0

*RSTreset instrument. Set all switches off*RST
*CLSsame as *RST*CLS
:DIGITAL:SWITCH# ON|OFFturn a switch on or off. 1 based:DIGI:SWIT1 ON
:DIGITAL:SWITCH#?get a switch' status. 1 based

:DIGITAL:SWITCH1?

OFF

 

Firmware

 

Here's a link to the GIST: https://gist.github.com/jancumps/9573730e7bac392efcdbfa24e722bf4d#file-scpiswitch-ino

 

I don' know what's wrong with element14's code highlighter these days, but it fails to show #includes with angle brackets.

The first two lines are:

#include <scpiparser.h>

#include <Arduino.h>

 

#include 
#include 


#define NUMBEROFCHANNELS 2


struct scpi_parser_context ctx;


scpi_error_t identify(struct scpi_parser_context* context, struct scpi_token* command);
scpi_error_t reset(struct scpi_parser_context* context, struct scpi_token* command);
scpi_error_t get_digital_1(struct scpi_parser_context* context, struct scpi_token* command);
scpi_error_t get_digital_2(struct scpi_parser_context* context, struct scpi_token* command);
scpi_error_t set_digital_1(struct scpi_parser_context* context, struct scpi_token* command);
scpi_error_t set_digital_2(struct scpi_parser_context* context, struct scpi_token* command);


bool bDigital [NUMBEROFCHANNELS] = {};
int  iPins [NUMBEROFCHANNELS] = {2,3};


void setup() {
  struct scpi_command* digital;
  int i = 0;
  Serial.begin(38400);
  
  for (i = 0; i < NUMBEROFCHANNELS; i++) {
    pinMode(iPins[i], OUTPUT);
    digitalWrite(iPins[i], LOW);    
  }




  /* First, initialise the parser. */
  scpi_init(&ctx);


  /*
   * After initialising the parser, we set up the command tree.  Ours is
   *
   *  *IDN?         -> identify
   *  *RST          -> reset
   *  :DIGI
   *    :SWIT1    -> channel 1
   *    :SWIT2    -> channel 2
   */
  scpi_register_command(ctx.command_tree, SCPI_CL_SAMELEVEL, "*IDN?", 5, "*IDN?", 5, identify);
  scpi_register_command(ctx.command_tree, SCPI_CL_SAMELEVEL, "*RST", 4, "*RST", 4, reset);
  scpi_register_command(ctx.command_tree, SCPI_CL_SAMELEVEL, "*CLS", 4, "*CLS", 4, reset);


  digital = scpi_register_command(ctx.command_tree, SCPI_CL_CHILD, "DIGITAL", 7, "DIGI", 4, NULL);


  scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH1?", 8, "SWIT1?", 6, get_digital_1);
  scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH2?", 8, "SWIT2?", 6, get_digital_2);


  scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH1", 7, "SWIT1", 5, set_digital_1);
  scpi_register_command(digital, SCPI_CL_CHILD, "SWITCH2", 7, "SWIT2", 5, set_digital_2);




}


void loop() {
  char line_buffer[256];
  unsigned char read_length;


  while(1)   {
    /* Read in a line and execute it. */
    read_length = Serial.readBytesUntil('\n', line_buffer, 256);  
    if(read_length >0)  
      if(line_buffer[read_length-1]=='\r')  // thank you jon clift
        read_length = read_length - 1;    if(read_length > 0)     {
      scpi_execute_command(&ctx, line_buffer, read_length);
    }
  }
}




 


/*
 * Respond to *IDN?
 */
scpi_error_t identify(struct scpi_parser_context* context, struct scpi_token* command) {
  scpi_free_tokens(command);


  Serial.println("Project14,Arduino SCPI Switch,1,0");
  return SCPI_SUCCESS;
}


/*
 * Respond to *RST and *CLS
 */
scpi_error_t reset(struct scpi_parser_context* context, struct scpi_token* command) {
  scpi_free_tokens(command);
  int i = 0;


  for (i = 0; i < NUMBEROFCHANNELS; i++) {
    digitalWrite(iPins[i], LOW);
    bDigital[i] = 0;    
  }
  return SCPI_SUCCESS;
}




scpi_error_t get_digital(int channel, struct scpi_parser_context* context, struct scpi_token* command) {
  if (bDigital[channel] ) {
    Serial.println("ON");
  } else {
    Serial.println("OFF");
   
  }
  scpi_free_tokens(command);
  return SCPI_SUCCESS;  
}




/**
 * get digital 1 status
 */
scpi_error_t get_digital_1(struct scpi_parser_context* context, struct scpi_token* command) {
  return get_digital(0, context, command);
}


/**
 * get digital 2 status
 */
scpi_error_t get_digital_2(struct scpi_parser_context* context, struct scpi_token* command) {
  return get_digital(1, context, command);
}


scpi_error_t set_digital(int channel, struct scpi_parser_context* context, struct scpi_token* command) {
  struct scpi_token* args;
  bool bSuccess = false;
  bool bSwitch = false;


  args = command;
  while(args != NULL && args->type == 0) {
    args = args->next;
  }


  if (args->length == 1) {
    if (args->value[0] == '0') {
      bSuccess = true;
    }
    if (args->value[0] == '1') {
      bSuccess = true;
      bSwitch = true;
    }
  }


  if (args->length == 2) {
    if ((args->value[0] == 'O') || (args->value[0] == 'o')) {
      if ((args->value[1] == 'N') || (args->value[1] == 'n')) {
        bSuccess = true;
        bSwitch = true;
      }    
    }
  }


  if (args->length == 3) {
    if ((args->value[0] == 'O') || (args->value[0] == 'o')) {
      if ((args->value[1] == 'F') || (args->value[1] == 'f')) {
        if ((args->value[2] == 'F') || (args->value[2] == 'f')) {
        bSuccess = true;
        }
      }
    }
  }


  scpi_free_tokens(command);


//  Serial.print("Success: ");
//  Serial.print(bSuccess ? "y" : "n");
//  Serial.print(" channel: ");
//  Serial.print(channel);
//  Serial.print(" pin ");
//  Serial.print(iPins[channel]);
//  Serial.print(" switch ");
//  Serial.print(bSwitch ? "on" : "off");
//  Serial.print("\n");


  if (bSuccess) {    
    digitalWrite(iPins[channel], bSwitch? HIGH : LOW);
    bDigital[channel] = bSwitch;
  }
  
  return bSuccess ? SCPI_SUCCESS : SCPI_COMMAND_NOT_FOUND;
 
}


scpi_error_t set_digital_1(struct scpi_parser_context* context, struct scpi_token* command) {
  return set_digital(0, context, command);
}


scpi_error_t set_digital_2(struct scpi_parser_context* context, struct scpi_token* command) {
  return set_digital(1, context, command);
}

 

It isn't difficult to trace the SCPI commands back to the firmware functions. Not a lot is happening here.

Please check the code. If there's a construct that you have questions about, post a comment and I'll elaborate.

 

If you haven't got the SCPI library installed yet, check the previous post: Arduino in Test Instrumentation - Part 1: SCPI Lib.

 

Related Blog
Arduino in Test Instrumentation - Intro: SCPI Programmable Switch
Arduino in Test Instrumentation - Part 1: SCPI Lib
Arduino in Test Instrumentation - Part 2: Firmware
Arduino in Test Instrumentation - Part 3a: LabVIEW Driver Intitialisation Block
Arduino in Test Instrumentation - Part 3b: LabVIEW Driver Switch Control Block
Arduino in Test Instrumentation - Part 3c: LabVIEW Driver Read Status Block
Arduino in Test Instrumentation - Outro: LabVIEW Example