can i set a pointer to the struct so that i can take some serial input and put each input byte into the struct all in one shot with out having to refer to each of the struct's members individually.
I can't seem to get this to work.
below is what i am currently doing, but it just seems kinda clunky.
void CheckSerial(){
if (Serial.available() > 0){
switch (Serial.read()){
case 'W':
{
delay(1000);
int i=0;
while ((Serial.available()>0) && i<6) configuration.mac[i++] = Serial.read();
Serial.println(i);
i=0;
while ((Serial.available()>0) && i<4) configuration.ip[i++] = Serial.read();
Serial.println(i);
i=0;
while ((Serial.available()>0) && i<4) configuration.subnet[i++] = Serial.read();
Serial.println(i);
i=0;
while ((Serial.available()>0) && i<4) configuration.gateway[i++] = Serial.read();
Serial.println(i);
i=0;
while ((Serial.available()>0) && i<4) configuration.server[i++] = Serial.read();
Serial.println(i);
i=0;
while(Serial.available() > 0) configuration.path[i++] = Serial.read();
configuration.path[i++] = '\0';
Serial.println(i);
PrintConfig();
}
EEPROM_writeAnything(0, configuration);
//PrintConfig();
break;
case 'R':
EEPROM_readAnything(0, configuration);
PrintConfig();
break;
default:
Serial.flush();
}
}
}
A union should work. A union allows you to refer to a section
of memory in multiple ways. If you create a union between your
struct and an array of bytes you can access the memory as bytes
or your struct fields.
This was very handy for the real-time clock library I wrote. For
transmitting bytes it was easier to use an array. For setting values
using field names like sec, min. hour was easier.
There are probably simpler examples but you could look at my RTC library and example code at Loading...
How about this - assign a pointer to the struct's location in memory.
Send W, then the same amount of bytes to fill the struct.
void CheckSerial(){
uint8_t *pPtr = (uint8_t *)&configuration;
if (Serial.available() > 0){
switch (Serial.read()){
case 'W':
// what if the W was erroneous... Watch for a timeout here perhaps?
while(serial.available()<sizeof(config_t))
delay(100);
for(int i=0;i<sizeof(config_t);i++)
*pPtr++ = Serial.read();
PrintConfig();
EEPROM_writeAnything(0, configuration);
break;
...
Spinlock - your solution will be problematical with the structure described above; the structure is 150 bytes long, and the Arduino's rx buffer is only 128 bytes.
Thanks for all the help this morning
couple of questions for clarification purposes.
is uint8_t the same as saying byte?
why the cast to uint8_t when doing the address of assignment?
being a noobie to C i would have written
byte *ptr = &configuration;
would this have worked?
is the delay 100 to let the buffer fill up?
i understand using sizeof in the for loop so you don't write past the end of the struct. but why use it in the while loop?
the members in the struct are basically all byte size members. would size of work if some of them were ints or longs?
now that i can store and retrieve these things from the struct and the eeprom. i am trying to use them to call ethernet.begin and it is not working for some reason. it is very hard to debug as there are no functions to check with the ethernet class to see if things are getting assigned properly. any thoughts on this?
I started a new topic in Arduino Forum ? Software ? Syntax & Programs ? "wiznet configuration settings in a struct"
if you want to look at the code i'm using
thanks for all your help spinlock
Jeff
Thanks for that Groove, you're right. (need more coffee.)
You can do it then without waiting for the whole data buffer to be in place...
void CheckSerial(){
uint8_t *pPtr = (uint8_t *)&configuration;
if (Serial.available() > 0){
switch (Serial.read()){
case 'W':
// what if the W was erroneous... Watch for a timeout here perhaps?
for(int i=0;i<sizeof(config_t);i++)
{ while(Serial.available()<1)
delay(100);
*pPtr++ = Serial.read();
}
PrintConfig();
EEPROM_writeAnything(0, configuration);
break;
...
The configuration structure is stored in memory, for arguments sake let say it starts at address 100 and goes for 150 bytes, the last being memory address 249.
using:
uint8_t (or byte) we assign another variable (I used the variable pPtr) to the starting address:
byte *pPtrByte = (byte*)&configuration;
pPtrByte now points to memory address 100.
If we used an int:
int *pPtrInt = (int*)&configuration;
It would also point to memory location 100.
However, because of how pointer incrementing works, when we increment pPtrByte like this:
pPtrByte++;
it now points to 101.
but...
pPtrInt++;
now increments the size of an int, and points to 104 (if an int is 32bit).
The next increment of the pointers would be 102(byte) and 108(int,32bit). and on and on.
If a pointer's type is 2 bytes, and you increment it, it increments 2 bytes.
If it is 4 bytes, it increments 4.
That's why a byte pointer is good - you can walk memory and hit every location.
Of course, with your configuration structure and the memory - we are assuming that it is packed one member immediately after another in memory. Some processors need to align members on word boundaries. sizeof(config_t) may be more than 150bytes on those architectures, and you would have to account for that.
You would want to think about if you were transferring your structure over serial in a binary fashion as you seem to be from other systems.
I wouldn't use a union, nor would I cast the pointer structure to a byte structure. I'd do it this way:
#define MAC_SZ 6
#define IP_SZ 4
#define PATH_SZ 128
struct config_t
{
byte mac[MAC_SZ];
byte ip[IP_SZ];
byte subnet[IP_SZ];
byte gateway[IP_SZ];
byte server[IP_SZ];
char path[PATH_SZ];
};
struct config_t configuration;
void CheckSerial()
{
int c;
c = Serial.read(); // returns -1 if no chars available
switch(c)
{
case 'W':
read_configuration(&configuration);
// store in EEPROM
break;
case 'R':
// etc, etc
break;
default: // this is here to show that you are handling the
break; // default case and haven't forgotten about it
}
}
void read_configuration(struct config_t *config)
{
read_bytes(MAC_SZ,&(config->mac));
read_bytes(IP_SZ,&(config->ip));
read_bytes(IP_SZ,&(config->subnet));
read_bytes(IP_SZ,&(config->gateway));
read_bytes(IP_SZ,&(config->server));
// have to do something else for 'path'
}
void read_bytes(int num_bytes, byte *b)
{
int i = 0;
while (i < num_bytes)
{
while (Serial.available() == 0) // wait for byte
;
b[i++] = Serial.read();
}
}
When you're reading from Serial, you have to remember that it has a buffer that is being filled with bytes as they are being received. This receiving goes on in the background, through an interrupt routine. Your program may be checking for characters faster than the buffer is being filled, and a packet of characters may be sent in multiple pieces. You can't assume that because the first character of a packet is in the buffer (Serial.available() > 0) that all of the packet is there. That's why you have to wait for each character.
For the same reason, you don't want to do that Serial.flush() at the end of your case statement. Let's say there is only one character available, and it's a 'Z'. Between reading that character and your flush, the interrupt routine puts a 'W' in the buffer. Your flush will then throw it away, which means your sketch will miss the 'W' command. Worst of all, it may work sometimes, and it may fail randomly.
Finally, you'll need a different routine to read the 'path'. The path will have to always be the same size, or there will have to be a terminating character, like a carriage return at the end, so your program knows when to stop reading. As someone else pointed out, the buffer is only 128 characters, so the whole config with a long path might not fit into the buffer. You need to loop, checking for an available character, putting it into 'path', and stopping when you reach 128 bytes or the terminating character.
void read_path(int max_path,char *path)
{
int c;
int i = 0;
byte done = false;
while (!done)
{
if (i >= max_path)
done = true;
else
{
while (Serial.available() == 0) // wait for byte
;
c = Serial.read();
if (c == '\r')
done = true;
else
path[i++] = c;
}
path[i] = '\0';
}
}
Please note that path[] must be big enough to hold max_path + 1 bytes, because of the NUL terminator. Also, there's an error in my previous code. You don't need the '&' for the structure elements. Here's the corrected code:
Hi Mike,
Thanks , again very helpful to see how others work out these problems.
Correct me if i am wrong, but it looks like to me that your read_path function will put the terminating nul character in on every iteration of the while loop?
Should it maybe be moved down one curly brace?
Jeff