форматирование символьных строк

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018
...
uint32_t i = 0;
double s = 1.2345;
char msg[ 40 ];
sprintf( msg, "%d => %8.2f", i, s );
Serial.println( msg ); 
...
 

В итоге получим вывод:

1000 => ?

Т.е. sprintf() функция есть, форматы %d, %s и т.д. - успешно работают. А формат %f/%e - нет. Почему? Хотя в описании форматов для printf() из avr-libc эти форматы (%f/%e) есть. Как можно форматировать строки?

 

 

 

 

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

При копировании фрагмента допустил ошибку, должно быть, естественно (для правильного понимания вопроса):

uint32_t i = 1000;

 

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

Причём вот так вывод вещественного значения (с приведением к строке, форматированием) нормально проходит:

Serial.println( s, 2 );

И результатом будет 1.23 - с заказанным числом знаков после десятичной точки. Т.е. форматировать оно кое-как умеет...

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sprintf искаропки не поддерживает форматный вывод с плавающей точкой. Используй dtostrf() или dtostre().

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

DetSimen пишет:

sprintf искаропки не поддерживает форматный вывод с плавающей точкой. Используй dtostrf() или dtostre().

Спасибо ... только пока не совсем понятен прототип (формат) их вызова.

 

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

DetSimen пишет:

sprintf искаропки не поддерживает форматный вывод с плавающей точкой. 

Зато очень удобно для форматирования всех других типов. Чтобы не плодить последовательность N+1 вызовов Serial.print(). Как-то так:

#include <alloca.h>

void setup() {
  Serial.begin( 9600 ); // устанавливаем последовательное соединение
}

void reply( boolean lng ) {
  unsigned long time = millis();
  char *msg = (char*)alloca( 80 );
  if( !lng )
    sprintf( msg, ".%lu", time % 1000 );
  else
    sprintf( msg, "%02lu:%02lu.%03lu", 
             ( time / 1000 ) / 60, 
             ( time / 1000 ) % 60, time % 1000 );
  Serial.println( msg ); 
}

void loop() {
  if( Serial.available() > 0 ) // есть доступные данные
    reply( '?' == Serial.read() );
  Serial.flush(); 
  delay( 300 ); 
}

И в итоге:

00:01.802
00:03.916
00:06.332
.450
.358
00:15.669
00:18.685
00:45.133
01:40.422

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Olej пишет:

DetSimen пишет:

dtostrf() или dtostre().

не совсем понятен прототип (формат) их вызова.

Документация по AvrLibC на сайте производителя контроллеров AVR. RTFM.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Olej пишет:

Зато очень удобно ... Как-то так:

Если printf Вам нужен только для вывода в поток (как в Вашем примере), то ещё удобнее делать это (выводить в поток) сразу, без запроса буфера. Например,

//	Эти три строки пишем в начале файла
#include <stdio.h>
static int serial_fputchar(const char ch, FILE *stream) { Serial.write(ch); return ch; }
static FILE *serial_stream = fdevopen(serial_fputchar, NULL);

void setup(void) { 
	stdout = serial_stream; // эта строка первая в setup
	//
	// А ТЕПЕРЬ ТУПО ПОЛЬЗУЕМСЯ printf
	//
	Serial.begin(115200);
	int a = 8;
	// Печатаем a в разных видах
	printf("a=%d, a=%02d, a=%04d, a=%05d, a=%06d\n", a, a, a, a, a);
}

void loop(void){}

В принципе, можно там же (вместо NULL в строке 4) и scanf назначить. Можете попробовать, отлично работает.

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

Это именно я и смотрел ... но там много и не совсем понятно с параметрами... да и с возвращаемым значением.

Разобрался:

void setup() {
  Serial.begin( 9600 );
}

void loop() {
  if( Serial.available() ) { // есть доступные данные
    Serial.read();
    char msg[ 40 ] = "", flt[ 12 ] = "";
    long r = random();  
    double s = r / 1000.;
    Serial.print( r, DEC );
    Serial.print( " => " );
    Serial.println( s, 2 ); 
    sprintf( msg, "%ld => %s", r, dtostrf( s, 8, 2, flt ) );
    Serial.println( msg );
  }     
  delay( 500 );
}

В итоге:

16807 => 16.81
16807 =>    16.80
282475249 => 282475.25
282475249 => 282475.25

 

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

ЕвгенийП пишет:

Если printf Вам нужен только для вывода в поток (как в Вашем примере), то ещё удобнее делать это (выводить в поток) сразу, без запроса буфера. Например,

Любопытно.

Спасибо.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Кстати, это приём (назначение потока) будет работать с любым потоком - например, SoftSerial и большинство библиотек для LCD и OLED (Adafruit очень любит свои библиотеки для экранов от Stream наследовать).