Button and char string questions [SOLVED]

Greetings;

I'm working on this project with a bunch of sensors, which need to be periodically calibrated, data entry being done on a TFT touch sreen.

The thing is working, but I have a few style questions, related to strings and buttons:

Button Definitions:

Adafruit_GFX_Button buttons[21];
/* create 21 buttons, in classic candybar phone style */
char buttonlabels[21][5] = {"EC", "RTD", "pH",
                            "cal", "Min", "low",
                            "hig", "tst", "CLR",
                            "1", "2", "3",
                            "4", "5", "6",
                            "7", "8", "9",
                            ".", "0", "RTN"
                           };
uint16_t buttoncolors[21] = {ILI9341_GREEN, ILI9341_GREEN , ILI9341_GREEN,
                             ILI9341_GREEN, ILI9341_GREEN , ILI9341_GREEN,
                             ILI9341_GREEN, ILI9341_YELLOW , ILI9341_DARKGREY,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_DARKCYAN, ILI9341_BLUE, ILI9341_RED,
                            }; //

Here, I draw the buttons:

// create buttons
  for (uint8_t row = 0; row < 7; row++) {
    for (uint8_t col = 0; col < 3; col++) {
      buttons[col + row * 3].initButton(&tft, BUTTON_X + col * (BUTTON_W + BUTTON_SPACING_X),
                                        BUTTON_Y + row * (BUTTON_H + BUTTON_SPACING_Y), // x, y, w, h, outline, fill, text
                                        BUTTON_W, BUTTON_H, ILI9341_WHITE, buttoncolors[col + row * 3], ILI9341_WHITE,
                                        buttonlabels[col + row * 3], BUTTON_TEXTSIZE);
      buttons[col + row * 3].drawButton();
    }
  }

Which nicely puts displays all the characters defined above.

Now, when I push a button:

  // now we can ask the buttons if their state has changed
  for (uint8_t b = 0; b < 21; b++) {
    if (buttons[b].justReleased()) {
      Serial.print("Released: "); Serial.println(b);
      buttons[b].drawButton();  // draw normal
    }

    if (buttons[b].justPressed()) {
      buttons[b].drawButton(true);  // draw invert!
      //-----------------------------------------------------------------------------
      switch (b) {                    // switch case based on the BUTTON PUSHED
        case 0:                       // Pushed EC ---> Display "100:"
          Serial.println("EC");       //
          textfield[textfield_i] = '1';
          textfield_i++;
          textfield[textfield_i] = '0';
          textfield_i++;
          textfield[textfield_i] = '0';
          textfield_i++;
          textfield[textfield_i] = ':';
          textfield_i++;

          textfield[textfield_i] = 0; // zero terminate
          break;                          // exits the switch case.

        case 1:                         // Pushed RTD ---> Display "102:"
          Serial.println("RTD");    //
          textfield[textfield_i] = '1';
          textfield_i++;
          textfield[textfield_i] = '0';
          textfield_i++;
          textfield[textfield_i] = '2';
          textfield_i++;
          textfield[textfield_i] = ':';
          textfield_i++;
          textfield[textfield_i] = 0; // zero terminate

          break;                          // exits the switch case.

        case 2:                         // Pushed pH ---> Display "99:"
          Serial.println("pH");    //
          textfield[textfield_i] = '9';
          textfield_i++;
          textfield[textfield_i] = '9';
          textfield_i++;
          textfield[textfield_i] = ':';
          textfield_i++;
          textfield[textfield_i] = 0; // zero terminate
          break;                          // exits the switch case.

   // other cases removed for clarity
        case 9 ... 19:    //digits and decimal point
          if (textfield_i < TEXT_LEN) {
            textfield[textfield_i] = buttonlabels[b][0];
            textfield_i++;
            textfield[textfield_i] = 0; // zero terminate
          }
          break;

        case 20:         //Complete command, send to EZO
          Serial.print("Entering Data: ");        
          status(textfield);
          channel = atoi(strtok(textfield, ":"));  // Let's parse the string at each colon, extracting the EZO address
          Serial.print(channel); Serial.print(":");
          cmd = strtok(NULL, ":");                    // Let's parse the string at each colon, extract the command  --------------> will it work for stuff other than r??
          Serial.println(cmd);
          I2C_call();                            // send to I2C

          textfield_i = 0;                      //reset textfield string
          textfield[textfield_i] = '\0';        // for next command after RTN
          // update the current text field
          //Serial.println(textfield);
          tft.setCursor(TEXT_X + 2, TEXT_Y + 10);
          tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
          tft.setTextSize(TEXT_TSIZE);
          tft.print("              ");
          break;

      }
      // update the current text field
      //Serial.println(textfield);
      tft.setCursor(TEXT_X + 2, TEXT_Y + 10);
      tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
      tft.setTextSize(TEXT_TSIZE);
      tft.print(textfield);
      //------------------------------------------------------------------------------
      delay(100); // UI debouncing
    }
  }

I write some contents (not the same as the button text) into a char array, textfield.

Notice, that I am writing every single character, one at a time and then incrementing the index pointer.

There must be a more elegant way of doing this...???

I tried various"100"; followed by incrementing the index 4 times. didn't work
I tried '100:'; followed by index+4. didn't work

As an aside: If I wanted to print the label of the pressed button (variable length labels) into my textfield, is there a way to do that? case 9 ...19,

        case 9 ... 19:    //digits and decimal point
          if (textfield_i < TEXT_LEN) {
            textfield[textfield_i] = buttonlabels[b][0];
            textfield_i++;
            textfield[textfield_i] = 0; // zero terminate
          }
          break;

used for the digits 0-9 uses this, but only manages to print the first (and only) digit.

Thanks for your help.

Cheers;

i2C_Touchscreen_Interactive_Case_rev1.zip (5.65 KB)

         textfield[textfield_i] = '1';
          textfield_i++;
          textfield[textfield_i] = '0';
          textfield_i++;
          textfield[textfield_i] = '0';
          textfield_i++;
          textfield[textfield_i] = ':';
          textfield_i++;

          textfield[textfield_i] = 0; // zero terminate

or

strcpy(textfield, "100:");

Not that I would grant the "elegant" status to strcpy()... :slight_smile:

Thanks, Aarg

What happens to the index, or how can I recover it?

Why do you need the index? If you're constructing a longer string from parts, use strcat().

aarg:
Why do you need the index? If you're constructing a longer string from parts, use strcat().

I have a DEL button, that needs to go back one character to erase the last entry... It was part of the keypad sketch I have.

Maybe there's another way to achieve that with strcopy or strcat.

Cheers

First of all, I confess to the horror of several times constructing a char array by indexing. I didn't have time to read the entire sketch, but I have a feeling from the last post that you might have combined input with construction of this string. At the moment, the way I do things like that is take input separately and do the string manipulation after I have all the data. So I have a destructive backspace, but the code that receives the line never sees any of that, because everything is only passed along after a carriage return (you can insert your favourite delimiter here).

Delete...

textfield[strlen(textfield)-1] = 0;

Although you probably want to make sure you don’t have an empty string first....

            strcat(textfield, buttonlabels[b][0]);
            Serial.println(buttonlabels[b][0]);

Or

            strcpy(textfield, buttonlabels[b][0]);
            Serial.println(buttonlabels[b][0]);

I get garbage...

probably, something to do with buttonlabels [0] not being processed properly...

...or textfield. We won't know unless you show us!

What value is "b"? Perhaps you are reading beyond the array bounds.

Are you trying to add/copy TO the buttonlabels, or TOthe textfield?

char *strcat(char *destination, const char *source)
char* strcpy(char* destination, const char* source);

It looks to me as though you are going the wrong way. Surely, after copying or concatenating to a field you'd want to...

Serial.println(destination);

hello pcbbc;

The working code is the last snippet in my original post:

        case 9 ... 19:    //digits and decimal point
          if (textfield_i < TEXT_LEN) {
            textfield[textfield_i] = buttonlabels[b][0];
            textfield_i++;
            textfield[textfield_i] = 0; // zero terminate
          }
          break;

I't trying to add the button label to textfield
I tried printing the destination first, it was garbled, so I use the same line to print the source. Also garbled

Follow up:

Oddly enough, this code works as intended:

            strcat(textfield, buttonlabels[b]);
            Serial.println(buttonlabels[b]);

by removing the second pointer to the first character of the button label, all works as intended.

When I increase the label from a single character, it turns to garbage again, but this is part of a numeric keypad, so only one digit is required.

I wouldn't call the case closed, but solution found.

Thanks for teaching me the strcpy and strcat functions.

You're welcome. That last code you posted looks a little weird to be working. But great, if it does. In future, if you want to know exactly why or how it works or doesn't work, you really have to post your entire sketch so we can see all the definitions and data types etc. Normally someone will request that early in a thread, but here we didn't, and the result is always the same... lack of complete clarity in the answers.

Thanks;

I'm cleaning it up now and will go ahead and post the [SOLVED] version shortly. Along with the remaining mystery.

Cheers

It works;

  // now we can ask the buttons if their state has changed
  for (uint8_t b = 0; b < 21; b++) {
    if (buttons[b].justReleased()) {
      Serial.print("Released: "); Serial.println(buttonlabels[b]);
      buttons[b].drawButton();  // draw normal
    }

    if (buttons[b].justPressed()) {
      if (strlen(textfield) < TEXT_LEN) {
        buttons[b].drawButton(true);  // draw invert!
        //-----------------------------------------------------------------------------
        switch (b) {                    // switch case based on the BUTTON PUSHED
          case 0:                       // Pushed EC ---> Display "100:"

            strcat(textfield, "100:");
            break;                          // exits the switch case.

          case 1:                         // Pushed RTD ---> Display "102:"
            strcat(textfield, "102:");
            break;                          // exits the switch case.

          case 2:                         // Pushed pH ---> Display "99:"
            strcat(textfield, "99:");
            break;                          // exits the switch case.

          case 3:                         // Pushed cal ---> append "cal,"
            strcat(textfield, "cal,");
            break;                          // exits the switch case.

          case 4:                         // Pushed min ---> append "min,"
            strcat(textfield, "mid,");
            break;                          // exits the switch case.

          case 5:                         // Pushed low ---> append "low,"
            strcat(textfield, "low,");
            break;                          // exits the switch case.

          case 6:                         // Pushed hig ---> append "high,"
            strcat(textfield, "high,");
            break;                          // exits the switch case.

          case 7:                         // Pushed ESC ---> ABORT ----------------needs further work
            //
            strcat(textfield, "r");
            break;

          case 8:                         //BACKSPACE and erase
            if (strlen(textfield) > 0) {
              textfield[strlen(textfield) - 1] = ' '; //replace last entered character with SPACE
            }
            break;

         case 9 ... 19:
            strcat(textfield, buttonlabels[b]);
            break;

          case 20:         //Complete command, send to EZO
            Serial.println("Entering Data: ");
            /*--------------------- temporarily disabled
              status(textfield);
              channel = atoi(strtok(textfield, ":"));  // Let's parse the string at each colon, extracting the EZO address
              Serial.print(channel); Serial.print(":");
              cmd = strtok(NULL, ":");                    // Let's parse the string at each colon, extract the command  --------------> will it work for stuff other than r??
              Serial.println(cmd);
              I2C_call();                            // send to I2C
            */
            textfield[0] = 0;        // for next command after RTN
            // update the current text field
            tft.setCursor(TEXT_X + 2, TEXT_Y + 10);
            tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
            tft.setTextSize(TEXT_TSIZE);
            tft.print("                   ");
            break;
        }

        // update the current text field
        tft.setCursor(TEXT_X + 5, TEXT_Y + 10);
        tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
        tft.setTextSize(TEXT_TSIZE);
        tft.print(textfield);
        if (textfield[strlen(textfield) - 1] == ' ') { //if we did a BACKSPACE, we need to move the \0 forward by one
          textfield[strlen(textfield) - 1] = '\0';  //so the next character overwrites the SPACE in textfield
        }

        //------------------------------------------------------------------------------
        delay(100); // UI debouncing
      }
    }
  }

if you compare this to the original, we went from 9 lines of code per case, to 1!!

a couple of strange ones:

          case 9 ... 19:
            strcat(textfield, buttonlabels[b]);
            break;

I wanted to only use the first letter of the button**, by using**
** **          case 9 ... 19:             strcat(textfield, buttonlabels[b][0]);             break;** **
However, this construct neither concantenates, nor Serial.print! go figure. It doesn't bother me here, because I only use it to append digits 0-9 and the decimal point, but it would have been nice....
The other trick is with BACKSPACE (case 8:) on a TFT screen, you need to overwrite the printed character you want to erase. So, you have to insert a SPACE in the second to last position textfield[strlen(textfield)-1)] = ' '.
** **          case 8:                        //BACKSPACE and erase             if (strlen(textfield) > 0) {               textfield[strlen(textfield) - 1] = ' '; //replace last entered character with SPACE             }             break;** **
And then, AFTER printing, you need to find that SPACE and replace it with a '\0', so the next character overwrites the SPACE.
** ** if (textfield[strlen(textfield) - 1] == ' ') { //if we did a BACKSPACE, we need to move the \0 forward by one           textfield[strlen(textfield) - 1] = '\0';  //so the next character overwrites the SPACE in textfield         }** **
The best part: no more keeping track of indeces!
Thanks for all the tips and tricks
complete coda attached for reference
i2C_Touchscreen_Interactive_Case_rev1.zip (5.61 KB)