Help with Arduino serial print. - Page 1
Forum section: electronics / beginners

author: geggi1
author_rank: Frequent Contributor
timestamp: August 03, 2022, 03:59:52 pm
content: Hi!I'm working on one of my summer projects for a 4 channel voltmeter based on an Arduino and some voltage dividers.My project is to make a logger for four small PV panels form solar led lights pointing in for directions and log the voltage.This will be used as a tool to plan installation of solar panels.Everything is more or less just a copy of this project.https://startingelectronics.org/software/processing/software-voltmeter/My code looks like this after some modifications.Code:// number of analog samples to take per reading, per channel#define NUM_SAMPLES 10// voltage divider calibration values#define DIV_1 11.1346#define DIV_2 11.1969#define DIV_3 11.0718#define DIV_4 11.0718// ADC reference voltage / calibration value#define V_REF 4.991int sum = {0}; // sums of samples takenunsigned char sample_count = 0; // current sample numberfloat voltage = {0.0}; // calculated voltageschar l_cnt = 0; // used in 'for' loopsvoid setup(){Serial.begin(9600);}void loop(){// take a number of analog samples and add them upwhile (sample_count < NUM_SAMPLES) {// sample each channel A2 to A5for (l_cnt = 0; l_cnt < 4; l_cnt++) {sum += analogRead(A2 + l_cnt);}sample_count++;delay(10);}// calculate the voltage for each channelfor (l_cnt = 0; l_cnt < 4; l_cnt++) {voltage = ((float)sum / (float)NUM_SAMPLES * V_REF) / 1024.0;}// each voltage is multiplied by the resistor network// division factor to calculate the actual voltagevoltage = voltage * DIV_1;voltage = voltage * DIV_2;voltage = voltage * DIV_3;voltage = voltage * DIV_4;// send voltages to Processing application via serial port / USB// voltage 1 - A (pin A2)// Serial.print("A ");Serial.print(voltage, 3);Serial.println(",");// Serial.print("B ");Serial.print(voltage, 3);Serial.println(",");// Serial.print("C ");Serial.print(voltage, 3);Serial.println(",");// Serial.print("D ");Serial.print(voltage, 3);Serial.println(",");delay(10);// reset count and sumssample_count = 0;for (l_cnt = 0; l_cnt < 4; l_cnt++) {sum = 0;}}When using serial monitor the data stream comes like this.CH a ,CH b ,CH c ,CH d ,I want my data stream to coma out like this in the serial monitor.CH a ,CH b ,CH c ,CH d ,

author: dl6lr
author_rank: Frequent Contributor
timestamp: August 03, 2022, 04:03:45 pm
content: println("") prints the characters and adds a line feed. To omit the line feed, use print(""). Maybe you want to print all 4 elements in one line and add a line feed to separate the new group of values?

author: geggi1
author_rank: Frequent Contributor
timestamp: August 03, 2022, 04:12:05 pm
content: Thanks for the quick reply!I need a bit more help here because I'm close to illiterate on coding.Would you be so kind to make a code example for me?Just the lines regarding sending the data stream.

author: AndyBeez
author_rank: Frequent Contributor
timestamp: August 03, 2022, 04:26:25 pm
content: You can use the printf operand, which outputs formatted text. Very usefull to know. See the examples here:https://m.cplusplus.com/reference/cstdio/printf/I also noted in your code that DIV_3 and DIV_4 have the same defined values. Is this correct?

author: geggi1
author_rank: Frequent Contributor
timestamp: August 03, 2022, 05:46:03 pm
content: The DIV_? konstant is a calibration factor for each channel.I got the code up and running after a bit of play.There is a bit more fine tuning but I'm getting there.Next task on the list is to find a software capable of receiving the data stream and writ it to a CSV file with timestamp.In the future i will also add a propper voltage reference to get better measurements.Code:// number of analog samples to take per reading, per channel#define NUM_SAMPLES 10// voltage divider calibration values#define DIV_1 11.1346#define DIV_2 11.1969#define DIV_3 11.0718#define DIV_4 11.0718// ADC reference voltage / calibration value#define V_REF 4.991int sum = {0}; // sums of samples takenunsigned char sample_count = 0; // current sample numberfloat voltage = {0.0}; // calculated voltageschar l_cnt = 0; // used in 'for' loopsvoid setup(){Serial.begin(9600);}void loop(){// take a number of analog samples and add them upwhile (sample_count < NUM_SAMPLES) {// sample each channel A2 to A5for (l_cnt = 0; l_cnt < 4; l_cnt++) {sum += analogRead(A2 + l_cnt);}sample_count++;delay(10);}// calculate the voltage for each channelfor (l_cnt = 0; l_cnt < 4; l_cnt++) {voltage = ((float)sum / (float)NUM_SAMPLES * V_REF) / 1024.0;}// each voltage is multiplied by the resistor network// division factor to calculate the actual voltagevoltage = voltage * DIV_1;voltage = voltage * DIV_2;voltage = voltage * DIV_3;voltage = voltage * DIV_4;// send voltages to Processing application via serial port / USB// voltage 1 - A (pin A2)// Serial.print("A ");Serial.print(voltage, 4);Serial.print(",");// Serial.print("B ");Serial.print(voltage, 4);Serial.print(",");// Serial.print("C ");Serial.print(voltage, 4);Serial.print(",");// Serial.print("D ");Serial.print(voltage, 4);Serial.print(",");Serial.println("");delay(10);// reset count and sumssample_count = 0;for (l_cnt = 0; l_cnt < 4; l_cnt++) {sum = 0;}}

author: rooppoorali
author_rank: Regular Contributor
timestamp: August 04, 2022, 10:24:57 am
content: println command always starts a new line. If you want your results to be shown in a single line, separated by commas, just remove all the Serial.println(","); from your code. Just write Serial.print(","); instead.

author: brucehoult
author_rank: Super Contributor
timestamp: August 04, 2022, 12:38:49 pm
content: Quote from: AndyBeez on August 03, 2022, 04:26:25 pmArduino code doesn't use the C standard library as many devices are too small to support it.

author: eugene
author_rank: Frequent Contributor
timestamp: August 04, 2022, 12:47:58 pm
content: Quote from: brucehoult on August 04, 2022, 12:38:49 pmJust include stdio.h.I have used sprintf() to construct a string as with printf() and then printed it with Serial.print(). Not sure why I didn't just use printf() in the first place... This is with devices that have lots of resources (teensy.) Should work on an Uno as long as you don't run out of memory.

author: mariush
author_rank: Super Contributor
timestamp: August 04, 2022, 01:30:22 pm
content: I'd suggest adding an external voltage reference, this way you'll have simpler math. If you tweak your voltage dividers the right way, you could get rid of floating point completely.For example, add a 4.096v voltage reference, and you'll just have to multiply by 4mV per step to get your value in mV if you use the ADC in 10 bit mode. Or basically shift the value 2 bits to the left.ex of voltage references:1.0% 4.096v for 0.36$ each (150ppm to-92) :https://www.digikey.com/en/products/detail/microchip-technology/LM4040DYM3-4-1-TR/7715171.0% 4.096v for 0.8$ each (100ppm) :https://www.digikey.com/en/products/detail/texas-instruments/LM4040DIM3-4-1-NOPB/3051390.5% 4.096v for 0.9$ each :https://www.digikey.com/en/products/detail/texas-instruments/LM4040CIM3-4-1-NOPB/305135If you make your voltage divider ratio 8 or 16, again you can multiply by 8 or 16 or just shift the value by 3-4 bits to the left, and stay with integer numbers all the time. When it comes to printing the value, you can just add the dot before the last 3 digits.For example use 1020 kOhm (1M ohm + 20kOhm in series) and 68 kOhm instead of 1M / 100k to have a ratio of 1:16 , so 64v becomes 4v, which is almost the peak of your 4.096v reference, and you'll still have up to 5v for overshoots.With 1% precision a 1 mohm could be 990-1010 kOhm so best to buy a bunch of them (ex 20-50) and pick the ones closest to the value you want, and then add a second resistor in series to get to 1020k or whatever value you decide.The 68k resistor will also be only 1% precise, so you can tweak that 20k value or add a third resistor in series to get exactly the ratio you want - resistors are super cheap. You can also add a resistor in parallel with another to slightly adjust its value. It's often done in multimeters, you can see two ceramic resistors or capacitors one on top of another to get a precise value.You can buy resistors with higher precision ex 0.5% or 0.1% but... often not worth it. Up to you if you really need/want that precise.0.1% 1M ohm resistors are 0.65$ each in through hole versions on Digikey, 25-35 cents each for surface mountand... even if you don't have an exact ratio, you have enough memory that you could add custom tables in your code to adjust the value in software ex. if adc value between 50 and 200, add 20mV, for 201..300 add 30mV etc

author: Ian.M
author_rank: Super Contributor
timestamp: August 04, 2022, 01:37:51 pm
content: @eugine,You probably found that the streamstdoutwhichprintf()relies on for character output wasn't mapped to the UART, and it was more trouble than its worth to write a shim for it usingSerial.write().

author: AndyBeez
author_rank: Frequent Contributor
timestamp: August 04, 2022, 01:51:09 pm
content: QuoteYeh, printf() is a bit grown-up for bambinuinos. Sprintf() has a lower resource footprint.I note there is a (kind of) LibPrinf() for the arduino IDE here:https://github.com/embeddedartistry/arduino-printfRemember to store character format strings as static constants in progmem (flash) space.https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

author: geggi1
author_rank: Frequent Contributor
timestamp: August 04, 2022, 04:16:07 pm
content: I'll probably add a RTC, Voltage ref and a SDcard to the unit in the future but i dont have the hardware at the moment.My main goal now is to get the import to Excel data streamer working.Transfer form UNO to Mini and look into getting a bit better stability on the measurement by increasing the number of samples for each loop.Next I will add some more channels so that I can use all the channels on the UNO and MINI.