RF program - what is uint8_t * ?

Hello!
I have recently used a transmitter and a receiver to communicate between two Arduino boards.
I took the program from a clear and good tutorial in this link: http://ilblogdidami.blogspot.co.il/2013/09/use-arduino-with-rf-modules.html

The program for the transmitter:

#include <VirtualWire.h>

char *message = "Hello";

void setup() {
  vw_set_tx_pin(2);
  vw_setup(2000);
}

void loop() {
  send(message);
  delay(1000);
}

void send (char *message) {
  vw_send((uint8_t *)message, strlen(message));
  vw_wait_tx();
}

The program for the receiver:

#include <VirtualWire.h>
byte message[VW_MAX_MESSAGE_LEN];
byte messageLength = VW_MAX_MESSAGE_LEN;

void setup() {
  Serial.begin(9600);
  Serial.println("Device is ready"); 
  vw_setup(2000);
  vw_rx_start();
}

void loop() {
  if (vw_get_message(message, &messageLength)) {
    Serial.print("Received: ");
    for (int i = 0; i < messageLength; i++) {
      Serial.write(message[i]);
    } 
    Serial.println();
  } 
}

As you can see those are very nice and short programs (they work perfectly) and I understood everything except of two things that I suggest are not connected only to this library but to Arduino language at total:

  • Why when I change the: char *message = "Hello"; to char message = "Hello"; the compiler gives me this error: invalid conversion from 'const char*' to 'char' ? What is the purpose of the '*' sign?

  • What is this? uint8_t *

Why is the '*' sign is inside the brackets and the 'message' variable is not?Here:
(uint8_t *)message

I will be happy
to hear any answers
Thank you!

Why when I change the ...

char * is a pointer to a char. The compiler allocates space for the string, and creates a pointer to the first character of the string.

When you remove the *, you are changing the variable type from pointer to char to char. A char can hold ONE character. Obviously, "hello" is NOT one character.

What is this?

A pointer to a uint8_t, which is an 8 bit type (the 8), unsigned (the u), integer (the int) standard type (the _t).

Why is the '*' sign is inside the brackets and the 'message' variable is not?

Those are not brackets. They are parentheses. The type name in parentheses is a cast. It tells the compiler to treat the variable as though it was the type in the parentheses.

vgoshat:
Why when I change the: char *message = "Hello"; to char message = "Hello"; the compiler gives me this error: invalid conversion from 'const char*' to 'char' ? What is the purpose of the '*' sign?

OK...

char message; assigns the name "message" to a one byte area in memory. That one byte area is pointed to by either a 16, 32 or 64 bit pointer (pointer size depends on the processor and compiler).

But, only one byte of memory is allocated, so, ALL you can do is something like this:
char message = 'A';

That is, you can only store one byte in it. If you try this:
char message = "Hello there";

The compiler gives you an error because it cannot put a whole string in one byte.

Now look at this (note the * character)
char *message = "Hello there";

What this does is tell the compiler to assign storage space for 12 bytes ("Hello there" plus a null terminator). The * character means that now the variable name "message" points to the memory address of the first character (i.e. the letter "H").

The compiler counted how many characters were in the string, reserved memory for them and finally returned a pointer to it for you to use.

Here's an example of what the memory map would look like:

char *message = "Hello there";

ADDR    DATA
0x0120  H
0x0121  e
0x0122  l
0x0123  l
0x0124  o
0x0125  <space>
0x0126  t
0x0127  h
0x0128  e
0x0129  r
0x012A  e
0x012B  0x00H

....
....
....

message

0x0150  0x20
0x0151  0x01

You see? The STRING "message" is stored starting at address 0x0120. The variable that points to it is stored at address 0x0150 and 0x0151 (a 16 bit location, and "little endian" backwards) and it contains the value "0x0120" (the start address of the string).

Now check this code out. I did it in Linux, but you can also do it in Arduino. Just change the "fprintf" calls to "Serial.print()".

If you do this, note the differences in:

  • The size of the pointers
  • The addresses things are stored at
#include <stdio.h>
#include <string.h>

int main (void)
{
	char *message = "Hello there";
	unsigned long str_len = (strlen (message) + 1); // grab the null too
	unsigned long ptr_siz = sizeof (message);
	unsigned long ptr_to_ptr_siz = sizeof (&message);
	unsigned long char_siz = sizeof (char);
	unsigned long char_ptr_siz = sizeof (char *);
	int x;

	fprintf (stdout, "The length of the string (including the null) is %lu\n", str_len);
	fprintf (stdout, "The string is %s\n", message);
	fprintf (stdout, "The string starts at 0x%08X\n", message);
	fprintf (stdout, "The address of the pointer to 'message' is 0x%08X\n", &message);
	fprintf (stdout, "The pointer to 'message' uses %lu byte%s\n", ptr_siz, (ptr_siz == 1) ? "" : "s");
	fprintf (stdout, "The pointer to the address of 'message' uses %lu byte%s\n", ptr_to_ptr_siz, (ptr_to_ptr_siz == 1) ? "" : "s");
	fprintf (stdout, "The size of a char in memory is %lu byte%s\n", char_siz, (char_siz == 1) ? "" : "s");
	fprintf (stdout, "The size of a char POINTER in memory is %lu byte%s\n", char_ptr_siz, (char_ptr_siz == 1) ? "" : "s");

	fprintf (stdout,
			 "\n"
			 "Address\t\tData\tChar\n"
			 "=======\t\t====\t====\n"
			);

	for (x = 0; x < str_len; x++) {
		fprintf (stdout, "0x%08X\t0x%02X\t %c\n", (message + x), message[x], message[x]);
	}

	fprintf (stdout, "\nBye!\n");

	return 0;
}

And running it produces:

The length of the string (including the null) is 12
The string is Hello there
The string starts at 0x004009B8
The address of the pointer to 'message' is 0xE2395968
The pointer to 'message' uses 8 bytes
The pointer to the address of 'message' uses 8 bytes
The size of a char in memory is 1 byte
The size of a char POINTER in memory is 8 bytes

Address Data Char
======= ==== ====
0x004009B8 0x48 H
0x004009B9 0x65 e
0x004009BA 0x6C l
0x004009BB 0x6C l
0x004009BC 0x6F o
0x004009BD 0x20
0x004009BE 0x74 t
0x004009BF 0x68 h
0x004009C0 0x65 e
0x004009C1 0x72 r
0x004009C2 0x65 e
0x004009C3 0x00

Bye!

Notice that my test shows 8 byte (64 bit) pointers. I'm using 64 bit linux. You will probably see 2 byte (16 bit) pointers if you run this on an Arduino.

Hope this helps explain pointers........ :slight_smile:

-- Roger

There is some confusion.

char message = "Hello, there"; [ fails because in this context "Hello,there" decays to a pointer to a string in memory the 'const char *', and it is illegal, and nonsensical to store that in a space reserved for a single char. It has nothing to do with the length of the string. char message = "H"; will fail just as badly.

KeithRB:
There is some confusion.

char message = "Hello, there"; [ fails because in this context "Hello,there" decays to a pointer to a string in memory the 'const char *', and it is illegal, and nonsensical to store that in a space reserved for a single char. It has nothing to do with the length of the string. char message = "H"; will fail just as badly.

...and the point is?

While most of the descriptions are correct, statements like this:

When you remove the *, you are changing the variable type from pointer to char to char. A char can hold ONE character. Obviously, "hello" is NOT one character.

imply that the reason it does not work is that "Hello" is more than one character, not that a pointer is not the same kind of object as a scalar.

KeithRB:
While most of the descriptions are correct, statements like this:

When you remove the *, you are changing the variable type from pointer to char to char. A char can hold ONE character. Obviously, "hello" is NOT one character.

imply that the reason it does not work is that "Hello" is more than one character, not that a pointer is not the same kind of object as a scalar.

OK, obviously the OP isn't a C programming expert (yet) since he didn't know about pointers and such. Therefore, I tried to make my explanation as easy to understand as possible without throwing in things that would be confusing.

Ever take a class where a SIMPLE calculation that can be done on a 4 function calculator is instead explained in the textbook using a level of math far above the understanding of most people and you are expected to memorize formulas?

Well, the formulas evaporate from the brain 10 minutes after you take the final exam, never to be remembered again.

However, if a GOOD teacher instead EXPLAINS how it works, THEN shows you the math, you say "Ah-ha! Now I get it!" and you don't need to memorize anything because with your UNDERSTANDING of the concept, you can DERIVE the formula any time you wish.

That's the difference between a good teacher and most other teachers / professors.

I try to teach through UNDERSTANDING. The nitty gritty bs about "scalar objects" will come before the OP in due time. For now he needs the basics.

(edit to add): I was wondering what you were talking about. I never said

When you remove the *, you are changing the variable type from pointer to char to char. A char can hold ONE character. Obviously, "hello" is NOT one character.

but now I see you were replying to someone else. Anyway, the rest of this post applies, so.....

Thank you very much!!!!!
That was great and detail explanation and I understood the problem thanks to it!!!!!

You are really good teacher! XD

vgoshat:
Thank you very much!!!!!
That was great and detail explanation and I understood the problem thanks to it!!!!!

You are really good teacher! XD

Glad it helped you out!

If you like, here's an Arduino version of the same test code. Be sure to edit the "Serial.begin()" line to your desired baud rate (I use 115200, others use 9600, set it to what you use).

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

	char *message = "Hello there";
	char buffer [256]; // printing buffer
	unsigned long str_len = (strlen (message) + 1); // grab the null too
	unsigned long ptr_siz = sizeof (message);
	unsigned long ptr_to_ptr_siz = sizeof (&message);
	unsigned long char_siz = sizeof (char);
	unsigned long char_ptr_siz = sizeof (char *);
	int x;

	sprintf (buffer, "The length of the string (including the null) is %lu\r\n", str_len);
	Serial.print (buffer);

	sprintf (buffer, "The string is %s\r\n", message);
	Serial.print (buffer);

	sprintf (buffer, "The string starts at 0x%08X\r\n", message);
	Serial.print (buffer);

	sprintf (buffer, "The address of the pointer to 'message' is 0x%08X\r\n", &message);
	Serial.print (buffer);

	sprintf (buffer, "The pointer to 'message' uses %lu byte%s\r\n", ptr_siz, (ptr_siz == 1) ? "" : "s");
	Serial.print (buffer);

	sprintf (buffer, "The pointer to the address of 'message' uses %lu byte%s\r\n", ptr_to_ptr_siz, (ptr_to_ptr_siz == 1) ? "" : "s");
	Serial.print (buffer);

	sprintf (buffer, "The size of a char in memory is %lu byte%s\r\n", char_siz, (char_siz == 1) ? "" : "s");
	Serial.print (buffer);

	sprintf (buffer, "The size of a char POINTER in memory is %lu byte%s\r\n", char_ptr_siz, (char_ptr_siz == 1) ? "" : "s");
	Serial.print (buffer);

	sprintf (buffer,
			 "\r\n"
			 "Address\t\tData\tChar\r\n"
			 "=======\t\t====\t====\r\n"
			);
	Serial.print (buffer);

	for (x = 0; x < str_len; x++) {
		sprintf (buffer, "0x%08X\t0x%02X\t %c\r\n", (message + x), message[x], message[x]);
		Serial.print (buffer);
	}

	sprintf (buffer, "\r\nBye!\r\n");
	Serial.print (buffer);

	// end of setup()
}

void loop (void)
{
	// we don't use loop() here
}

On my Arduino UNO R3, this code outputs the following. Take note of the differences in the output of the 64 bit Linux version and the 16 bit Arduino version... particularly the pointer sizes:

The length of the string (including the null) is 12
The string is Hello there
The string starts at 0x00000100
The address of the pointer to 'message' is 0x000008FA
The pointer to 'message' uses 2 bytes
The pointer to the address of 'message' uses 2 bytes
The size of a char in memory is 1 byte
The size of a char POINTER in memory is 2 bytes

Address Data Char
======= ==== ====
0x00000100 0x48 H
0x00000101 0x65 e
0x00000102 0x6C l
0x00000103 0x6C l
0x00000104 0x6F o
0x00000105 0x20
0x00000106 0x74 t
0x00000107 0x68 h
0x00000108 0x65 e
0x00000109 0x72 r
0x0000010A 0x65 e
0x0000010B 0x00

Bye!

Have fun!

Thank you again!!!

There is a missing line in the program for the receiver:

vw_set_rx_pin(7);

TomCh