turning char[] received from bluetooth to int

The bluetooth devices (and other serial) return an array of char.

What is the best/safe/proper way to extract these to other types?

For example, if theData is known to have a long from elements 2-5, doI coerce it with something like

myLong= (unsigned long) theData[2]

or dereference as perhaps

myLong = * ( & theData + 2)

or even

myLong = * ( & theData[2])

Are there dragons lurking when I do this (other than needing to insure that the data is actually there?

[in my current situation, I have a counter with the size of the received buffers I know that something is there]

If your chars are 0-9 then you can transform a char to an int by subtracting ‘0’
For example, if

char c = '4';
int i = c-'0';

Then i will be equal to 4.

If you want to have the value coded in array 2 to 5

long value =0;
for (int i=2;i<6;i++) value = value *10+(theData[i]-'0');

This should do the job. But you don’t need a long for this, an int is ok

If the character array has the numeric data starting at element 2, the *long * could take up as many as 10 characters. You could do something like this:

long MyConverterToLong(char *buff)   //  assume this is the starting address of buff; buff[0]
{
   char temp[15];
   int i;

   for (i = 2; isdigit(buff[i]); i++) {   // Start with character 2 in the array...
      temp[i - 2] = buff[i];              // Copy only digit characters into temporary buffer
   }
   temp[i] = '\0';                          // Make it a string

   return atol(temp);                    // convert to long and return it
}

I haven't tested this but it should be close. I think this will handle +/- but not commas in the data.

I've got a feeling that I'm not all that clear here . . .

theData[] is a seventeen character buffer (sixteen data plus a null that I probably don't need), and there is another count variable to tell me how many bytes were actually received (there is a timeout when data doesn't come fast enough).

The first character is a prefix telling it what the command is (I have a switch based upon that).

For receiving a time setting, the transmitting computer would sent a "t", a reserved byte that is ignored, and then four bytes of long unsigned int (most often, the unix time in seconds). The counter would hold the value "6", the pointer to the next byte that would have loaded if the data stream continued).

So as it sits at locations 2-5, it is already a four byte integer in machine format, ready to load to a variable; it is not an ascii representation at all.

I could do something like 0x1000000 * char[2] + 0x10000*char[3] + 0x100* char[4] + char [5], but that's awfully "hacky" for a value that's already sitting there, which is why I'm thinking of pointers and such.

If the four-byte integer was sent by a computer that uses the same Endianness as your receiving Arduino, then just memcpy() the 4 bytes into a uint32_t.

Ahh.

Thanks; that's exactly what I need!

The other end will usually be ARM (a Pi with the One True Ring for my garden :) )

And while we’re at it. . . what about going the other direction with binary data?

write() takes a byte, string, or buffer.

Can I tell it that my unsigned long is a 4 byte buffer?

write(& myUnsignedLong,4);

?

Or would it be something like

for(byte i=0;i<=3;i++{
 altSoftSerial.write(*(myUnsignedLong + i));
}

The great thing about Open Source software is that you have the source code. Take a look in Print.cpp:

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}

Thanks. If I’m reading everything correctly (famous last words!), that’s doing the same thing I’m suggesting in my for(), and is what would be called from my first version?

I don’t have a board set up to test at the moment (and my family just got home, so I can’t escape to my lair to find it), but the following compiles and would seem to do what I’m trying to do (twice, with the non-working attempt commented out:

#include "SoftwareSerial.h"

void setup() {
  // put your setup code here, to run once:
  unsigned long myUnsignedLong = 123456789;
  byte myLength = 4;
  //Serial.write(&myUnsignedLong, myLength);

  for (byte i = 0; i <= 3; i++) {
    Serial.write( * ( &myUnsignedLong + i));
  }

  Serial.write( (char*) myUnsignedLong, myLength);
}

void loop() {
  // put your main code here, to run repeatedly:

}

I’m still trying to get my head fully wrapped around pointers, *, and &, but I think type coercion is finally sticking . . .

dochawk: I've got a feeling that I'm not all that clear here . . .

theData[] is a seventeen character buffer (sixteen data plus a null that I probably don't need), and there is another count variable to tell me how many bytes were actually received (there is a timeout when data doesn't come fast enough).

The first character is a prefix telling it what the command is (I have a switch based upon that).

For receiving a time setting, the transmitting computer would sent a "t", a reserved byte that is ignored, and then four bytes of long unsigned int (most often, the unix time in seconds). The counter would hold the value "6", the pointer to the next byte that would have loaded if the data stream continued).

So as it sits at locations 2-5, it is already a four byte integer in machine format, ready to load to a variable; it is not an ascii representation at all.

I could do something like 0x1000000 * char[2] + 0x10000*char[3] + 0x100* char[4] + char [5], but that's awfully "hacky" for a value that's already sitting there, which is why I'm thinking of pointers and such.

This is information that really should have been included in the first post. Nobody can tell you guide you on what to do if we don't even know what format it is in. By including this in the first place you can get to the answer quicker.

And my own final answer (without admitting how much time I lost to not thinking about endianness! ::) :astonished: )

(unsigned long) btInBfr[2]

lets me refer to the long I need, so I can then set time with it by

setUnixTime((unsigned long) btInBfr[2]);

which calls my function

//a function to set unix time and handle side effexts
void setUnixTime(unsigned long newTime) {
  //shut off any running sprinklers

  //note:  it would be better to just watch for time changes.  We just want to make sure that no sprinkler
  //    is left on forever

  allSprinklersOff();


  setTime(newTime);
}

Which will expand for better housekeeping later (I'm under the gun to have something working before it's too late to plant!