Форматированный вывод в Serial

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015
Начинающий коллега увидел у меня в коде форматированный по типу printf отладочный вывод в Serial (когда вместо:
Serial.print("U=");
Serial.print(u);
Serial.print("V f=");
Serial.print(f);
Serial.println("KHz");
пишется
SerialPrintf("U=%dV f=%dKHz\n, v, f);
попросил дать ему код и посоветовал опубликовать здесь, т.к., по его мнению, многие с удовольствием воспользуются.
В последнем сомневаюсь, но, на всякий случай, публикую, может и правда кому сгодится. Вывод реализован одной функцией. Параметры у неё как у printf. Текст функции и пример использования:
/*
 * SerialPrintf
 * Реализует функциональность printf в Serial.print
 * Применяется для отладочной печати
 * Параметры как у printf
 * Возвращает 
 *		0 - ошибка формата
 *		отрицательное чило - нехватка памяти, модуль числа равен запрашиваемой памяти
 *		положительное число - количество символов, выведенное в Serial
 */
const size_t SerialPrintf (const char *szFormat, ...) {
	va_list argptr;
	va_start(argptr, szFormat);
	char *szBuffer = 0;
	const size_t nBufferLength = vsnprintf(szBuffer, 0, szFormat, argptr) + 1;
	if (nBufferLength == 1) return 0;
	szBuffer = (char *) malloc(nBufferLength);
	if (! szBuffer) return - nBufferLength;
	vsnprintf(szBuffer, nBufferLength, szFormat, argptr);
	Serial.print(szBuffer);
	free(szBuffer);
	return nBufferLength - 1;
}


/*
 * Пример использования SerialPrintf
 */
void setup() {
	Serial.begin(19200);
	while(!Serial) yield();
	SerialPrintf("Fun begins!\n");
	SerialPrintf("%d%s = 0x%04X%s\n", 321, "(decimal)", 321, "(hexadecimal)");
}

void loop() {
}

 

 
Piskunov
Offline
Зарегистрирован: 13.02.2014

Спасибо.

Для меня это полезная и удобная вещь.
Не могли бы Вы в дополнение пояснить, как (где) узнать про другие функции, которые не описаны в Arduino Language Reference, но реально работают?

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

Piskunov пишет:

Не могли бы Вы в дополнение пояснить, как (где) узнать про другие функции, которые не описаны в Arduino Language Reference, но реально работают?

Я не знаю, я просто знаю стандартные библиотеки С/С++ и, когда мне нужно что-то оттуда, проверяю, есть ли это в библиотеках Arduino (просто вставляю в код и смотрю скомпилирует или скажет "нет такой"). Там ведь, на самом деле язык Ардуино - всего лишь препроцессор, после работы которого код передаётся стандартному С++ компилятору gcc.

ratman
Offline
Зарегистрирован: 11.10.2015

Ты бы хоть копирайт свой оставил, чтобы страна помнила своих героев.

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

1. Хотельсь бы знать, зачем в строке 15 дополнительно прибавляется 1. vsprintf сам по себе возвращает длину с учетом терминирующего символа.

 

2. При использовании данной процедуры необходимо помнить, что она использует кучу, уменьшая при этом размер стека, причем на заранее неизвестную глубину. Что в условиях существенно ораниченного объема памяти Arduino может оказаться весьма существенным.

Maverik
Offline
Зарегистрирован: 12.09.2012

спасибо, полезная штучка.

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

andriano пишет:

1. Хотельсь бы знать, зачем в строке 15 дополнительно прибавляется 1. vsprintf сам по себе возвращает длину с учетом терминирующего символа.

Здесь используется vsnprintf, а она возвращает длину без терминального нуля ( just попробуйте :)))

andriano пишет:

2. При использовании данной процедуры необходимо помнить, что она использует кучу, уменьшая при этом размер стека, причем на заранее неизвестную глубину. Что в условиях существенно ораниченного объема памяти Arduino может оказаться весьма существенным.

Ну, да, кто-то использует таймер, кто-то пины, кто-то кучу, кто-то стек - всем в этом мире от бедняги разработчика чего-то нужно :)))))

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

Цитата:
Здесь используется vsnprintf, а она возвращает длину без терминального нуля ( just попробуйте :)))

А Вы добавьте из предложенной Вами функции вывод в Serial значения, возвращаемого vsnprintf, и посмотрите, что она напечатает.

Хотя, в принципе, если существующая реализация сделана не постандарту, то, согласен, лучше перестраховаться и добавить - на случай, когда поправят реализацию.

Цитата:
Ну, да, кто-то использует таймер, кто-то пины, кто-то кучу, кто-то стек - всем в этом мире от бедняги разработчика чего-то нужно :)))))

Так никто не говорит, что это плохо. Просто эту функцию, как и любое сильнодействующее средство, нужно применять осторожно.

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

andriano пишет:

нужно применять осторожно.

В смысле форматы на мегабайты не писать? А и напишет кто - если ей памяти не хватит, она код ошибки вернёт. Ничего не сломается, просто не выведет ничего.

Ваше "осторожно" напомнило книгу "Физики продолжают шутить". Там был фразеологический словарь для перевода научных статей на нормальный язык. В частности были такие определения:

"Во время эксперимента прибор был слегка повреждён" - перевод: "Уронили на пол"

"С прибором обращались с исключительной осторожностью" - перевод: "Не роняли на пол" :)

_kp
Offline
Зарегистрирован: 07.10.2016

Случайно натолкнулся на этот  очередной "велосипед".
Всё время это делалось стандартным образом:

#include <stdio.h>
#include <avr/pgmspace.h>

int uart0_putchar(char ch, FILE *stream)
{
  Serial.print(ch);
  return 0;
}

FILE *uart_stream;

void setup(void)
{ 
  Serial.begin(9600);
  uart_stream = fdevopen(uart0_putchar, /*uart0_getchar*/ NULL );
  stderr = stdout = stdin = uart_stream;
  printf("Init stdio - OK.\n");
  printf_P(PSTR("\nString in FLASH.\n"));
}

Преимущества:
- Расход ОЗУ меньше, вывод идёт сразу в UART.
- Можно использовать строковые константы из флеш.
- меньше лишнего кода
- По аналогии, можно связать printf с буферизированным UART(ами) и LCD.

PS: Впрочем, за рабочий "велосипед" - зачет. Ардуино для этого и предназначена.
 

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

_kp пишет:

Преимущества:

- Расход ОЗУ меньше, вывод идёт сразу в UART.
- Можно использовать строковые константы из флеш.
- меньше лишнего кода
- По аналогии, можно связать printf с буферизированным UART(ами) и LCD.
 

Недостатки

Отжирает лишние 900 байтов памяти программы. Пруф. ниже

/*
 * SerialPrintf
 * Реализует функциональность printf в Serial.print
 * Применяется для отладочной печати
 * Параметры как у printf
 * Возвращает 
 *		0 - ошибка формата
 *		отрицательное чило - нехватка памяти, модуль числа равен запрашиваемой памяти
 *		положительное число - количество символов, выведенное в Serial
 */
const size_t SerialPrintf (const char *szFormat, ...) {
	va_list argptr;
	va_start(argptr, szFormat);
	char *szBuffer = 0;
	const size_t nBufferLength = vsnprintf(szBuffer, 0, szFormat, argptr) + 1;
	if (nBufferLength == 1) return 0;
	szBuffer = (char *) malloc(nBufferLength);
	if (! szBuffer) return - nBufferLength;
	vsnprintf(szBuffer, nBufferLength, szFormat, argptr);
	Serial.print(szBuffer);
	free(szBuffer);
	return nBufferLength - 1;
}


/*
 * Пример использования SerialPrintf
 */
void setup() {
	Serial.begin(9600);
	printf("Hello, world!\n");
}

void loop() {} 

/////////////////////////////////////////////////////////
//	Результат компиляции в IDE 1.6.12
// Скетч использует 1 508 байт (4%) памяти устройства. 
/////////////////////////////////////////////////////////
#include <stdio.h>
#include <avr/pgmspace.h>

int uart0_putchar(char ch, FILE *stream)
{
  Serial.print(ch);
  return 0;
}

FILE *uart_stream;

void setup(void)
{ 
  Serial.begin(9600);
  uart_stream = fdevopen(uart0_putchar, /*uart0_getchar*/ NULL );
  stderr = stdout = stdin = uart_stream;
  printf("Hello, world!\n");
}

void loop() {} 

/////////////////////////////////////////////////////////
//	Результат компиляции в IDE 1.6.12
// Скетч использует 2&nbsp;408 байт (7%) памяти устройства.
/////////////////////////////////////////////////////////

Но за хороший учебный материал, безсусловно, зачёт. Дополнение очень в струю. Жаль нельзя добавить в первоначальный топик.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Могу показать свой логгер, который использую везде в коде: TelnetServer

Там файлы: Logger.cpp и Logger.h

Используется так:

extern CLogger Logger;

#define logdebug( fmt, ... ) Logger.Log( PSTR( "[DEBUG] " fmt ), ##__VA_ARGS__ )
#define logerror( fmt, ... ) Logger.Log( PSTR( "[ERROR] " fmt ), ##__VA_ARGS__ )
char s1[32], s2[32];

strncpy_P( s1, ( const char * ) CVersion::GetVersionString(), sizeof( s1 ) );
strncpy_P( s2, ( const char * ) CVersion::GetBuildDateString(), sizeof( s2 ) );

s1[ sizeof( s1 ) - 1 ] = '\0';
s1[ sizeof( s2 ) - 1 ] = '\0';

logdebug( "Telnet Server, version %s, %s", s1, s2 );

Если кто захочет использовать, придётся доработать под себя. У меня для каждой строчки выводится дата и время, т.к. есть поддержка времени в коде программы.

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

uni пишет:

Могу показать свой логгер, который использую везде в коде: TelnetServer

Спасибо!

Тема изначально учебная, поэтому здорово, что появляются посты типа Вашего и _kp. Больше примеров, хороших и разных!

Сам я для отладочной печати использую потоковую нотацию, очень компактно, но, разумеется без printf'овского форматирования. Но там есть засада. Параметр передаётс по значению, поэтому, например, если печатать String, то создаётся новый экземпляр со всеми вытекающими.

_kp
Offline
Зарегистрирован: 07.10.2016

Цитата:
Отжирает лишние 900 байтов памяти программы. Пруф. ниже

1.
Отлично! Будем считать, первый вариант оптимизированным, например чиcто для отладочного вывода. Когда от библиотеки stdio нужен только printf.

2.
С первым вариантом есть проблема с перерасходом ОЗУ.
При вызове например SerialPrintf("Fun begins!\n"); Строковая константа "Fun begins!\n" будет размещена и во флеш, и в ОЗУ(а для AVR это сверх зло).
Плюс выделяется дополнительный временный буфер.
Несколько десятков "отладочных printf", и для основной программы совсем нехватит ОЗУ.
На AVR обычно не пользуются стандартным printf/sprintf, а используют printf_P/sprintf_P

3.
На минималистичной программе, сравнение размеров кода не объективно, практическая разница будет всего 120..150 байт. Но зато единообразно работает ввод/вывод и UART, и с LCD, и с SdCard.

4,
При переделке SerialPrintf для экономии ОЗУ,  при замене  vsnprintf на vsnprintf_P,  размер  минималистичных программ заметно распухает, и не менее заметно сокращается разница в размера кода для обоих вариантов, приближаясь к рассчитанной выше.. 

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

О, Господи, не лезьте в драку, это учебный материал. Я показал как использовать sprintf, Вы показали как перенаправить поток - прекрасно, что показано и то, и другое.

тем более, что 

_kp пишет:

На AVR обычно не пользуются стандартным printf/sprintf, а используют printf_P/sprintf_P

Господь с Вами, в сколько-нибудь серьёзных программах никто не использует ни того, ни другого. 

_kp
Offline
Зарегистрирован: 07.10.2016

И снова привет.

 

На станице http://playground.arduino.cc/Main/Printf говорится, о том что можно добавить метод printf в базовый класс Print.
Фича в том, что метод xxx.printf появится не только у Serial, но и у всех классов, его использующих, то есть LCD и TFT дисплеи.

Там же  есть и пример, показывающий что именно надо подправить в фирменной библиотеке.
Но, как обычно, там вариант с временным текстовым буфером. Что для AVR крайне вредно, и буфера всё равно мало.

Вот сделал почти тоже самое, но с существенно меньшим расходом ОЗУ, и работающее быстрее.
То есть, это вариант добавления printf в базовый класс Print.
После чего можно писать примерно так:

   Serial.printf("var1=%4d var2=0x%06X\n",var1,var2);
   TFT1.printf(F("/e[%d;%dH V%02u = %08lu"), yst+n,x, d[n]);

Для установки нужно изменить оригинальные библиотеки Arduino, предварительно сохранив оригиналы.

файл: arduino-1.6.11\hardware\arduino\avr\cores\arduino\Print.h
// Добавляем в описание класса это:
 

   FILE oStream; //жрёт 12байт ОЗУ. Конечно, можно вынести в print, но быстродействие упадёт.
    size_t printf (const char *szFormat, ...);
    size_t printf (const __FlashStringHelper  *szFormat, ...);

файл: arduino-1.6.11\hardware\arduino\avr\cores\arduino\Print.cpp
// Добавляем в файл  это:
 


#include <stdarg.h>
#include <stdio.h>
                            
//non class member function for callback from stdio.FILE steam.
//in field FILE.udata  passed class->this
static int dummy_putchar(char c, FILE *stream  ) 
{
    Print* pPrint = (Print*)(stream->udata); 
    pPrint->print(c);
    return 1;
}

size_t Print::printf(const char *fmt, ...)
{
   oStream.put   = dummy_putchar;
   oStream.flags |= __SWR;
   oStream.udata = (void*) this;
   //----  
   oStream.flags &= ~__SPGM;
   //
   va_list ap;
   va_start( (ap), (fmt) );     
   size_t i = vfprintf(&oStream, fmt, ap);
   va_end(ap); 
   return i; 
}

size_t Print::printf(const __FlashStringHelper  *fmt_P, ...)
{ 
   PGM_P fmt = reinterpret_cast<PGM_P>(fmt_P);  

   oStream.put   = dummy_putchar;
   oStream.flags |= __SWR;
   oStream.udata = (void*)this;
   //----
   oStream.flags |=  __SPGM;    
   //
   va_list ap;
   va_start( (ap), (fmt) );   //Ignore warning, code worked: "second parameter of 'va_start' not last named argument" 
   size_t i = vfprintf(&oStream, fmt, ap);

   va_end(ap); 
   return i; 
}

Результаты тестов.
1.  avr-g++ гененерирует компактнее код для статических классов, по сравнению с передачей кучи указателей в функции в стиле си.
То есть добавление printf в базовый класс Print выгоднее, по сравнению с fprintf(stream,format,...). Объём кода меньше.
При объёме кода за 20кБ этот вариант, printf в классе даёт самый компактный код.

2. При неиспользовании printf, лишнего кода во флеш нет.

3. Даже при неиспользовании printf, отъедается 12 байт ОЗУ, под структуру FILE,
которую можно перенести в методы print, но  нецелесообразно из за быстродействия.
При вызове Serial.print, думаю разницы в быстродействии не увидеть, а при выводе на TFT LCD быстродействие и так на грани возможного.

4. Расход ОЗУ минимален. 12 байт под структуру FILE, 11+1 временный буфер в libc.vprintf плюс стек под переменные.

5.  Ну и о недостатках. Библиотеки vprintf/printf/sprintf в Arduino без поддержки float.
Штатными методами не лечится.
- Можно подменить библиотеку, но так каждый проэкт будет больше, что нехорошо.
- Можно использовать arduino-makefile. Кроме того что новичкам надо разбираться, проблем нет.
- Можно для форматирования float использовать - dtostrf
- Ну и конечно, не "злоупотреблять" float'ами на AVR.    

ps: я так понял, что файл то тут приложить ни как?

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

_kp пишет:
 

ps: я так понял, что файл то тут приложить ни как?

файл - нет.

код - да.

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

ratman пишет:

Ты бы хоть копирайт свой оставил, чтобы страна помнила своих героев.

Коллега - копираст?

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Добрый день. 

Взял чей то вполне рабочий скетч, немного переделал под себя, пока все устравало. Но ТЗ расширилось и вместо вывода в порт чисел типа byte, понадобилось выволдить числа double.

Привожу первый рабочий вариант

#include <Wire.h> // библиотека для управления устройствами по I2C 

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
byte kp1 = 0;      byte kp2 = 0; 
byte ki1 =  0;      byte ki2 = 0; 
byte kd1 =  0;      byte kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  byte *k1;
};

struct MENU_2{ //
char name2[13];
  byte *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)++;
        (*nMenu[curMenu].k2)++;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)--;;
      (*nMenu[curMenu].k2)--;
  }


  //вывод использую буфер
  char ch1[16];
  char ch2[16]; 
  
  snprintf(ch1, 16, "%d         ", *mMenu[curMenu].k1); 
  snprintf(ch2, 16, "%d         ", *nMenu[curMenu].k2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.println(mMenu[curMenu].name1);
  Serial.println(ch1);
  Serial.println(nMenu[curMenu].name2);
  Serial.println(ch2);
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

Значения перед выводом хранятся в строковом буферe посредством snprintf . Вот ее описание:

int snprintf(char *restrict buf, size_t num,
              const char restrict format, ...);

Она идентична функции sprintf() за исключением того, что в массиве, адресуемом указателем buf, будет сохранено максимум num-1символов. По окончании работы функции этот массив будет завершаться символом конца строки (нуль-символом). Таким образом, функция snprintf() позволяет предотвратить переполнение буфера buf.

Нерабочий 

#include <Wire.h> // библиотека для управления устройствами по I2C 

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
double kp1 = 0;      double kp2 = 0; 
double ki1 =  0;      double ki2 = 0; 
double kd1 =  0;      double kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  double *k1;
};

struct MENU_2{ //
char name2[13];
  double *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }


  //вывод использую буфер
  char ch1[16];
  char ch2[16]; 
  
  snprintf(ch1, 16, "%f         ", *mMenu[curMenu].k1); 
  snprintf(ch2, 16, "%f         ", *nMenu[curMenu].k2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.println(mMenu[curMenu].name1);
  Serial.println(ch1);
  Serial.println(nMenu[curMenu].name2);
  Serial.println(ch2);
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

В первом варианте спецификатор d - для целого знакового десятичного числа, во втором  f - для число с плавающей точкой. Подскажите в чем не прав

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018
//переменные которые надо менять
byte kp1 = 0;      byte kp2 = 0; 
byte ki1 =  0;      byte ki2 = 0; 
byte kd1 =  0;      byte kd2 = 0; 
double kp1 = 0;      double kp2 = 0; 
double ki1 =  0;      double ki2 = 0; 
double kd1 =  0;      double kd2 = 0; 

Чтобы легче понять, вот их основные различия. Во втором коде в порт выводятся не числа а  знаки ?. 

snprintf(ch1, 16, "%d        ", *mMenu[curMenu].k1); 
snprintf(ch2, 16, "%d        ", *nMenu[curMenu].k2);
snprintf(ch1, 16, "%f         ", *mMenu[curMenu].k1); 
snprintf(ch2, 16, "%f         ", *nMenu[curMenu].k2);

 

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

В том, что sprintf с плавающей точкой ниработаить

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

А чем это заменить чтобы заработало?

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

dtostrf()

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018
strtod видимо. Функция strtod преобразовывает строку string в double. Да уже понял
DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Strtod это перевести строку в double

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

А взат, из числа в строку - dstrtof()

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

BuonanotteMasha,

1.
кажется, где-то в опциях линкера можно было включить поддержку флоатов в штатной библиотеке, но точно не помню и не уверен.

2.
Если есть лишние полкило памяти программ, то можно обойтись без печати в буфер, а связать штатный printf с потоком и выводить в любой поток просто штатным printf. ПРимеры я тут в разных темах много раз выкладывал.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Да спасибо, уже работает

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
float kp1 = 0;      float kp2 = 0; 
float ki1 =  0;      float ki2 = 0; 
float kd1 =  0;      float kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  float *k1;
};

struct MENU_2{ //
char name2[13];
  float *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }


  //вывод использую буфер
  char charbuf1[16];
  char charbuf2[16]; 
  
  dtostrf(*mMenu[curMenu].k1, 16, 3, charbuf1);
  dtostrf(*nMenu[curMenu].k2, 16, 3, charbuf2);
  
  //snprintf(charbuf1, 16, "%f         ", *mMenu[curMenu].k1); 
  //snprintf(charbuf2, 16, "%f         ", *nMenu[curMenu].k2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.println(mMenu[curMenu].name1);
  Serial.println(charbuf1);
  Serial.println(nMenu[curMenu].name2);
  Serial.println(charbuf2);
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Только не пойму, почему в порт только одно число выводится

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Тут ошибок не наблюдаю

  //вывод использую буфер
  char charbuf1[16];
  char charbuf2[16]; 
  
  dtostrf(*mMenu[curMenu].k1, 16, 3, charbuf1);
  dtostrf(*nMenu[curMenu].k2, 16, 3, charbuf2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.println(mMenu[curMenu].name1);
  Serial.println(charbuf1);
  Serial.println(nMenu[curMenu].name2);
  Serial.println(charbuf2);
  }

 

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

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

BuonanotteMasha,

1.
кажется, где-то в опциях линкера можно было включить поддержку флоатов в штатной библиотеке, но точно не помню и не уверен.

я пыталса анажды найти, как в спринтф включать плавающую точку, прям из атмеловской документации пытался повторить, неделю нерничал, пил по чёрному, но так и не смог. 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Танцы с бубном не помогли 

#include <Wire.h> // библиотека для управления устройствами по I2C 

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
float kp1 = 0;      float kp2 = 0; 
float ki1 =  0;      float ki2 = 0; 
float kd1 =  0;      float kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  float *k1;
};

struct MENU_2{ //
char name2[13];
  float *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }


  //вывод использую буфер
  static char charbuf1[16];
  static char charbuf2[16]; 
  
  dtostrf(*mMenu[curMenu].k1, 16, 3, charbuf1);
  dtostrf(*nMenu[curMenu].k2, 16, 3, charbuf2);
  
  //snprintf(charbuf1, 16, "%f         ", *mMenu[curMenu].k1); 
  //snprintf(charbuf2, 16, "%f         ", *nMenu[curMenu].k2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.print(mMenu[curMenu].name1);
  Serial.println(charbuf1);
  Serial.print(nMenu[curMenu].name2);
  Serial.println(charbuf2);
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

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

Внимательно пока не смотрел, но попробуйте поставить после строки 92 Serial.flush();

Виноват, поправил на 92 - в общем, после печати сразу. А то в старом скетче это было 90.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Нет не помогло, пробую с floatToString. Поменял как вы сказали на 92 тоже нет

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

Покажите текущий код (в котором не помогло)

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018


//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
float kp1 = 0;      float kp2 = 0; 
float ki1 =  0;      float ki2 = 0; 
float kd1 =  0;      float kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  float *k1;
};

struct MENU_2{ //
char name2[13];
  float *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }


  //вывод использую буфер
  static char charbuf1[16];
  static char charbuf2[16]; 
  
  dtostrf(*mMenu[curMenu].k1, 16, 3, charbuf1);
  dtostrf(*nMenu[curMenu].k2, 16, 3, charbuf2);
  
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.print(mMenu[curMenu].name1);
  Serial.println(charbuf1);
  Serial.print(nMenu[curMenu].name2);
  Serial.println(charbuf2);
  Serial.flush();
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Евгений спасибо вам за помощь, но у меня самого получилось

#include<stdlib.h> // included for floatToString function

String floatToString(float x, byte precision = 2) {
  char tmp[50];
  dtostrf(x, 0, precision, tmp);
  return String(tmp);
}

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
float kp1 = 0;      float kp2 = 0; 
float ki1 =  0;      float ki2 = 0; 
float kd1 =  0;      float kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  float *k1;
};

struct MENU_2{ //
char name2[13];
  float *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }


  //вывод использую буфер
  static char charbuf1[16];
  static char charbuf2[16]; 
  
  //dtostrf(*mMenu[curMenu].k1, 16, 3, charbuf1);
  //dtostrf(*nMenu[curMenu].k2, 16, 3, charbuf2);
  
   String str1 = floatToString(*mMenu[curMenu].k1); // conversion call
   String str2 = floatToString(*nMenu[curMenu].k2); // conversion call
  //floatToString(charbuf1, *mMenu[curMenu].k1, 2);
  //floatToString(charbuf2, *nMenu[curMenu].k2, 2);
  
  //snprintf(charbuf1, 16, "%f         ", *mMenu[curMenu].k1); 
  //snprintf(charbuf2, 16, "%f         ", *nMenu[curMenu].k2);
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
  Serial.print(mMenu[curMenu].name1);
  Serial.println(str1);
  Serial.print(nMenu[curMenu].name2);
  Serial.println(str2);
  Serial.flush();
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

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

проблема в указателях

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Окончательный рабочий код

#include<stdlib.h> // included for floatToString function

String floatToString(float x, byte precision = 2) {
  char tmp[50];
  dtostrf(x, 0, precision, tmp);
  return String(tmp);
}

//Назначаем пины кнопок управления
#define BUTTON_RIGHT 10
#define BUTTON_UP 9
#define BUTTON_DOWN 8

//состояние кнопок по умолчанию
boolean State_Up = LOW;
boolean State_Down = LOW;
boolean State_Left = LOW;
boolean State_Right = LOW;
boolean State_Ok = LOW;

//переменные которые надо менять
float kp1 = 0;      float kp2 = 0; 
float ki1 =  0;      float ki2 = 0; 
float kd1 =  0;      float kd2 = 0; 

//переменные для кнопок
long ms_button = 0;
boolean  button_state = false;
boolean  button_long_state = false;

byte curMenu = 0; //текущий пункт меню,
bool b_ShowmMenu = 0; // флаг отображения меню
const byte CountMenu = 3; //количество пунктов меню
 
//массив элементов меню
struct MENU_1{ //
char name1[13];
  float *k1;
};

struct MENU_2{ //
char name2[13];
  float *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_1 mMenu[CountMenu] = {
"   kP1 =", &kp1,
"   kI1 =", &ki1,
"   kD1 =", &kd1
};

MENU_2 nMenu[CountMenu] = {
"   kP2 =", &kp2,
"   kI2 =", &ki2,
"   kD2 =", &kd2
};
 
//функция выполнения меню
void menu_setup(){
  
  if (State_Right == LOW && millis() - ms_button > 200) {   //следующий пункт меню по кругу
      ms_button =  millis();
      if (curMenu == CountMenu - 1) 
      curMenu = 0; 
      else  curMenu++; 
  }
   
  if (State_Up == LOW && millis() - ms_button > 200) {
        ms_button =  millis();
        (*mMenu[curMenu].k1)+=0.5;
        (*nMenu[curMenu].k2)+=0.5;
  }

  if (State_Down == LOW && millis() - ms_button > 200) {
      ms_button =  millis();
      (*mMenu[curMenu].k1)-=0.5;
      (*nMenu[curMenu].k2)-=0.5;
  }
  
  String str1 = floatToString(*mMenu[curMenu].k1); // conversion call
  String str2 = floatToString(*nMenu[curMenu].k2); // conversion call
  
  if ( millis() - ms_button > 1000) {
  ms_button =  millis();
     Serial.print(mMenu[curMenu].name1);
     Serial.println(str1);
     Serial.print(nMenu[curMenu].name2);
     Serial.println(str2);
     Serial.flush();
  }
  
}


void setup() {
  Serial.begin(9600); 
  pinMode (BUTTON_UP, INPUT);     digitalWrite(BUTTON_UP, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_DOWN, INPUT);   digitalWrite(BUTTON_DOWN, HIGH); //подключаем подтягивающий резистор
  pinMode (BUTTON_RIGHT, INPUT); digitalWrite(BUTTON_RIGHT, HIGH); //подключаем подтягивающий резистор  
}


void loop() {
  
  //Считываем состояние кнопок управления
  State_Up = digitalRead(BUTTON_UP);
  State_Down = digitalRead(BUTTON_DOWN);
  State_Right = digitalRead(BUTTON_RIGHT);
  //-------------------------------------/
  
    menu_setup(); //вывод меню  
}

 

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

А, ну понятно, в строках 79-80 замените 16 на 15 - у Вас терминальный ноль не помещается.

А лучше заменять не на 15, а на sizeof(charbuf1)-1 и sizeof(charbuf2)-1

Я бы на Вашем месте остался бы на dtostrf. Возьмите код из поста #36 и поправьте, что я сказал, там делов-то.

moozon
Offline
Зарегистрирован: 04.04.2018

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

Отжирает лишние 900 байтов памяти программы. Пруф. ниже

/*

 * Пример использования SerialPrintf
 */
void setup() {
	Serial.begin(9600);
	printf("Hello, world!\n");
}

void loop() {} 

/////////////////////////////////////////////////////////
//	Результат компиляции в IDE 1.6.12
// Скетч использует 1 508 байт (4%) памяти устройства. 
/////////////////////////////////////////////////////////

Дак Вы вызывали printf, а не свою SerialPrintf функцию. При вызове SerialPrintf, скетч использует аж 3 700 байт;)

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

Сколько кто отжирает приведено в том посте, так что ля-ля не надо. А "свою", "не свою" - это Вы мимо. Они все мои. И использование scanf - тоже (я и его пользую).

Данный топик не для вбросов, а для решения задачи. Есть хорошее предложение? Выкладываейте, будет одним больше. Нет - не вбрасывайте, срача всё равно не будет. С моим участием, по крайней мере, точно не будет.

moozon
Offline
Зарегистрирован: 04.04.2018

Какой вброс? Вы о чем? Я лишь указал на ошибку в ваших тестах. Нужно уметь признавать свои ошибки.

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

Да, действительно, только сейчас заметил. Извините.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Здравствуйте, ТС и форумчане. Имею скетч, пока занимает 774 (37%) оперативы и 16 794 байт (52%) флэш. Но скетч постоянно дополняется и улучшается и предстоит еще немало доработать.  Поэтому если не составит труда подскажите как элементы структуры ниже с помощью PROGMЕM перенести во флэш чтобы потом не возникало желания перейти на другую плату помощнее, uno вполне устраивает

//массив элементов меню
struct PODMENU_1{ //
char name1[6];
  double *k1;
};

struct PODMENU_2{ //
char name2[6];
  double *k2;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
PODMENU_1 mMenu[CountMenu] = {
"KP1 =", &kp1,
"KI1 =", &ki1,
"KD1 =", &kd1,
"PW1 =", &pwr_BOTTOM
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
PODMENU_2 nMenu[CountMenu] = {
"KP2 =", &kp2,
"KI2 =", &ki2,
"KD2 =", &kd2,
"PW2 =", &pwr_TOP
};

void setup() {}
void loop() {}

//функция выполнения меню в loop
void selectParam(void){

  // добавить сохранение текущих параметров в eeprom  void SaveOption()
  
  //вывод использую буфер
  static char charbuf1[8];
  static char charbuf2[8]; 
  
  dtostrf(*mMenu[curMenu].k1, sizeof(charbuf1)-1, 2, charbuf1);
  dtostrf(*nMenu[curMenu].k2, sizeof(charbuf2)-1, 2, charbuf2);
  
  lcd.setCursor(5,0);  lcd.print(mMenu[curMenu].name1); lcd.print(charbuf1);    
  lcd.setCursor(5,2);  lcd.print(nMenu[curMenu].name2); lcd.print(charbuf2);
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018
const char  str[] PROGMEM = "STOP";
const char str1[] PROGMEM = "PEAK";
const char str_[] PROGMEM = "    ";

if (number) lcd.print(i ? (const __FlashStringHelper*)str : (const __FlashStringHelper*)str_); 
     else lcd.print(i ? (const __FlashStringHelper*)str1 : (const __FlashStringHelper*)str_)

Делал со строками в коде таким образом, но здесь такое не прокатит

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

BuonanotteMash,

если Вы не заметили, это тема о форматированном выводе в поток, и Ваш вопрос здесь - оффтопик и флуд. Потрудитесь, пожалуйста, найти подходящую тему или создайте свою.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

ЕвгенийП, извините, сразу не подумал. Последую вашему совету