Pointer array to point subset of array

Hi everyone

I just learned the existence of pointer array, so pardon me if there is any misunderstanding I have about it. My question is, is it possible to have a pointer array pointing to a subset of array (especially char array)?

Backround to what I’m trying to do

I’m trying to read serial communication from my computer. The computer (Flight sim program) is trying to write to the arduino the state of buttons, lights, leds, etc. The data received will have a size somewhere under 500 characters (currently the limit is not programmed yet, but I will get around this later). Each data received has a ‘*’ as a sort of handshake to let the arduino know that the data is for it (not from it), followed by 4 characters to determine how long the new data is. This is then followed by the data itself, which comprises of the following:

  • A number determining how long the variable identifier is
  • The variable identifier (the length determined by the previous integer)
  • The value of the variable (indeterminate length, but should be under 4 byte)
  • A delimiter ‘#’

Each of this data is delimited by a ‘#’ except at the end
So for example:

  • 0018 4 6250 1 # 4 6251 100500 (without spaces)

I have learned about strtok(). I’m using it to seperate each data block. From my understanding, instead of creating a new array, this creates a pointer to the array instead.
My question is, is it possible to do something similar, eg. create a pointer array, say to point to the first block of the identifier (6250), instead of creating a new character array for this. This is especially useful for the variable value (eg. 100500 for the second block), since this value has indeterminate length

The following is the snippet of my code:

void readSerial()
{
  //Request Serial
  Serial.println("##");
  //Read Serial
  //Wait until '*' (start of new input) or timeout
  long startTime= millis();
  while (Serial.read() != '*' && millis() - startTime < comReceiveTimeout);
  if (Serial.available())
  {
    //Check how long is the new input (Exclude handshake and the first 4 digit (the input length))
    char il[4];   //input length in char
    for (byte i=0; i<4; i++)
    {
      il[i] = Serial.read();
    }
    //Convert to int
    unsigned int inputLength= atoi(il);

    //Check if input is not empty
    if (inputLength > 0)
    {
      //Construct the char array
      char input[inputLength];
      //Read until end of input (length of inputLength)
      for (unsigned int i=0; i< inputLength; i++)
      {
        input[i] = Serial.read();
      }
      
      //Split the string into tokens and store each variable
      char* token= strtok(input, "#");
      while (token != NULL)
      {
        //Check how long the offset identifier is
        byte offsetLength= token[1];
        //Get the offset ID
        char oi[offsetLength];  //offsetID in char
        for (byte i=0; i<offsetLength; i++)
        {
          oi[i]= token[i+2];
        }
        unsigned int offsetID= atoi(oi);
        //Get the size of value
        unsigned int valueSize= (sizeof(token)/sizeof(char)) - (1 + offsetLength);
        //Get the value
        char v[valueSize]; //value in char
        for (byte i=0; i<valueSize; i++)
        {
          v[i]= token[i+offsetLength+1];
        }
        // unsigned long value= atoi(v);
        //Set the value to the offset
        setValue(offsetID, v);
  
        //Get the next token
        token= strtok(NULL, "#");
      }
    }
  }
}

Also attached is the arduino code, along with a lua code for FSUIPC (in.txt), in case anyone needs it

Note: I haven’t fully tested the above code snippet yet, so I’m not sure yet if I’m doing the entire thing (especially the string token) right. However, my question still stands: Is it possible to create a pointer array to point to a subset of array? If so, how? If not, is there an alternative?

Any help will be greatly appreciated
Cheers!

mcp_v0.2.ino (7.5 KB)

arduino1.txt (4.38 KB)

Not sure I understand where you're going with this, but:

char *pointerArray[ARRAY_SIZE];

will define an array of pointers to char (i.e. char *). Every element of this array can point to a char or any position in an array of char. You could use two consecutive elements to point to the beginning and end of your substring. Note, this substring won't necessarily be terminated by the '\0' necessary to qualify it as a c-string.

How do you exactly select a subset of the main array? Do you just use a loop? Is there a more efficient way?

Like I said, I really don't know what you're trying to accomplish. Please post your entire code using code tags. That will be much more helpful than the snippet.

ariz9: How do you exactly select a subset of the main array? Do you just use a loop? Is there a more efficient way?

What do you mean by "select" in this context ? Copy to somewhere else or use the data in place knowing where it starts and ends ?

gfvalvo:
Like I said, I really don’t know what you’re trying to accomplish. Please post your entire code using code tags. That will be much more helpful than the snippet.

I have made a lot of changes, here is the newer version of my code. There are a lot of definitions and variable initialisation on top, but I can’t fit it in this post due to character limit. I will post it on the next post

void setup() {
  Serial.begin(250000);
  
  //Loop through all variables with offset and calculate its offset length
  //Do it for all types of data
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_BYTE;i++)
  {
    variableTracker_byte[i].offsetLength= calculateUIntDigit(variableTracker_byte[i].offset);
  }
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_INT;i++)
  {
    variableTracker_int[i].offsetLength= calculateUIntDigit(variableTracker_byte[i].offset);
  }
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_UINT;i++)
  {
    variableTracker_uint[i].offsetLength= calculateUIntDigit(variableTracker_byte[i].offset);
  }
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_ULONG;i++)
  {
    variableTracker_ulong[i].offsetLength= calculateUIntDigit(variableTracker_byte[i].offset);
  }
}

void loop() {
  //Check if new data is arriving
  if (Serial.available() && Serial.read() == '*')
  {
    readSerial();
  }
  currentTime= millis();
  if (currentTime - previousTime > clockCycle)
  {
    if (!(cycleCounter % readCycle))
    {
      //Read and update displays
      //updateDisplays();
      readButtons();
      readEncoders();
    }
    if (!(cycleCounter % comSendCycle))
    {
      writeSerial();
    }
    if (!(cycleCounter % comReceiveCycle))
    {
      //Request Serial
      Serial.println("##");
    }
    cycleCounter++;
  }
}

void readButtons()
{
  
}

void readEncoders()
{
  headingEncoderCurrent= headingEncoder.read()/4;
  //int headingEncoderDiff= headingEncoderPrevious - headingEncoderCurrent;
  if (headingEncoderPrevious != headingEncoderCurrent)
  {
     heading -= (headingEncoderCurrent - headingEncoderPrevious);
     heading = to360(heading);
     //Note that the heading has changed
     heading_changed= 1;
     headingEncoderPrevious = headingEncoderCurrent;
  }
}

void writeSerial()
{
  //Check for all types of data
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_BYTE;i++)
  {
    if (*variableTracker_byte[i].changed == 1)
    {
      Serial.print(variableTracker_byte[i].offsetLength);
      Serial.print(variableTracker_byte[i].offset);
      Serial.println(*variableTracker_byte[i].value);
      *variableTracker_byte[i].changed = 0;
    }
  }
  
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_INT;i++)
  {
    if (*variableTracker_int[i].changed == 1)
    {
      Serial.print(variableTracker_int[i].offsetLength);
      Serial.print(variableTracker_int[i].offset);
      Serial.println(*variableTracker_int[i].value);
      *variableTracker_int[i].changed = 0;
    }
  }
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_UINT;i++)
  {    
    if (*variableTracker_uint[i].changed == 1)
    {
      Serial.print(variableTracker_uint[i].offsetLength);
      Serial.print(variableTracker_uint[i].offset);
      Serial.println(*variableTracker_uint[i].value);
      *variableTracker_uint[i].changed = 0;
    }
  }
  for (byte i= 0; i<SIZEOF_VARIABLETRACKER_ULONG;i++)
  {
    if (*variableTracker_ulong[i].changed == 1)
    {
      Serial.print(variableTracker_ulong[i].offsetLength);
      Serial.print(variableTracker_ulong[i].offset);
      Serial.println(*variableTracker_ulong[i].value);
      *variableTracker_ulong[i].changed = 0;
    }
  }
  
}



//Serial input example:
//*2375259120#52590210000#5259180#5259170#5259280#5259061#5259110#5259220#5259270
//#5258880#5259260#5259250#5259240#5259200#5259190#5258960#5259090#5259070#5259210
//#5259230#5259160#5259150#5258980#5258970#5259040#5259140#525892100#5259104#5259000
void readSerial()
{
  //Check how long is the new input (Exclude handshake and the first 3 digit (the input length))
  char il[3];   //input length in char
  for (byte i=0; i<3; i++)
  {
    il[i] = Serial.read();
  }
  //Convert to int
  unsigned int inputLength= atoi(il);

  //Check if input is not empty
  if (inputLength > 0)
  {
    
    /*
    //Construct the char array
    char input[inputLength];
    //Read until end of input (length of inputLength)
    for (unsigned int i=0; i< inputLength; i++)
    {
      input[i] = Serial.read();
    }
    
    //Split the string into tokens and store each variable
    char* token= strtok(input, "#");
    while (token != NULL)
    {
      //Check how long the offset identifier is
      byte offsetLength= token[0];
      //Get the offset ID
      char oi[offsetLength];  //offsetID in char
      for (byte i=0; i<offsetLength; i++)
      {
        oi[i]= token[i+1];
      }
      unsigned int offsetID= atoi(oi);
      //Get the size of value
      unsigned int valueSize= (sizeof(token)/sizeof(char)) - (1 + offsetLength);
      //Get the value
      char v[valueSize]; //value in char
      for (byte i=0; i<valueSize; i++)
      {
        v[i]= token[i+offsetLength+1];
      }
      // unsigned long value= atoi(v);
      //Set the value to the offset
      setValue(offsetID, v);

      //Get the next token
      token= strtok(NULL, "#");
    }*/
  }
}


void setValue(unsigned int offsetID, char* value)
{
  switch(offsetID)
  {
    case MCP_Course:
      course= atoi(value);
    case MCP_IASMach:
      iasMach= atoi(value);
    case MCP_IASBlank:
      iasBlank= atoi(value);
    case MCP_IASOverspeedFlash:
      iasOverspeedFlash= atoi(value);
    case MCP_IASUnderspeedFlash:
      iasUnderspeedFlash= atoi(value);
    case MCP_Heading:
      heading= to360(atoi(value));
    case MCP_Altitude:
      altitude= atoi(value);
    case MCP_VertSpeed:
      vertSpeed= atoi(value);
    case MCP_VertSpeedBlank:
      vertSpeedBlank= atoi(value);
    case MCP_FDSw:
      fdSw= atoi(value);
    case MCP_ATArmSw:
      atArmSw= atoi(value);
    case MCP_BankLimitSel:
      bankLimitSel= atoi(value);
    case MCP_DisengageBar:
      disengageBar= atoi(value);
    case MCP_annunFD:
      annunFD= atoi(value);
    case MCP_annunATArm:
      annunATArm= atoi(value);
    case MCP_annunN1:
      annunN1= atoi(value);
    case MCP_annunSPEED:
      annunSPEED= atoi(value);
    case MCP_annunVNAV:
      annunVNAV= atoi(value);
    case MCP_annunLVL_CHG:
      annunLVL_CHG= atoi(value);
    case MCP_annunHDG_SEL:
      annunHDG_SEL= atoi(value);
    case MCP_annunLNAV:
      annunLNAV= atoi(value);
    case MCP_annunVOR_LOC:
    annunVOR_LOC= atoi(value);
    case MCP_annunAPP:
      annunAPP= atoi(value);
    case MCP_annunALT_HOLD:
      annunALT_HOLD= atoi(value);
    case MCP_annunVS:
      annunVS= atoi(value);
    case MCP_annunCMD_A:
      annunCMD_A= atoi(value);
    case MCP_annunCWS_A:
      annunCWS_A= atoi(value);
    case MCP_annunCMD_B:
      annunCMD_B= atoi(value);
    case MCP_annunCWS_B:
      annunCWS_B= atoi(value);
    break;
  }
}

int to360(int value)
{
  if (value > 359)
  {
    value = 0;
  }
  else if (value < 0)
  {
    value = 359;
  }
  return value;
}

byte calculateUIntDigit(int number)
{
  unsigned int key= 1;
  for (byte i=1; i <=5; i++)
  {
    key *= 10;
    if (number/ key <= 0)
    {
      return i;
    }
  }
}

I’m trying (if possible)to separate the serial input using pointer, without copying it.

UKHeliBob:
What do you mean by “select” in this context ? Copy to somewhere else or use the data in place knowing where it starts and ends ?

I mean I’m trying to know where each value of each data block starts and ends, without copying them. But I would also prefer not to just know where it starts and end, but also have a pointer selecting a subset of the array (rather than the entire array), and manipulate values that way (rather than copying the values, since I believe this will take up a lot of memory). For example above:

  • 0018 4 6250 1 # 4 6251 100500 (without spaces)

Say I would like to manipluate the value 10500. I know it starts from 18 up to 23. But is it possible to do something like this:

char *value= input[from index 17 to 22]

without copying the data

I’m sorry if I’m not clear enough

@gfvalvo Here are the definitions and everything else on top of the setup()

#include <Encoder.h>
#define clockCycle    4 //ms
#define readCycle     20 //cycle
#define comReceiveCycle 10000 //cycle
#define comSendCycle 2000 //cycle
#define comReceiveTimeout 100 //ms
/*
  1 cycle is 4 ms (customisable above)
  Receive data every 20 cycle (customisable above)
  Has a timeout of 100 ms (customisable above
  read buttons/encoders every cycle
  update display (7 segments and leds) every cycle

  Control IDs
  Control names               Control ID
  EVT_MCP_COURSE_SELECTOR_L   376
  EVT_MCP_FD_SWITCH_L         378
  EVT_MCP_AT_ARM_SWITCH       380
  EVT_MCP_N1_SWITCH           381
  EVT_MCP_SPEED_SWITCH        382
  EVT_MCP_CO_SWITCH           383
  EVT_MCP_SPEED_SELECTOR      384
  EVT_MCP_VNAV_SWITCH         386
  EVT_MCP_SPD_INTV_SWITCH     387
  EVT_MCP_BANK_ANGLE_SELECTOR 389
  EVT_MCP_HEADING_SELECTOR    390
  EVT_MCP_LVL_CHG_SWITCH      391
  EVT_MCP_HDG_SEL_SWITCH      392
  EVT_MCP_APP_SWITCH          393
  EVT_MCP_ALT_HOLD_SWITCH     394
  EVT_MCP_VS_SWITCH           395
  EVT_MCP_VOR_LOC_SWITCH      396
  EVT_MCP_LNAV_SWITCH         397
  EVT_MCP_ALTITUDE_SELECTOR   400
  EVT_MCP_VS_SELECTOR         401
  EVT_MCP_CMD_A_SWITCH        402
  EVT_MCP_CMD_B_SWITCH        403
  EVT_MCP_CWS_A_SWITCH        404
  EVT_MCP_CWS_B_SWITCH        405
  EVT_MCP_DISENGAGE_BAR       406
  EVT_MCP_FD_SWITCH_R         407
  EVT_MCP_COURSE_SELECTOR_R   409
  EVT_MCP_ALT_INTV_SWITCH     885

*/
struct vt_byte
{
  byte *value;
  byte *changed;
  unsigned int offset;
  byte offsetLength;
};
struct vt_int
{
  int *value;
  byte *changed;
  unsigned int offset;
  byte offsetLength;
};
struct vt_uint
{;
  unsigned int *value;
  byte *changed;
  unsigned int offset;
  byte offsetLength;
};
struct vt_long
{
  long *value;
  byte *changed;
  unsigned int offset;
  byte offsetLength;
};
struct vt_ulong
{
  unsigned long *value;
  byte *changed;
  unsigned int offset;
  byte offsetLength;
};


//      Event                   Event ID
#define MCP_Course              0x6520
#define MCP_IASMach             0x6524
#define MCP_IASBlank            0x6528
#define MCP_IASOverspeedFlash   0x6529
#define MCP_IASUnderspeedFlash  0x652A
#define MCP_Heading             0x652C
#define MCP_Altitude            0x652E
#define MCP_VertSpeed           0x6530
#define MCP_VertSpeedBlank      0x6532
#define MCP_FDSw                0x6533
#define MCP_ATArmSw             0x6535
#define MCP_BankLimitSel        0x6536
#define MCP_DisengageBar        0x6537
#define MCP_annunFD             0x6538
#define MCP_annunATArm          0x653A
#define MCP_annunN1             0x653B
#define MCP_annunSPEED          0x653C
#define MCP_annunVNAV           0x653D
#define MCP_annunLVL_CHG        0x653E
#define MCP_annunHDG_SEL        0x653F
#define MCP_annunLNAV           0x6540
#define MCP_annunVOR_LOC        0x6541
#define MCP_annunAPP            0x6542
#define MCP_annunALT_HOLD       0x6543
#define MCP_annunVS             0x6544
#define MCP_annunCMD_A          0x6545
#define MCP_annunCWS_A          0x6546
#define MCP_annunCMD_B          0x6547
#define MCP_annunCWS_B          0x6548

//      Control                 Control ID
#define EVT_MCP_CRS_L_SET       14500
#define EVT_MCP_CRS_R_SET       14501
#define EVT_MCP_IAS_SET         14502
#define EVT_MCP_MACH_SET        14503  // Sets MCP MACH (if active) to parameter*0.01 (e.g. send 78 to set M0.78)
#define EVT_MCP_HDG_SET         14504
#define EVT_MCP_ALT_SET         14505
#define EVT_MCP_VS_SET          14506

byte course;
unsigned long iasMach;
byte iasBlank;
byte iasOverspeedFlash;
byte iasUnderspeedFlash;
int heading;
unsigned int altitude;
int vertSpeed;
byte vertSpeedBlank;
byte fdSw;
byte atArmSw;
byte bankLimitSel;
byte disengageBar;
byte annunFD;
byte annunATArm;
byte annunN1;
byte annunSPEED;
byte annunVNAV;
byte annunLVL_CHG;
byte annunHDG_SEL;
byte annunLNAV;
byte annunVOR_LOC;
byte annunAPP;
byte annunALT_HOLD;
byte annunVS;
byte annunCMD_A;
byte annunCWS_A;
byte annunCMD_B;
byte annunCWS_B;

byte course_changed = 0;
byte iasMach_changed = 0;
byte iasBlank_changed = 0;
byte iasOverspeedFlash_changed = 0;
byte iasUnderspeedFlash_changed = 0;
byte heading_changed = 0;
byte altitude_changed = 0;
byte vertSpeed_changed = 0;
byte vertSpeedBlank_changed = 0;
byte fdSw_changed = 0;
byte atArmSw_changed = 0;
byte bankLimitSel_changed = 0;
byte disengageBar_changed = 0;
byte annunFD_changed = 0;
byte annunATArm_changed = 0;
byte annunN1_changed = 0;
byte annunSPEED_changed = 0;
byte annunVNAV_changed = 0;
byte annunLVL_CHG_changed = 0;
byte annunHDG_SEL_changed = 0;
byte annunLNAV_changed = 0;
byte annunVOR_LOC_changed = 0;
byte annunAPP_changed = 0;
byte annunALT_HOLD_changed = 0;
byte annunVS_changed = 0;
byte annunCMD_A_changed = 0;
byte annunCWS_A_changed = 0;
byte annunCMD_B_changed = 0;
byte annunCWS_B_changed = 0;


#define SIZEOF_VARIABLETRACKER_BYTE 20
vt_byte variableTracker_byte[] = 
{
  {&course, &course_changed, MCP_Course},
  {&iasBlank, &iasBlank_changed, MCP_IASBlank},
  {&iasOverspeedFlash, &iasOverspeedFlash_changed, MCP_IASOverspeedFlash},
  {&iasUnderspeedFlash, &iasUnderspeedFlash_changed, MCP_IASUnderspeedFlash},
  {&vertSpeedBlank, &vertSpeedBlank_changed, MCP_VertSpeedBlank},
  {&fdSw, &fdSw_changed, MCP_FDSw},
  {&atArmSw, &atArmSw_changed, MCP_ATArmSw},
  {&bankLimitSel, &bankLimitSel_changed, MCP_BankLimitSel},
  {&disengageBar, &disengageBar_changed, MCP_DisengageBar},
  {&annunFD, &annunFD_changed, MCP_annunFD},
  {&annunATArm, &annunATArm_changed, MCP_annunATArm},
  {&annunN1, &annunN1_changed, MCP_annunN1},
  {&annunSPEED, &annunSPEED_changed, MCP_annunSPEED},
  {&annunVNAV, &annunVNAV_changed, MCP_annunVNAV},
  {&annunLVL_CHG, &annunLVL_CHG_changed, MCP_annunLVL_CHG},
  {&annunHDG_SEL, &annunHDG_SEL_changed, MCP_annunHDG_SEL},
  {&annunLNAV, &annunLNAV_changed, MCP_annunLNAV},
  {&annunVOR_LOC, &annunVOR_LOC_changed, MCP_annunVOR_LOC},
  {&annunAPP, &annunAPP_changed, MCP_annunAPP},
  {&annunALT_HOLD, &annunALT_HOLD_changed, MCP_annunALT_HOLD},
  {&annunVS, &annunVS_changed, MCP_annunVS},
  {&annunCMD_A, &annunCMD_A_changed, MCP_annunCMD_A},
  {&annunCWS_A, &annunCWS_A_changed, MCP_annunCWS_A},
  {&annunCMD_B, &annunCMD_B_changed, MCP_annunCMD_B},
  {&annunCWS_B, &annunCWS_B_changed, MCP_annunCWS_B}
};

#define SIZEOF_VARIABLETRACKER_INT 2
vt_int variableTracker_int[] =
{
  {&heading, &heading_changed, EVT_MCP_HDG_SET},
  {&vertSpeed, &vertSpeed_changed, MCP_VertSpeed}
};

#define SIZEOF_VARIABLETRACKER_UINT 1
vt_uint variableTracker_uint[] =
{
  {&altitude, &altitude_changed, MCP_Altitude}
};

#define SIZEOF_VARIABLETRACKER_ULONG 1
vt_ulong variableTracker_ulong[] =
{
  {&iasMach, &iasMach_changed, MCP_IASMach}
};


//Encoder
int headingEncoderCurrent, headingEncoderPrevious;
Encoder headingEncoder(2,3);

unsigned long currentTime, previousTime;
unsigned int cycleCounter;
char *value[]= input[from index 17 to 22]

No, that is not valid C / C++ syntax.

A pointer can only point to one place, it can't point to a range. You would need 2 pointers for that, one pointing at the beginning of the range, the other at the end of the range.

Also, if you have some variables defined something like this:

someDataType *startPointer, *endPointer;
uint16_t subSetLength;

Then this expression will give you the number of elements in the selected subset:

subSetLength = (endPointer - startPointer) / sizeof(someDataType) + 1;

You can manipulate the data in-place if you're using functions that will take start / end pointers or start-pointer / length. But be careful if you're using functions that expect pointers to c-strings as your subset probably won't contain the required '\0' terminator.

Thanks gfvalvo, this answers my questions!

Cheers!