Inject integer into char*

Hi everyone,

So, I'm not sure if I'm really doing the correctly in the first place. I'm trying to take an int and replace part of a char array with it. I know I've done something similar before, but for the life of me can't remember how.

The code basically saves the state of 8 relays for power recovery. What I need to do is take what's stored (and retrieved) as a char array of 8 values (one for each relay) that are either 1 or 0 and be able to just replace one of them as the relay states are changed. The initial value that it is reading right now is "00000000"

Here's a bit of example code, that I'm sure I'm doing something wrong with. Can anyone point me in the right direction?

void SetSavedState(int Relay, int State) {
    char* LastState = "";
    char buffer[1];
    Relay--; // to handle the fact that the relays are numbered elsewere as 1-8 while the array will actually store as 0-7
    LastState = functiontogetstateofrelaysfromeeprom(); // returns a char* of "00000000" currently
    itoa(State, buffer, 10); // convert into to char
    LastState[Relay] = buffer[0]; // I've also tried just buffer
    //  code here to store the state again into eeprom

I know something is wrong, because when this function gets run it crashes and reboots. I'm just hoping someone else sees something I am too tired to see or knows the correct way to do what I want to do.

Thanks in advance!

Oh, and before anybody says it, the reason I'm not just storing integers in an array in the first place has to do with the library I'm using to store and retrieve configuration. It's not really an option right now.

A string (null terminated character array) has at least 2 elements, a character and a terminating NULL (\0).

You could store the states in one byte. And use the bitSet(), bitClear() functions to change the states. Then you only have one byte to mess with.

From what you say, you would probably like to use enums as states, I suspect that the actual problem in your code is this line:

LastState =  functiontogetstateofrelaysfromeeprom()

because when returning raw-pointer to a stack allocated variable you may run into memory leaks.

Edit: if you have 8 relays whose state is 1 or 0 so you can do the job using only one byte of memory, (not 8 as you planned in the char array) so again, either use enum, or just use a byte. Personally in this problem I would prefer to use a byte

1 Like

Okay, I can do that:

  1. Read a byte from EEPROM (I use a random value).
  2. Change a bit (I use a random bit which is randomly set to 0 or 1).
  3. Convert it to text.
  4. Never ever return a pointer to an array on the stack.
#include <EEPROM.h>

void setup() 
{
  Serial.begin(115200);
}

void loop() 
{
  SetSavedState( random(0,8), random(0,2));
  delay(3000);
}

// Return a byte with a random pattern.
byte functiontogetstateofrelaysfromeeprom()
{
  byte result = 0;
  for(int i=0; i<8; i++)
    bitWrite(result, i, random(0,2));
  return( result);
}

// Request the data from EEPROM and change it and send it
// Relay: 0...7
// State: 0 or 1
void SetSavedState(int Relay, int State) 
{
  // Get it
  byte myRelaysOld = functiontogetstateofrelaysfromeeprom();
  
  // Change it
  byte myRelaysNew = myRelaysOld;
  bitWrite( myRelaysNew, Relay, State);

  // Conver it to text
  char text[] = "00000000";   // must be 8 characters of 0
  for( int i=0; i<8; i++)
  {
    if( bitRead( myRelaysNew, 7-i) == 1)  // start with higest bit
      text[i] = '1';
  }

  // Print everything
  Serial.print("From EEPROM: ");
  for( int i=7; i>=0; i--)              // print zeros in front
  {
    if( bitRead( myRelaysOld, i) == 0)
      Serial.print('0');
    else
      break;
  }
  Serial.print( myRelaysOld, BIN);
  Serial.print(", Relay = ");
  Serial.print(Relay);
  Serial.print(", State = ");
  Serial.print(State);
  Serial.print(", result as text = ");
  Serial.println(text);
}

Try it in Wokwi:

If you are not familiar with using pointers you should not use them at all.
the asteriks the "star" * is changing from variable to pointer to variable

just define all variables global.
defining global means define them outside all functions on top of your source-code

an array of char needs a byte for every character you want to store
and one additional byte for the terminating zero of the array of char
so for storing your characters

12345678
00000000

instead of coding

you have to code

char buffer[9]; // 9 elements 8 chars and 1 for terminating_zero

and you have to know that the indexnumbers start at zero

char buffer[9] = {"ABCDEFGH"}; 

accessing the "A" is with index 0

buffer[0]

is the "A"

best regards Stefan

consider

output for 0, 4, 7, 8, 4

00000001
00010001
10010001
10000001

const int Nrelay = 8;
byte relayState;
char relayStr [9] = "00000000";

char buf [80];

// -----------------------------------------------------------------------------
void
loop (void)
{
    if (Serial.available ())  {
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';

        n = atoi (buf);
        if (Nrelay > n)  {
            byte bitMask = 1 << n;
            relayState  ^= bitMask;

            relayStr [Nrelay-1-n] = relayState & bitMask ? '1' : '0';
            Serial.println (relayStr);
        }
    }
}

void
setup (void)
{
    Serial.begin (9600);
}

Ahhh. ... but using them is how you get familiar with them. Like interrupts, it's a Catch 22 ... you shouldn't use them unless you're familiar, but you won't get familiar until you try using them and get burned a few times.

there's using ptrs and then there's ptr arithmetic

I'm missing the point? Understanding pointer arithmetic is part of using pointers.

how often do you see something like

MyStruct *p = mystructs;

     *(p+13)->val++;

and in this case

char myStr [20];
char *p = myStr;

      *(p+3) = '0';

instead of

    myStr [3] = '0';

I'm still missing your point. Is there one?

of course there is.

Best advice I have read in this forum in a very long time :grinning:

It still eludes me. But, OK.

this statement made me think

there are of course lots of languages that don't have pointers. And even those that have pointer (Pascal) maintain them thru the language, don't allow there values to be specified.

while array indices can replace the use of pointers in some applications (e.g. linked lists), C's definition of pointers make programming on hardware much more convenient. avoiding assembler routines makes the code more portable and is the reason why the Arduino IDE supports C++. but when has an OP on this forum described a program needing such features

while pointers can make the code a bit easier to read, i wonder where/if there a line where they aren't necessary (function ptrs, callback functions)?

or is the issue really the mis-application of ptrs, using them when not appropriate?

In my own code, I think "nothing" of using pointers, just a natural as I learned C (early) immediately after Fortran at uni.

But, I go out of my way to avoid pointer examples in this Forum. Pointers are just too easy to "break" when used by the novice; heck, I have even gotten myself into pointer-hades after a couple of brews and untwisting pointer logic is no fun the next day when one has a hangover. :face_vomiting:

IMO: teaching/learning to utilize pointers correctly is not well suited to brief forum exchanges.

The simplest and safest approach is to rebuild the character array. Something like:

char buf[40]={0};
strncpy(buf,"start string",sizeof buf);
itoa(number, &buf[strlen(buf)+1], 10); //concatenate ASCII representation of integer
strncat(buf,"end string", sizeof buf);

It sometimes comes up in the forum to use a union to cheat casting. The selected-best in this post is why you should not:
c++ - Using char array inside union - Stack Overflow

Yes, you can cast any typed variable you have to a char array and mess with it for whatever purpose you have in mind. But type-punning through unions is illegal in C++, there are no buts and excuses. Yes, it may work. Yes, if you're not bothered by it, you may continue to use it. But per C++ standard, it is clearly illegal.

Hey everyone, sorry it's been a few days. It's been very busy and tiring with work.

That is a possibility, but the library that I'm using (if I wasn't using it I would just write my own eeprom routines) stores things as a char array that it's web interface can read for users to modify settings. And, ideally if the settings is going to be displayed, I was hoping it could be easily user readable and modifiable as the string of 8 characters.

This line is not an exact representation, but more of an arbitrary piece of code I just put for an example. As I said above, I'm using functions of a library I'm already using for other features and the function is actually defined as...

char* IOTAppStory::getField(const char *fieldID)
and
void IOTAppStory::getField(char *&variable, const char *fieldID)

both of which work fine as they are coded, so I didn't bother to post the code within them. The second one is a void function that gets the data from eeprom and stores it in a variable. The first one calls that function, but stores it in a local variable to return to the calling code, which can sometimes be easier for coding.

To be fair on this one, buffer[1] is exactly that, a buffer for the single character as I was going crazy trying to figure out how to do this and I was thinking maybe I could have an array of 1 character, put the value into that and then copy, lets say buffer[0] to LastState[3] as I could not figure out how to directly put an integer into the char array. And, I am fully aware of arrays beginning at zero, which is why I had the line...

Relay--;

to decrement the relay number to match the array location.

gcjr, I am not awake enough yet to read that much code. lol. But I will definitely be looking at that later as it looks promising.

I will look into this as well.

As far as the pointers, I'm not going to say I'm an expert, but I do somewhat understand them. It is sometimes useful to pass the pointer to a function and then have the function access that pointer to update the info for the previous calling function as I have run into the calling function (whether loop or something else) getting random gibberish back from a return statement even though the variable being used in the return line can be printed to serial before the return statement without any problems. And, some things you just don't want a global variable for because they are only used in certain cases.

For example, part of what I am doing is a relay board that will be able to be controlled by remote...by multiple remotes (think handheld, dash mounted and maybe a tablet) that will all be able to be paired at once and configured. Now, if you're using something dash mounted in your vehicle, then you step out and use a hand remote, you don't want the global variables being used and your functions getting confused in the case of maybe someone inside he vehicle sending commands to control one thing while someone outside of the vehicle is using a remote at the same time to control another thing.

I appreciate all the advice and will be looking at some of it later today to see if it will work for my code. Any other suggestions are welcome, and I hope I don't offend anyone with any of my replies. Mostly I'm just trying to work with the library I'm using for other functions, which include OTA updates and a web configuration interface. Perhaps I could use other options later, but this library suits my needs for now and already has the eeprom functions and I don't want to accidentally store something in the wrong spot and mess up the library's configuration.

Thank you!

From the initial description
having a sequence of 8 characters beeing either "0" or "1"
examples

You haven't posted the complete sketch. Just a small snippet. What you have written above sounds like you try to change values stored in an EEPROM in a way that is different from the standard-way of the library-functions you are using is doing it.

But I can't really tell if this is true because you did not post the rest of your code.
additional you have not told what microcontroller you are using. OTA sounds like ESP8266 / ESP32 but I'm not sure

You should post at least this part of your code that shows
What is the standard-way of your library to store any kind of value in the EEPROM

your initial code

LastState = functiontogetstateofrelaysfromeeprom(); // returns a char* of "00000000" currently

if the comment is right you store a pointer into variable named "LastState"
You did not show what type of variable this is

next line uses a different variable named "State"
Where was which value assigned to variable "State"?

itoa(State, buffer, 10); // convert into a char

Then next line

LastState[Relay] = buffer[0];

shows that variable LastState[Relay] is an array
again you did not post what variable-type are the elements of your array LastState[]
You are assigning a single char from char-array "buffer" to an element of array LastState

It would help a lot if you would post your complete sketch. It doesn't matter if this sketch has 2000 lines of code

You initially wrote

Somewhere in your code there must be a part that does the storing
"the state of 8 relays as a char-array"
.
and somewhere there must be a part that is
"retrievieving the states of 8 relays as a char array of 8 values"

or is this exactly that part of the code you want to add????

If so somewhere in your code the relays are switched on/off. How does this code look like??
Again in summary post your complete sketch to be able to look-up what ever details is required to get an overview about how your code does what.

best regards Stefan