Converting analog value to 4-character ASCII string[SOLVED]

Hello, Forum! This is my second topic.
My first one was described as “epic” → http://forum.arduino.cc/index.php?topic=382303.0

I’m trying to get an analogRead() value converted into a 4-character ASCII “string” variable, so that I can display it on my 4-character ASCII display. Eventually, this will function as a 4-digit digital meter, measuring an analog voltage.

With thanks to the Forum members who responded to my first thread, I already have the ASCII display code working 100%. :slight_smile:

Now, the compiler seems to be “choking” on it’s inability to determine the length of the string to be created by the analog value. I’m completely stumped, having tried numerous things with the String functions, as shown in the Reference. None have worked, yet, so I raise the white flag, here. :wink:

Here is my sketch. The problem is in the function “GETVAL” to get the analog value:

// First attempt to display a numeric value to the 4 character display, (not working)

char Msg[] = "1023"; 
// I need Msg[] to contain the ASCII equivalent
// of the variable, val, obtained below.
// (Start with "1023" as a dummy 4 char value.)

char Valstring='0';

// If I comment-out the "GETVAL" function, below, 
// (and the call to it in the "void loop()"),
// I see "1023" on the display, as expected. How to Get the analog value
// into the string is what is eluding me.

int GETVAL(int val) 
{
  val = analogRead(A0);
  String Valstring = String(val); 
  char Msg[] = Valstring;  
   // ^^^ This gives an error: exit status 1
   // initializer fails to determine size of 'Msg'
}

// **** All of this code, below, works perfectly with the dummy string. ***
// datapins; least significant bit first (Single unit, 4 Char display)
const byte datapins[] = {2, 3, 4, 5, 6, 7, 8, 9};

// Two bits for display address on the single display unit
const byte addresspins[] = {10, 11};

void WRITEA(char A) //Address of digit (0-3)
{
  for (int bitcnt = 0; bitcnt < 2; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is 1 or 0
    bool bitvalue = (A & (1 << bitcnt)) == (1 << bitcnt);
    bitvalue = !bitvalue; //Invert bits to get forward scroll with positive numbers
    // set corresponding output
    digitalWrite(addresspins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the parallel data to the display
}

// Convert ASCII character into parallel bits on pins for Display
void WRITED(char D)
{
  for (int bitcnt = 0; bitcnt < 8; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is set or not
    bool bitvalue = (D & (1 << bitcnt)) == (1 << bitcnt);
    // set corresponding output
    digitalWrite(datapins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the parallel data to a character in the display
}
//Pulse a designated pin; 13=Display Write
void Pulse(byte P)
{
  digitalWrite(P, 0);
  delay(1);
  digitalWrite(P, 1);
}
// the setup function runs once when you press reset or power the board
void setup()
{
  delay(100); //Allow display to init internally with tri-state pins
  // initialize digital pins as outputs.
  for (int x = 2; x < 14; x++)
  {
    pinMode(x, OUTPUT);
    digitalWrite(x, 0); //Set ALL pins 0 to start
  }
  delay(2); // Pin 13 Low wrote 0x00 into the display unit.
  digitalWrite(13, 1); //Set Write pin high. (Pluses low to write)
  WRITED(0x8E); //Clear the display
  WRITED(0x0E); //Turn off Clear Display bit and set parameters (D=25% bright, E=50%, F=100%)
  digitalWrite(12, 1); // Set AD2 high for DATA mode
  // End of SETUP routine (Initialize the displays for data)
}
int LEN = strlen(Msg); // Get the length of the message for main "for" loop
char CHR = ' '; // Initialize the variable with a "space" character

void loop() {
GETVAL; // <-- How do I get this into the "string" to feed to the displays?

  // Display the 4 chars in the string Msg[]

  for (byte nybble = 0; nybble < 4; nybble++) // Loop thru 4 display characters
  {
    CHR = (Msg[nybble]); //Get the character from the string
    WRITEA(nybble); //Write the digit address for it

    WRITED(CHR); //Place ASCII character onto DATA pins & send to display
  } //End of display loop

  delay(500);  // Fetch the analog value and display it twice per second.
   // End of main "for" loop. Repeats until 4 chars have been displayed
} // Repeat forever

Thank you! :slight_smile: (This noob is learning.)

Willie…

Now, the compiler seems to be "choking" on it's inability to determine the length of the string to be created by the analog value. I'm completely stumped, having tried numerous things with the String functions, as shown in the Reference. None have worked, yet, so I raise the white flag, here.

Use sprintf() to write to a char array. Use the %d format specifier, with optional (look them up; it's good exercise) values to get 4 character output every time.

char Valstring='0';

Valstring is NOT a string.

By the way, you will need to declare the array to write to to be big enough to hold at least 5 characters.

Isn't sprintf() the command to send serial data to an LCD? (Or to the serial monitor?)

So it should be, what, Msg="12345" ?

I'm trying to search the Forum for sprintf(). Some of what I'm finding leaves me with more questions than answers. :frowning: I'm very much a noob when it comes to C++.

Even a simple line or two of code, to help me actually USE this command, would be nice.

Like this...
sprintf(Msg,val)
...but that doesn't work, either, so what is the proper way?

Willie...

This is what I need to do:

  1. Read an analog input and save it to an integer variable.
  2. Convert that integer variable into a 4 character string, like "1002"
  3. Send that 4 character string to my display code, the way Msg="1002"; works now.

Does that explain it better? :slight_smile:

Willie...

Willie---:
Isn't sprintf() the command to send serial data to an LCD? (Or to the serial monitor?)

No, print and println are those functions

More than you ever wanted to know :smiley: man sprintf

int val = 1234;
char buffer[5];
sprintf(buffer, "%d", val);

Be aware of the pitfalls; if val ever goes over 4 digits, the code will crash. So you have to build in a check for that before calling sprint for use snprintf (same manpage) in which case the result will be truncated.

The second argument in sprintf is the format specifier. For e.g. a time in hh:MM:ss you can use

sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds");

And for a hour without the leading zero but with a leading space

sprintf(buffer, "%2d:%02d:%02d", hours, minutes, seconds");

Same pitfalls as earlier mentioned apply; make sure your buffer is big enough.

PS

From hearsay, Arduino does not support floating point conversion.
2)
Other function to look at is itoa (integer-to-ascii).
3)
I think it's time to find a C (not C++) tutorial on the web to start covering the basics.

LOL! Yes, sterretje, more than I wanted/needed to know. :wink: (My head is now spinning at 20,000 RPM)

In scrolling/searching that manual, I see references to the snprintf() function, but I don't see an EXAMPLE for it. (I learn best by example.) You gave an example, above, of sprintf(), so how then would I modify it to incorporate the 4 character hard limit? It SHOULD work, despite the risk of crashing... so to fix that, I'll just need to "tweak" it a little. :slight_smile:

Since the ASCII display unit I'm going to use for this is only 4 digits, that is all I will ever need. Integer is fine, as I can just put a small LED under the physical display to show where the decimal point would be. (Yeah, that's kludgey, but to show one on this display would use a whole character, so then I'd only have 3 actual digits, IE: 10.2 instead of 10.23)

Because I'm tinkering with rather unique hardware, the sketches and libraries written for the common hardware (like the LCD) are of minimal use to me. I have a pile of these amazing little 4-character dot-matrix LED displays, so I want to make use of them, and Arduino has already proven to be an excellent platform upon which to build. :slight_smile: I have the sketch working perfectly to write to these displays, now I just need to get the integer-to-ASCII part working with it. :slight_smile:

You've given me some valuable advice... I'll give it a try, and look forward to learning more! Thanks!

Willie...

if(yourval > 9999 || yourval < -999)
{
  // safe to place in 5 character buffer
  ...
  ...
}
else
{
  // not safe; e.g. display 4 flashes dashes
  ...
  ...
}

You just need to search a little for the functions that you’re advised to use. Personally I find the so-called man pages more of a reference but this one has some examples. Maybe this would have been a better link.

OK, that IS a better link! :wink: I followed it to here: snprintf - C++ Reference

They do give an example, but they also include a library… I assume that this only works that way?

I tried your first example, and at least that stopped the compiler from crashing! :wink: Unfortunately, it is only writing 0x00 to each character of the displays. I commented-out the analogRead and instead, assigned a value to the variable, val. It still sends 0x00 instead of the ASCII characters for the number.

Here is the code. (You can ignore all of the display-driver related code, as it’s working 100%)

// Second attempt to display a numeric value to the 4 character display, (still not working)


// Despite calling the "GETVAL" function, below, in the void loop()
// I don't see a value on the display. How to Get the analog value
// into the string to be displayed, is what's still eluding me.

char Msg[5]; // Create the buffer for the ASCII string

int GETVAL(int val) //Function to get and convert tha ADC value to ASCII
{
// val = analogRead(A0);  //**Commented out for testing
val = 1234; //Try forcing a value, but it still sends 0x00 to each character
sprintf(Msg, "%d", val);
}

//*****************************************
// **** All of this code, below, works perfectly. *****

// datapins; lsb first (Single unit, 4 Char display)
const byte datapins[] = {2, 3, 4, 5, 6, 7, 8, 9};

// Two bits for display address on the single display unit
const byte addresspins[] = {10, 11};

void WRITEA(char A) //Address of digit (0-3) //Write character Address
{
  for (int bitcnt = 0; bitcnt < 2; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is 1 or 0
    bool bitvalue = (A & (1 << bitcnt)) == (1 << bitcnt);
    bitvalue = !bitvalue; //Invert bits to get forward scroll with positive numbers
    // set corresponding output
    digitalWrite(addresspins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the Address data to the display
}

// Convert ASCII character into parallel bits on pins for Display
void WRITED(char D)
{
  for (int bitcnt = 0; bitcnt < 8; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is set or not
    bool bitvalue = (D & (1 << bitcnt)) == (1 << bitcnt);
    // set corresponding output
    digitalWrite(datapins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the parallel data to a character in the display
}
//Pulse a designated pin; 13=Display Write
void Pulse(byte P)
{
  digitalWrite(P, 0);
  delay(1);
  digitalWrite(P, 1);
}
// the setup function runs once when you press reset or power the board
void setup()
{
  delay(100); //Allow display to init internally with tri-state pins
  // initialize digital pins as outputs.
  for (int x = 2; x < 14; x++)
  {
    pinMode(x, OUTPUT);
    digitalWrite(x, 0); //Set ALL pins 0 to start
  }
  delay(2); // Pin 13 Low wrote 0x00 into the display unit.
  digitalWrite(13, 1); //Set Write pin high. (Pluses low to write)
  WRITED(0x8E); //Clear the display
  WRITED(0x0E); //Turn off Clear Display bit and set parameters (D=25% bright, E=50%, F=100%)
  digitalWrite(12, 1); // Set AD2 high for DATA mode
  // End of SETUP routine (Initialize the displays for data)
}
int LEN = strlen(Msg); // Get the length of the message for main "for" loop
char CHR = ' '; // Initialize the variable with a "space" character

void loop() {
GETVAL; // ***** How do I get this into the "string" to feed to the displays?

  // Write each character in the string Msg[] to the display
  for (byte nybble = 0; nybble < 4; nybble++) // Loop thru 4 display characters
  {
    CHR = (Msg[nybble]); //Get the character from the string
    WRITEA(nybble); //Write the address to display digit

    WRITED(CHR); //Write the ASCII character to the addressed display digit
  } //End display loop for 4 characters

  delay(500);  // Fetch the analog value and display it twice per second.
} // Repeat forever

BTW, my head is only spinning at about 2,000 RPM right now. :wink:

Willie…

@sterretje, you need to take a look at that if statement, and the comments. If the value is greater than 9999, or less than -999, it is NOT safe to convert the value using a 5 element array.

OP: You should make sure that your array can hold all possible values that you will write to it. If the value is from an analog pin, it will be in the range 0 to 1023, which is safe to write to a 5 element array.

If the value is a generic int, you should make sure that the array can hold 7 characters or more (-32768 and a NULL is 7 characters).

They do give an example, but they also include a library... I assume that this only works that way?

If I'm not mistaken, the Arduino includes the library for you. If you don't use the '#include' and it complains that it does not know the 'sprintf', it is not included by default and you have to include it.

Unfortunately, it is only writing 0x00 to each character of the displays.

void loop() {
GETVAL; // ***** How do I get this into the "string" to feed to the displays?

You're not calling a function; try with round braces.

void loop() {
  GETVAL();

PaulS:
@sterretje, you need to take a look at that if statement, and the comments. If the value is greater than 9999, or less than -999, it is NOT safe to convert the value using a 5 element array.

OOPS, that's number three this year :wink: I had to re-type it to realize what was wrong.
@willie, the comments are swapped.

Sterretje,
It does not accept snprintf() so that's not in there. sprintf() is, tho.

The compiler balks at GETVAL();

exit status 1
too few arguments to function 'int GETVAL(int)'

It doesn't balk at GETVAL;

If I try a dummy argument, GETVAL(0); The compiler CRASHES. (I get the "report error to Microsoft" window.)

That's the least of my problems right now, tho... Even when I comment out the analog read, and directly assign 1023 to the variable val, 0x00 is written to the display 4 times. I know this, because the character that is displayed is the symbol these displays show when 0x00 is sent to them as an ASCII character.

PaulS, Yes, I am planning to feed it data from an analog input, so I am aware that it will only range from 0~1023. :slight_smile: Once I can successfully get an analog value to READ and then DISPLAY on these 4-character displays, I will be well on my way to better things! :slight_smile:

This is a challenge for me, and I enjoy the opportunity to LEARN! I think the Arduino is an amazing and powerful platform! I look forward to much more tinkering in the future! For now... I just have to keep taking baby steps.

THANK YOU, gentlemen, for your input! It is genuinely appreciated!! :smiley:

Willie...

Sorry, I overlooked that GETVAL requires an argument. With it, it compiles in IDE 1.6.6 without issues.

I think the Arduino is an amazing and powerful platform!

That it is, opening the door for many more people to learn programming skills, and to recognize just how the devices that they take for granted might actually work.

For now... I just have to keep taking baby steps.

THAT is the way to learn. Good for you!

sterretje - Hmmm... I'm using V1.6.7 of the IDE. I guess from .6 to .7 they broke it? :frowning:

PaulS - I've been tinkering with all kinds of electronics since I was a kid, so I've learned lot! C++ is a steep learning curve, for sure. (Compared to BASIC, which I know well!)

So, I'm still stuck with 0x00 being written to the 4 display characters, even when I assign a value to the variable. If I assign a 4-character number or word to the Msg variable, it displays perfectly. There's SOMETHING in the implementation of sprintf() that I'm obviously missing.

Willie...

Under normal circumstances, crashing of the IDE is not related to the software that you write. Sounds like a PC issue (in the broadest sense). Maybe start a new sketch from scratch, type the code in again (not copy/paste) and try it again? Maybe a re-install of the IDE is in order?

I don't know; crashing software is always a trial-and-error approach to get it solved.

As a workaround, you can have a look at the itoa() function that I mentioned earlier.

Just for haha’s and to get a little practice with using the analog inputs, I created this little sketch, and it works perfectly! Of course it’s kludgey, but THIS IS ONLY A TEST. :wink:

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins (4-pin mode)
// USEAGE 3-line ctrl: LiquidCrystal lcd(rs, rw, enable, d4, d5, d6, d7)
// USEAGE 2-line ctrl: LiquidCrystal lcd(rs, enable, d4, d5, d6, d7)

LiquidCrystal lcd(12, 11, 2, 3, 4, 5);

void setup() {
pinMode(13, OUTPUT);
  digitalWrite(13,0); //Turn off the LED, because it's distracting. ;)
  // set up the LCD's number of columns and rows:
  lcd.begin(20, 4);

  // Print a message to the LCD. USEAGE: setCursor(Position,Line)
  lcd.print("Analog input test"); //(Starts at 0,0 after lcd.begin)
  lcd.setCursor(0, 1); //Next line down for number display loop
}

void loop() {
  // A very simple loop to keep reading and displaying three analog input values
int  val0 = analogRead(A0); // Get the 1'st value
int  val1 = analogRead(A1); // Get the 2'nd value
int  val2 = analogRead(A2); // Get the 3'rd value
  lcd.setCursor(0, 1); //Location of first value to display
  lcd.print("A0=     "); // Clear the old value by overwriting with spaces
  lcd.setCursor(3, 1); // Position to print the number
  lcd.print(val0); // Print it
  lcd.setCursor(0, 2); // Location of second value to display
  lcd.print("A1=     "); // Clear the old value by overwriting with spaces
  lcd.setCursor(3, 2); // Position to print the number
  lcd.print(val1); // Print it
    lcd.setCursor(0, 3); // Location of third value to display
  lcd.print("A2=     "); // Clear the old value by overwriting with spaces
  lcd.setCursor(3, 3); // Position to print the number
  lcd.print(val2); // Print it
    delay(100); //Slow things down enough to read the numbers
} // Loop forever

For now, I’m looking at these three (rapidly updating/changing) inputs on the LCD, and it’s pretty cool… but it’s not what I really want to do. :slight_smile: (The analog inputs are apparently very high impedance!)

So, TECHNICALLY, all I want to be able to do, now, is read ONE analog input, convert the numeric value (0 thru 1023) into a 4-character STRING, and then send that string to my 4-character LED display sketch.

Willie…

as long as you know that the readings are indeed bounded, it is easy. Something like this:

void loop() {
  // A very simple loop to keep reading and displaying three analog input values
  int  val0 = analogRead(A0); // Get the 1'st value
  int  val1 = analogRead(A1); // Get the 2'nd value
  int  val2 = analogRead(A2); // Get the 3'rd value
  char buffer[5] = "";
  sprintf(buffer, "A0 = %04d", val0);
  lcd.setCursor(0, 1); //Location of first value to display
  lcd.print(buffer); // Clear the old value by overwriting with spaces
  //... etcetera

OK, first, THANK YOU for pointing out a few things that I was obviously missing! :slight_smile:

Adding the =" " to the char assignment was apparently what I needed!

As you had it written, I was getting "A0=%" on the display! That was a sign that you helped me to get SOMETHING right! :slight_smile: (For the first time, the display wasn't showing four 0x00 characters, which on this display is the letter i with a slant above it!)

char Msg[5]="    "; // Create the buffer for the ASCII string

int GETVAL(int val=0) //Function to get and convert the ADC value to ASCII
{
val = analogRead(A0);
sprintf(Msg, "%d", val);
}

So far, so good! I now have it showing me the analog value in realtime!! :smiley: YAHOO!!! Now all I need to do, is get it to replace the 0x00 characters with spaces when the numeric value is LESS THAN 4 chars...

This is a huge leap forward, so THANK YOU!! :slight_smile:

Willie...

I got it! :slight_smile:

I just added this line to the display code, and now it pads the right end of the display with spaces... (not ideal, as it's normal to pad the left side with spaces) but it's a lot better than those strange symbols filling the display! :slight_smile:

if ((Msg[nybble]) == (0x00)) CHR=' ';

Another update. :slight_smile: I just upgraded my IDE to the newest one, V1.6.8 and it’s much better! :slight_smile:

I’ve also tweaked this sketch some more, and it’s working 90%!

The issue I’m having now, tho, is if the value changes quickly (say, from a 4-digit value to a 1-digit value) one or 2 of those 4 digits gets “left behind” on the display. I tried assigning " " (4 spaces) to the variable Msg and sending it to the display, but that’s not clearing the extra character(s)

Example… if I’m displaying 1022 and change the input quickly to 0, the display will show something like this: “0 22”

There’s also the way the digits fill-in from the left, rather than right.

Here’s the whole thing:

// Display a numeric value to the 4 character display (Working awkwardly, but OK!)

char Msg[5]="    "; // Create the buffer for the ASCII string, filled with 4 spaces

int GETVAL(int val=0) //Function to get and convert the A(0) value to ASCII
{
  int i; //Create variable for loop
for (i = 0; i < 10; i++){
    val = val + analogRead(0);    // Read analog pin 0 ten times, adding them
  }
  val = val / 10;    // Divide by 10 to get the average (Stabilizes the display a lot!)
sprintf(Msg, "%d", val); //Create ASCII string of averaged value
}

//***************************************************
// ** This display code, below, works perfectly. ***

// datapins; lsb first (Single unit, 4 Char display)
const byte datapins[] = {2, 3, 4, 5, 6, 7, 8, 9};

// Two bits for display address on the single display unit
const byte addresspins[] = {10, 11};

void WRITEA(char A) //Address of digit (0-3) //Write character Address
{
  for (int bitcnt = 0; bitcnt < 2; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is 1 or 0
    bool bitvalue = (A & (1 << bitcnt)) == (1 << bitcnt);
    bitvalue = !bitvalue; //Invert bits to get forward scroll with positive numbers
    // set corresponding output
    digitalWrite(addresspins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the Address data to the display
}

// Convert ASCII character into parallel bits on pins for Display
void WRITED(char D)
{
  for (int bitcnt = 0; bitcnt < 8; bitcnt++)
  {
    // value (0 or 1) using bitmask to check if a bit in the character is set or not
    bool bitvalue = (D & (1 << bitcnt)) == (1 << bitcnt);
    // set corresponding output
    digitalWrite(datapins[bitcnt], bitvalue);
  }
  Pulse(13); //Write the parallel data to a character in the display
}
//Pulse a designated pin; 13=Display Write
void Pulse(byte P)
{
  digitalWrite(P, 0);
  delay(1);
  digitalWrite(P, 1);
}
// the setup function runs once when you press reset or power the board
void setup()
{
  delay(100); //Allow display to init internally with tri-state pins
  // initialize digital pins as outputs.
  for (int x = 2; x < 14; x++)
  {
    pinMode(x, OUTPUT);
    digitalWrite(x, 0); //Set ALL pins 0 to start
  }
  delay(2); // Pin 13 Low wrote 0x00 into the display unit.
  digitalWrite(13, 1); //Set Write pin high. (Pluses low to write)
  WRITED(0x8E); //Clear the display
  WRITED(0x0E); //Turn off Clear Display bit and set parameters (D=25% bright, E=50%, F=100%)
  digitalWrite(12, 1); // Set AD2 high for DATA mode
  // End of SETUP routine (Initialize the displays for data)
}
int LEN = strlen(Msg); // Get the length of the message for main "for" loop
char CHR = ' '; // Initialize the variable with a "space" character

void PrintIt() // Prints the 4 byte string, Msg[] to the display
{
  // Write each character in the string Msg[] to the display
  for (byte nybble = 0; nybble < 4; nybble++) // Loop thru 4 display characters
  {
    CHR = (Msg[nybble]); //Get the character from the string
    WRITEA(nybble); //Write the address to display digit

if ((Msg[nybble]) == (0x00)) CHR=' ';  //Replace bogus 0x00 chars with spaces

    WRITED(CHR); //Write the ASCII character to the addressed display digit
  } //End display loop for 4 characters

}

void loop() {
GETVAL(); // Get the analog value, to put into the display
PrintIt();
  delay(200);  // Slow things down to 5 per second
  
char Msg[5]="    "; // Clear display of extraneous characters
PrintIt(); //(This part isn't working)
  
} // Repeat forever

THANK YOU everyone, for your valuable input! You’ve all helped me a great deal, and I sincerely appreciate it!! :slight_smile:
I’m learning!

Willie…