Vrekrer / Vrekrer_scpi_parser

Simple SCPI parser for Arduino
MIT License
87 stars 26 forks source link

Sub-Trees / commands not correctly decoded #26

Closed OutdoorRob closed 1 year ago

OutdoorRob commented 1 year ago

Hello together, I want to implement the following SCPI command structure:

DIAGnostic DIAGnostic:ADDRress? DIAGnostic:PRODuctNumber? DIAGnostic:DEViceSerialNumber? DIAGnostic:SET:ADDRress DIAGnostic:SET:PRODuctNumber DIAGnostic:SET:DEViceSerialNumber

I tried several ways but did not get it working completely

`parser.SetCommandTreeBase(F("DIAGnostic")); { parser.RegisterCommand(F(":ADDRress?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface) { // works });

    parser.RegisterCommand(F("PRODuctNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
    {
        // works
    });

    parser.RegisterCommand(F("DEViceSerialNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
    {
        // works
    });

    parser.SetCommandTreeBase(F(":SET"));
    {
        parser.RegisterCommand(F(":ADDRress"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
        {
            // never accessed
            interface.print("ACK 1\n");
        });

        parser.RegisterCommand(F(":PRODuctNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
        {
            // never accessed
            interface.print("ACK 2\n");
        });

        parser.RegisterCommand(F(":DEViceSerialNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
        {
            // never accessed
            interface.print("ACK 3\n");
        });
    }
}`

`parser.SetCommandTreeBase(F("DIAGnostic")); { parser.RegisterCommand(F(":ADDRress?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface) { // works });

parser.RegisterCommand(F("PRODuctNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // works
});

parser.RegisterCommand(F("DEViceSerialNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // works
});

parser.RegisterCommand(F(":SET:ADDRress"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // always accessed, also when sending SET:PRODuctNumber and SET:DEViceSerialNumber
    interface.print("ACK 1\n");
});

parser.RegisterCommand(F(":SET:PRODuctNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // never accessed
    interface.print("ACK 2\n");
});

parser.RegisterCommand(F(":SET:DEViceSerialNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // never accessed
    interface.print("ACK 3\n");
});

}`

I tried a couple more settings also without the SetCommandTreeBase but there are always some issues I can't figure out. I'm grateful for any advice.

Thanks and greets, Rob

OutdoorRob commented 1 year ago

This way works apart from the DEViceSerialNumber? which does not make any sense to me since the two commands and requests for ADDRress and PRODuctNumber work fine.

`parser.RegisterCommand(F("DIAGnostic:SET:ADDRress"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface) { // works interface.print("ACK 1\n"); });

parser.RegisterCommand(F("DIAGnostic:SET:PRODuctNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // works
    interface.print("ACK 2\n");
});

parser.RegisterCommand(F("DIAGnostic:SET:DEViceSerialNumber"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
{
    // works
    interface.print("ACK 3\n");
});

parser.SetCommandTreeBase(F("DIAGnostic"));
{
    parser.RegisterCommand(F(":ADDRress?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
    {
        // works
    });

    parser.RegisterCommand(F("PRODuctNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
    {
        // works
    });

    parser.RegisterCommand(F("DEViceSerialNumber?"), [](SCPI_Commands commands, SCPI_P parameters, Stream& interface)
    {
       // accesses :SET:DEViceSerialNumber instead
    });

}`

Vrekrer commented 1 year ago

Hello

Your code should be something like:

my_instrument.SetCommandTreeBase(F("DIAGnostic"));
  my_instrument.RegisterCommand(F(":ADDRress?"), &get_Address_Handler);
  my_instrument.RegisterCommand(F(":PRODuctNumber?"), &get_ProductNumber_Handler);
  my_instrument.RegisterCommand(F(":DEViceSerialNumber?"), &get_DeviveSerialNumber_Handler);
  my_instrument.RegisterCommand(F(":ADDRress"), &set_Address_Handler);
  my_instrument.RegisterCommand(F(":PRODuctNumber"), &set_ProductNumber_Handler);
  my_instrument.RegisterCommand(F(":DEViceSerialNumber"), &set_DeviveSerialNumber_Handler);

In that way, you make a address query using diag:addr? and set a value using diag:addr some_value

If you still what to use the SET branch then do:

my_instrument.SetCommandTreeBase(F("DIAGnostic"));
  my_instrument.RegisterCommand(F(":ADDRress?"), &get_Address_Handler);
  my_instrument.RegisterCommand(F(":PRODuctNumber?"), &get_ProductNumber_Handler);
  my_instrument.RegisterCommand(F(":DEViceSerialNumber?"), &get_DeviveSerialNumber_Handler);
my_instrument.SetCommandTreeBase(F("DIAGnostic:SET"));
  my_instrument.RegisterCommand(F(":ADDRress"), &set_Address_Handler);
  my_instrument.RegisterCommand(F(":PRODuctNumber"), &set_ProductNumber_Handler);
  my_instrument.RegisterCommand(F(":DEViceSerialNumber"), &set_DeviveSerialNumber_Handler);

Note that your tokens like DEViceSerialNumber do not follow the SCPI command syntax. The uppercase letters after the first lowercase letter will be treated also as lowercase.

If you still have problems, check if it is not memory problems (not enough RAM). Also see the CommandTree_Check example.

OutdoorRob commented 1 year ago

Hello @Vrekrer thanks for your quick answer and advice :-)

I tested my (extended) command structure as proposed in the CommandTree_Check example.

Don't know if the resulting commands are too long but now matter what hash_magic_number I use many of the hashes result in 255.

I tried this example with hash_magic_number = 2 / 3 / 37 / 113

#include "Arduino.h"
#include "Vrekrer_scpi_parser.h"

SCPI_Parser my_instrument;

void Identify(SCPI_C commands, SCPI_P parameters, Stream& interface) {
  //interface.println(F("Vrekrer,Hash check example,#00," VREKRER_SCPI_VERSION));
}

void DoNothing(SCPI_C commands, SCPI_P parameters, Stream& interface) {
}

void setup()
{
  //We change the `hash_magic_number` variable before registering the commands
  my_instrument.hash_magic_number = 19;

  my_instrument.RegisterCommand(F("*RST"), &DoNothing);
  my_instrument.RegisterCommand(F("SYSTem:VERSion?"), &DoNothing);
  my_instrument.RegisterCommand(F("*IDN?"), &DoNothing);
  my_instrument.RegisterCommand(F("*WAIt"), &DoNothing);
  my_instrument.RegisterCommand(F("*TST?"), &DoNothing);

  my_instrument.SetCommandTreeBase(F("CHANnel#:MEASure"));
    my_instrument.RegisterCommand(F(":OBJect:TEMPerature?"), &DoNothing);
    my_instrument.RegisterCommand(F(":AMBient:TEMPerature?"), &DoNothing);

  my_instrument.SetCommandTreeBase(F("DIAGnostic:HW"));
      my_instrument.RegisterCommand(F(":MODuleid?"), &DoNothing);
      my_instrument.RegisterCommand(F(":VARiant?"), &DoNothing);

  my_instrument.SetCommandTreeBase(F("DIAGnostic:HW:VERsion"));
      my_instrument.RegisterCommand(F(":MAJor?"), &DoNothing);
      my_instrument.RegisterCommand(F(":MINor?"), &DoNothing);
  my_instrument.SetCommandTreeBase(F("DIAGnostic:HW:MODEL"));
      my_instrument.RegisterCommand(F(":IDENTification?"), &DoNothing);
  my_instrument.SetCommandTreeBase(F("DIAGnostic:DEVice"));
      my_instrument.RegisterCommand(F(":ADDRress?"), &DoNothing);
      my_instrument.RegisterCommand(F(":PRODuctnumber?"), &DoNothing);
      my_instrument.RegisterCommand(F(":SERialnumber?"), &DoNothing);

  my_instrument.SetCommandTreeBase(F("DIAGnostic:SET:HW"));
      my_instrument.RegisterCommand(F(":MODuleid"), &DoNothing);
      my_instrument.RegisterCommand(F(":VARiant"), &DoNothing);
  my_instrument.SetCommandTreeBase(F("DIAGnostic:SET:HW:VERsion"));
      my_instrument.RegisterCommand(F(":MAJor"), &DoNothing);
      my_instrument.RegisterCommand(F(":MINor"), &DoNothing);
  my_instrument.SetCommandTreeBase(F("DIAGnostic:SET:HW:MODEL"));
      my_instrument.RegisterCommand(F(":IDENTification?"), &DoNothing);
  my_instrument.SetCommandTreeBase(F("DIAGnostic:SET:DEVice"));
      my_instrument.RegisterCommand(F(":ADDRress"), &DoNothing);
      my_instrument.RegisterCommand(F(":PRODuctnumber"), &DoNothing);
      my_instrument.RegisterCommand(F(":SERialnumber"), &DoNothing);

  Serial.begin(9600);
  while (!Serial) {;}

  my_instrument.PrintDebugInfo();
}

void loop()
{
  my_instrument.ProcessInput(Serial, "\n");
}

This is the debug output

TOKENS :15
  *RST
  SYSTem
  VERSion
  *IDN
  *WAIt
  *TST
  CHANnel#
  MEASure
  OBJect
  TEMPerature
  AMBient
  DIAGnostic
  HW
  MODuleid
  VARiant

VALID CODES :
  23
  65
  193
  27
  11
  24
  194
  134
  171
  255
  255
  255
  255
  255
  255
  156
  157
  255
  255
  255
  255
  255
  255

I will test a completely different structure tomorrow.

Vrekrer commented 1 year ago

Hello

That is a problem with the max number of allowed tokens, defined in the SCPI_MAX_TOKENS macro (15 by default),

The GitHub version of this library address this issue in the Configuration_Options example If you find any bug in the GitHub version, please let me know (in a new issue)