очередной раз PROGMEM

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

Хорошо объяснюсь по порядку. Изначально пишу на основе уже существующего кода. Код меня много в чем не устраивает особенно тем что построен на куче case для прокрутке меню и десятке использований millis для кнопок когда можно для этого использовать библиотеку. Один подпункт меню у меня будет аналогично сделанному на casах: каждое нажатие кнопки обновляет информацию на экране так что ДОПУСТИМ обновляется строка "STEPS" и соответственно ей переменная profileSteps, следующее нажатие "RAMP: " и double rampRateStep[9]; и т д

Перечислю их  

// заданы в виде массива
double rampRateStep[9];
int temperatureStep[9];
int dwellTimerStep[9];
//
int pwr_TOP, pwr_BOTTOM;
int profileSteps;
int Setpoint2;
double kp1, kd1, ki1; // возможно int
double kp2, kd2, ki2; // возможно int

Переменные имеют разный тип поэтому удобно их обработать через структуру

Тестовый код

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

char buf[8];

struct station {
 double rampRateStep[9];
 int temperatureStep[9];
 int dwellTimerStep[9];
 int pwr_TOP, pwr_BOTTOM;
 int profileSteps;
 int Setpoint2; 
 double kp1, kd1, ki1; // возможно int
 double kp2, kd2, ki2; // возможно int
};


const char string_0[] PROGMEM = "STEPS ";
const char string_1[] PROGMEM = "B.HEAT";
const char string_2[] PROGMEM = "T.HEAT";
const char string_3[] PROGMEM = "B.PWR ";

const char string_4[] PROGMEM = "T.PWR  ";
const char string_5[] PROGMEM = "RAMP:  ";
const char string_6[] PROGMEM = "TARGET ";
const char string_7[] PROGMEM = "DWELL ";
const char *const text[] PROGMEM = { string_0, string_1, string_2, string_3, string_4, string_5, string_6, string_7 };

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


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
}  // void loop()

void function (const uint8_t n) { 
        station reflow; 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);
        //Serial.print(reflow.Setpoint2);  	
}

Моя идея решения простая: создается функция и в нужном месте вызывается и достаточно. Но решена проблема только со строками. Переменные имеют разный тип и некоторые заданы в виде массива, я это показал. А значит и возника идея в нумерации элементов структуры чтобы затем этот номер и передавать функции чтобы затем по номеру вернуть искомую переменную и ее вывести. Плодить if не хочу

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

Я правда не понимаю, что Вам нужно. Все нормальные люди в такой ситуации пишут пачку ифов. Если Вы хотите работать как с массивом, Вам нужно менть организацию данных. Если Вы хотите спрятать печать и писать её в одном месте, Вам надо что структура сама себя печатала через интерфейст Printable. Чего Вам надо то?

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Прежде всего поймите что строки в меню разные по типу https://habr.com/post/257607/

Цитата:

Главным догматом менюОС является «Все есть файл». Да будет так.

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

Вот отсюда и надо прыгать

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

Евгений нашел ваши Этюды для начинающих: интерфейс Printable

Буду разбираться http://arduino.ru/forum/programmirovanie/etyudy-dlya-nachinayushchikh-interfeis-printable

ЕвгенийП, в этой ветке упоминалось что возможно добавить такую функциональность в класс LCD. Подскажите каким образом, на основе вашего же кода? Заранее спасибо

struct SSensorValue : public Printable {
	unsigned long timeStamp;  //	Время съёма показаний (мс от запуска МК)
	float temperture;         //	температура в градусах С
	float humidity;           //	влажность в %
	float pressure;           //	давление в мм.рт.ст.

	size_t printTo(Print& p) const { // в качестве p нам передадут, например, Serial
		size_t res = p.print("SENSOR VALUE: Time:");
		res += p.print(timeStamp);
		res += p.print("ms; T:");
		res += p.print(temperture);
		res += p.print("C; H:");
		res += p.print(humidity);
		res += p.print("%; P:");
		res += p.print(pressure);
		return res + p.println("mm");
	}
};

void setup() {
	Serial.begin(115200);
	Serial.println("Fun begins!");
	//
	//	Объявляем структуру и заполняем какими-то значениями
	//
	SSensorValue sv;
	sv.timeStamp = millis();
	sv.temperture = 36.6;
	sv.humidity = 93.2;
	sv.pressure = 755;
	//
	//	Печатаем структуру
	//
	Serial.print(sv);
}

void loop() {}

 

Да это возможно. Дальше уже через if буду выводить как мне нужно. Нужно было раньше найти ваши этюды, помогло

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

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

LiquidCrystal_I2C lcd(0x27, 16, 2); // присваиваем имя lcd для дисплея 20х4

char buf[6];

struct SSensorValue : public Printable {
        float rampRateStep[9];
        int temperatureStep[9];
        int dwellTimerStep[9];
	int profileSteps;
        int pwr_BOTTOM;
        int pwr_TOP;
	int Setpoint2;        
	float kp1;
        float kd1; 
        float ki1; // возможно int
        float kp2; 
        float kd2; 
        float ki2; // возможно int;           

	size_t printTo(Print& p) const { 
		size_t res = p.print("Steps:");
		//res += p.println(timeStamp);
		//res += p.print("ms; T:");
		//res += p.print(temperture);
		//res += p.print("C; H:");
		res += p.print(profileSteps);
		//res += p.print("%; P:");
		//res += p.print(pressure);
		return res;
	}
};

/*enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,
  REFLOW_STATE_MENU_RESET};*/


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () {
  Serial.begin(9600); 
  lcd.begin();
  lcd.backlight(); // включение подсветки дисплея
  
  SSensorValue sv;
  sv.Setpoint2 = 100;
  sv.profileSteps = 9;
  sv.kp1 = 36.6;
  sv.kd1 = 93.2;
  sv.ki1 = 55.1;
  
  Serial.print(sv);
  lcd.setCursor(0, 0);
  lcd.print(sv);    	
}  // void setup()


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
}  // void loop()

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);
        
}

 

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

Возник неприятная проблема: если метод вызывается в функции setup() все работает. Как только переношу его в loop() отказывается что либо выводить. Направьте

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

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

LiquidCrystal_I2C lcd(0x27, 16, 2); // присваиваем имя lcd для дисплея 20х4

char buf[6];

struct reflowStation : public Printable {
        float rampRateStep[9];
        int temperatureStep[9];
        int dwellTimerStep[9];
	int profileSteps;
        int pwr_BOTTOM;
        int pwr_TOP;
	int Setpoint2;        
	float kp1;
        float kd1; 
        float ki1; // возможно int
        float kp2; 
        float kd2; 
        float ki2; // возможно int;           

	size_t printTo(Print& p) const { 
		size_t res = p.print("Steps:");
		//res += p.println(timeStamp);
		//res += p.print("ms; T:");
		//res += p.print(temperture);
		//res += p.print("C; H:");
		res += p.print(profileSteps);
		//res += p.print("%; P:");
		//res += p.print(pressure);
		return res;
	}
};

/*enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,
  REFLOW_STATE_MENU_RESET};*/


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () {
  Serial.begin(9600); 
  lcd.begin();
  lcd.backlight(); // включение подсветки дисплея   
  // прекрасно работает
  reflowStation pr;
  pr.Setpoint2 = 100;
  pr.profileSteps = 9;
  pr.kp1 = 36.6;
  pr.kd1 = 93.2;
  pr.ki1 = 55.1;
  
  Serial.print(pr);
  lcd.setCursor(0, 0);
  lcd.print(pr); 	
}  // void setup()


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
    
}  // void loop()

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);  
        // не работает
        /*reflowStation pr;
        pr.Setpoint2 = 100;
        pr.profileSteps = 9;
        pr.kp1 = 36.6;
        pr.kd1 = 93.2;
        pr.ki1 = 55.1;
        
        Serial.print(pr);
        lcd.setCursor(0, 0);
        lcd.print(pr);   */   
}

Ошибок нет, но и монитор пуст

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);  
        // не работает
        /*reflowStation pr;
        pr.Setpoint2 = 100;
        pr.profileSteps = 9;
        pr.kp1 = 36.6;
        pr.kd1 = 93.2;
        pr.ki1 = 55.1;
        
        Serial.print(pr);
        lcd.setCursor(0, 0);
        lcd.print(pr);   */   
}

PS Евгений чтобы у вас потом вопросов не возникало добавлю. К вашему коду претензий не имею но вы в примере: 

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

Давайте это напишем:

struct SSensorValue : public Printable {
	unsigned long timeStamp;  //	Время съёма показаний (мс от запуска МК)
	float temperture;         //	температура в градусах С
	float humidity;           //	влажность в %
	float pressure;           //	давление в мм.рт.ст.

	size_t printTo(Print& p) const { // в качестве p нам передадут, например, Serial
		size_t res = p.print("SENSOR VALUE: Time:");
		res += p.print(timeStamp);
		res += p.print("ms; T:");
		res += p.print(temperture);
		res += p.print("C; H:");
		res += p.print(humidity);
		res += p.print("%; P:");
		res += p.print(pressure);
		return res + p.println("mm");
	}
};

void setup() {
	Serial.begin(115200);
	Serial.println("Fun begins!");
	//
	//	Объявляем структуру и заполняем какими-то значениями
	//
	SSensorValue sv;
	sv.timeStamp = millis();
	sv.temperture = 36.6;
	sv.humidity = 93.2;
	sv.pressure = 755;
	//
	//	Печатаем структуру
	//
	Serial.print(sv);
}

void loop() {}

Вот так! Обратите внимание: в строке 1 мы указали, что наша структура поддерживает интерфейс Printable, а в строках 7-17 мы реализовали необходимый для этого интерфейса метод printTo. Теперь, мы можем просто писать Serial.print(sv) (строка 34) и система поймёт, чтов этот момент надо просто вызвать наш метод, а он всё. что нужно, напечатает. Причём напечатает не тупо в Serial, а именно в тот поток, для которого он был вызван.

Если Вы запустите этот скетч, то он спокойно скомпилируется и в сериале Вы увидите:

Fun begins!
SENSOR VALUE: Time:0ms; T:36.60C; H:93.20%; P:755.00mm

 

Организовали метод SSensorValue, для вывода показаний с датчика и сделали вывод в setup(). Да но в моем случае меню логичнее сделать это в loop() поскольку показания постоянно обновляются (нужно часто настраивать). Если перенести это в функцию вызваемую из   loop() не работает, не пойму почему

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

Какая разница методу откуда его вызывают?

В Вышем коде я не вижу вызова этого метода (всё закоментировано, а то, что заскомментировано - то написано неправильно).

Вот у Вас же есть строки 69-76.

Вставьте такие же в loop (для начала в точности такие же) и больше ничего. Убедитесь, что работает. Потом попробуйте что Вам надо. Если не получится, то покажите код. Но именно неработающий код, а не кучу комментариев.

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

Рабочий 

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

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

LiquidCrystal_I2C lcd(0x27, 16, 2); // присваиваем имя lcd для дисплея 20х4

char buf[6];

struct reflowStation : public Printable {
        float rampRateStep[9];
        int temperatureStep[9];
        int dwellTimerStep[9];
	int profileSteps;
        int pwr_BOTTOM;
        int pwr_TOP;
	int Setpoint2;        
	float kp1;
        float kd1; 
        float ki1; // возможно int
        float kp2; 
        float kd2; 
        float ki2; // возможно int;           

	size_t printTo(Print& p) const { 
		size_t res = p.print("Steps:");
		//res += p.println(timeStamp);
		//res += p.print("ms; T:");
		//res += p.print(temperture);
		//res += p.print("C; H:");
		res += p.print(profileSteps);
		//res += p.print("%; P:");
		//res += p.print(pressure);
		return res;
	}
};

const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () {
  Serial.begin(9600); 
  lcd.begin();
  lcd.backlight(); // включение подсветки дисплея   
  
  reflowStation pr;
  pr.Setpoint2 = 100;
  pr.profileSteps = 9;
  pr.kp1 = 36.6;
  pr.kd1 = 93.2;
  pr.ki1 = 55.1;
  
  Serial.print(pr);
  lcd.setCursor(0, 0);
  lcd.print(pr); 	
}  // void setup()


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
    
}  // void loop()

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);  
}

Нерабочий

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

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

LiquidCrystal_I2C lcd(0x27, 16, 2); // присваиваем имя lcd для дисплея 20х4

char buf[6];

struct reflowStation : public Printable {
        float rampRateStep[9];
        int temperatureStep[9];
        int dwellTimerStep[9];
	int profileSteps;
        int pwr_BOTTOM;
        int pwr_TOP;
	int Setpoint2;        
	float kp1;
        float kd1; 
        float ki1; // возможно int
        float kp2; 
        float kd2; 
        float ki2; // возможно int;           

	size_t printTo(Print& p) const { 
		size_t res = p.print("Steps:");
		//res += p.println(timeStamp);
		//res += p.print("ms; T:");
		//res += p.print(temperture);
		//res += p.print("C; H:");
		res += p.print(profileSteps);
		//res += p.print("%; P:");
		//res += p.print(pressure);
		return res;
	}
};

const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () {
  Serial.begin(9600); 
  lcd.begin();
  lcd.backlight(); // включение подсветки дисплея   	
}  // void setup()


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
    
    reflowStation pr;
    pr.Setpoint2 = 100;
    pr.profileSteps = 9;
    pr.kp1 = 36.6;
    pr.kd1 = 93.2;
    pr.ki1 = 55.1;
    
    Serial.print(pr);
    lcd.setCursor(0, 0);
    lcd.print(pr);     
    
}  // void loop()

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);   
}

Их единственное отличие 

reflowStation pr;
  pr.Setpoint2 = 100;
  pr.profileSteps = 9;
  pr.kp1 = 36.6;
  pr.kd1 = 93.2;
  pr.ki1 = 55.1;
  
  Serial.print(pr);
  lcd.setCursor(0, 0);
  lcd.print(pr); 

В первом случае на экран выводится надпись "Steps:9", во втором экран пустой

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

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

Какая разница методу откуда его вызывают?

Согласен с вами, но не могу найти ошибку

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

  

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

BuonanotteMasha пишет:

В первом случае на экран выводится надпись "Steps:9", во втором экран пустой

вангую - причина в строке 56.

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

Кстати Всех с наступающим праздником :) Мирного неба над головой

Приехал домой, кое что убрал в нерабочем коде, заработал. Задумаюсь... может и правда - причина в строке 56 (проверка буфера)

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



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

#include <Wire.h> // библиотека для управления устройствами по I2C 
#include <LiquidCrystal_I2C.h> // подключаем библиотеку для LCD 2004
#include "Cl_do_btn_long.h" // подключаем библиотеку для опроса кнопок

LiquidCrystal_I2C lcd(0x27, 16, 2); // присваиваем имя lcd для дисплея 20х4

#define BTN_NEXT 11 // кнопка 

uint8_t i = 0;

char buf[6];

Cl_do_btn_long Btn_next(BTN_NEXT);

void press_next() { /*обработчик короткого*/
   if (i < 5) i++; //увеличивает счетчик кнопки на 1
   else i = 0;          //если счетчик достиг предела положений то его надо обнулить.
   
}
void longPress_next() { /*обработчик длинного*/
  
}

struct reflowStation : public Printable {
        float rampRateStep[9];
        int temperatureStep[9];
        int dwellTimerStep[9];
int profileSteps;
        int pwr_BOTTOM;
        int pwr_TOP;
int Setpoint2;        
float kp1;
        float kd1; 
        float ki1; // возможно int
        float kp2; 
        float kd2; 
        float ki2; // возможно int;           

size_t printTo(Print& p) const { 
size_t res;
                if (i == 0) res = p.print(profileSteps);
else if (i == 1) res = p.print(pwr_BOTTOM);
                else if (i == 2) res = p.print(pwr_TOP);
                else if (i == 3) res = p.print(kp1);
                else if (i == 4) res = p.print(kd1);
                else res = p.print(ki1);
                //res += p.print(kp2);
return res;
}
};

const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () { 
  lcd.begin();
  lcd.backlight(); // включение подсветки дисплея   
  
  DDRB = 0x00; // назначить пины кнопок на вход
  PORTB = 0b00111111; // подключить внутренние подтягивающие pull-up резисторы

}  // void setup()


void loop() {   

  Btn_next.run(&press_next, &longPress_next);
  
  reflowStation pr;
  pr.Setpoint2 = 100;
  pr.profileSteps = 9;
  pr.kp1 = 36.6;
  pr.kd1 = 93.2;
  pr.ki1 = 55.1;
  pr.kp2 = 14.3;
  pr.kd2 = 78.5;
  pr.ki2 = 99.4;
  pr.pwr_BOTTOM = 33.8;
  pr.pwr_TOP = 46.7;
  
  lcd.setCursor(0, 1);
  lcd.print(pr); 
    
  function(i);  
}  // void loop()

void function (const uint8_t n) { 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        lcd.setCursor(0, 0);
        lcd.print(buf);  
}

 

 

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

Ну, и слава Богу.

Кстати, поупражняйтесь. Два часа потратил! Правда, большую часть на писание текста :)

MaksVV
Offline
Зарегистрирован: 06.08.2015

напишу сюда. Взял стандартный пример прогмем. Все работает


#include <avr/pgmspace.h>

#define  BUF_LENGTH 50
char buf[BUF_LENGTH];  

const char stringMES_0[] PROGMEM = "NULL_C"; 
const char stringMES_1[] PROGMEM = "COMMAND";
const char stringMES_2[] PROGMEM = "REPORT";      
const char stringMES_3[] PROGMEM = "REQUEST";     
 
const char* const string_tableMES[] PROGMEM = 
{
   stringMES_0, 
   stringMES_1, 
   stringMES_2, 
   stringMES_3, 
};


void PRiNT(byte addr) 
{
   strcpy_P(buf, (char*)pgm_read_word(&(string_tableMES[addr])));
   Serial.println (buf);    
}

void setup() 
{
   Serial.begin (115200);
   PRiNT(1);
}

void loop() {}

но в моем скетче нужно будет часто изменять число строк и сами строки. Можно ли сделать попроще, чтобы каждый раз этот массив

const char* const string_tableMES[] PROGMEM = 
{
   stringMES_0, 
   stringMES_1, 
   stringMES_2, 
   stringMES_3, 
};

не заполнять вручную? 

Может как-то так: 

#include <avr/pgmspace.h>
#define BUF_LENGTH 50

const char stringMES[][BUF_LENGTH] PROGMEM = 
{
  "VATA", 
  "SHLYAPA",
  "KAKA"
};

const char* const string_tableMES[sizeof(stringMES)/BUF_LENGTH] PROGMEM  = 
{
  stringMES[0], 
  stringMES[1],
  stringMES[2]
};

char buf[BUF_LENGTH];

void PRiNT (int addr) 
  {
    strcpy_P(buf, (char*)pgm_read_word(&(string_tableMES[addr])));
    Serial.println (buf);  
  }

void setup() 
{
  Serial.begin (115200);
  PRiNT (0);
}

void loop() {}

только как бы заставить автоматически заполнять массив string_tableMES в зависимости от количества строк в массиве stringMES ?

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

у тебя в этом случае 



const char stringMES[][BUF_LENGTH] PROGMEM = { "VATA", "SHLYAPA", "KAKA" };

в PROGMEM попадают не сами строки, а указатели на них, а сами строки по-брежнему валяюца в ОЗУ. Думаю, это не то, что ты хотел.  

и от это 

string_tableMES[sizeof(stringMES)/BUF_LENGTH];

мня неуловимо смущает.  Мошт, я, правда не протрезвел еще. 

Если ты мне, как тупому, обьяснишь, что ты _на самом деле_ хочешь получить - мошт и получица помочь.

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

А от так, строки у тебя будут лежать в PROGMEM, а массив нет. Ну, для конкретно этого массива, думаю, 10 байт (число строк * размер указателя) в ОЗУ не жалко, но, если нада больше строк, то его тоже можно во флэш запхать. Только читать потом придёца специальным образом. 

const char str_1[] PROGMEM = "String 1";
const char str_2[] PROGMEM = "String 2";
const char str_3[] PROGMEM = "String 3";
const char str_4[] PROGMEM = "String 4";
const char str_5[] PROGMEM = "String 5";

const char *PMemStrings[] = {
	str_1,
	str_2,
	str_3,
	str_4,
	str_5
};

const uint8_t Strings_Count = sizeof(PMemStrings) / sizeof(PMemStrings[0]);

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

	Serial << "Count = " << Strings_Count << eoln;

	for (uint8_t i = 0; i < Strings_Count; i++) 
            Serial << PFlashString(PMemStrings[i]) << eoln;

}

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Если есть желание поиграться с PROGMEM, то вот вам задачка

/**/
void html( const char * title, ... ) {
  va_list ap;
  const char * message;
  va_start( ap, title );
  Serial.println(title);
  message = va_arg( ap, const char * );
  while (message) {
    Serial.println(message);
    message = va_arg( ap, const char *);
  }
  va_end( ap );
}
void setup() {
  Serial.begin(9600);
  html(
    "1", //PSTR("1")
    "2", //PSTR("2")
    0 //<-- конец
  );
}

void loop() {
}

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

DetSimen пишет:
А от так, строки у тебя будут лежать в PROGMEM, а массив нет. Ну, для конкретно этого массива, думаю, 10 байт (число строк * размер указателя) в ОЗУ не жалко, но, если нада больше строк, то его тоже можно во флэш запхать. Только читать потом придёца специальным образом.

да, строк будет много, поэтому с прогмем и хочу заморочиться. Тот пример, что ты написал, в принципе у меня уже работает,  (только и массив в прогмем). Но суть вопроса с твоим примером также остается - Помимо самих строк, нужно забивать вручную массив . 

MaksVV
Offline
Зарегистрирован: 06.08.2015

DetSimen пишет:
и от это 

string_tableMES[sizeof(stringMES)/BUF_LENGTH];

мня неуловимо смущает.  Мошт, я, правда не протрезвел еще. 

Если ты мне, как тупому, обьяснишь, что ты _на самом деле_ хочешь получить - мошт и получица помочь.

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

string_tableMES[sizeof(stringMES)/BUF_LENGTH];  а не так string_tableMES[]

sadman41
Offline
Зарегистрирован: 19.10.2016

MaksVV пишет:

все строки будут одинаковой длины, 

http://arduino.ru/forum/programmirovanie/progmem-tricks#comment-439006 -> №3 

MaksVV
Offline
Зарегистрирован: 06.08.2015

пасиб, посмотрю

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/**/
void html_P( const char * title, ... ) {
  va_list ap;
  const char * message;
  va_start( ap, title );
  Serial.println((const __FlashStringHelper *)title);
  message = va_arg( ap, const char * );
  while (message) {
    Serial.println((const __FlashStringHelper *)message);
    message = va_arg( ap, const char *);
  }
  va_end( ap );
}
void setup() {
  Serial.begin(9600);
  html_P(
    PSTR("<html>"),
    PSTR("<head>"),
    PSTR("<title>"),
    PSTR("С чего начинается страница"),
    PSTR("</title>"),
    PSTR("</head>"),
    PSTR("<body>"),
    PSTR("Каждая HTML-страница начинается с тега <html> "),
    PSTR("</body>"),
    PSTR("</html>"),
    0 //<-- конец
  );
}

void loop() {
}

Ответ на задачу #65

MaksVV
Offline
Зарегистрирован: 06.08.2015
MaksVV пишет:
но в моем скетче нужно будет часто изменять число строк и сами строки. Можно ли сделать попроще, чтобы каждый раз этот массив
const char* const string_tableMES[] PROGMEM = 
{
   stringMES_0, 
   stringMES_1, 
   stringMES_2, 
   stringMES_3, 
};

не заполнять вручную?

 
 
Уважаемые Гуру, если не затруднит, посмотрите правильно ли я запихал строки в прогмем по примеру № 3 Садмана (а также вывод их на печать). И ещё, строка прагма оптимизатора действительно нужна? что делает этот оптимизатор? 

#pragma GCC optimize ("O0")

#define BUF_SIZE 13

enum devices_enum
{
   Rosetka,
   Lampa,
   Shtora,
   Boiler,
  
   DEVICE_QUANTITY
};

const char PROGMEM devices[][BUF_SIZE]=
{
  "Rosetka",
  "Lampa",
  "Shtora",
  "Boiler"
};


void PrinT (byte addr)
{
  char buf[BUF_SIZE];
  if (addr<DEVICE_QUANTITY) {strcpy_P(buf, devices[addr]);}
  else {Serial.print (F("Неизвестное устройство  ")); return;}
  Serial.print(buf);
}

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

void loop() {}

 

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

прагму выкинь, она отключает оптимизацию

MaksVV
Offline
Зарегистрирован: 06.08.2015

ок, понятно зачем эта строка. 

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

ну и безопаснее будет вместо 

  char buf[BUF_SIZE];
  if (addr<DEVICE_QUANTITY) {strcpy_P(buf, devices[addr]);}

написать 

  char buf[BUF_SIZE+1];
  memset(buf, 0x00, BUF_SIZE+1);
  if (addr<DEVICE_QUANTITY) {strcpy_P(buf, devices[addr]);}

 

sunjob
sunjob аватар
Offline
Зарегистрирован: 18.07.2013

добрый день

по поводу поста #23

на старых версиях мандарины (младше 1.6.х) будет выдаваться ошибка

invalid conversion from 'uint16_t {aka unsigned int}' to 'const char*' [-fpermissive]

соотв. строки надо изменить на

1й пример
const char * addrStroki = (const char*) pgm_read_word_near((u16)(text + index));

2й пример
softPrint((u16)(text + 1));
void softPrint(const u16 addrOfAddr)
const char * addrStroki = (const char *) pgm_read_word_near(addrOfAddr);

3й пример
softPrint ( (const char*) (pgm_read_word_near ((u16)(text + 1))) );

 

b707
Offline
Зарегистрирован: 26.05.2017

И давно у нас char стал размера 16 бит, как нужно функции pgm_read_word?

Sunjob, помоему вы ерунду предложили

sunjob
sunjob аватар
Offline
Зарегистрирован: 18.07.2013

> помоему вы ерунду предложили

во первых, Вы пропустили либо пробел, либо тире, во вторых, да, я обычно так и делаю, предлагаю всякую ерунду :о)


pgmspace.h
#define pgm_read_word(addr) (*(const unsigned short *)(addr))

USBAPI.h
typedef unsigned short u16;

можно немного упростить (разыменовывать обычным обращением к элементу массива)

#include <avr/pgmspace.h>

const char eep_str0    [] PROGMEM = "EEP String 0"; // string to 'PROGMEM' :o)
const char eep_str1    [] PROGMEM = "EEP String 1";
const char eep_str2    [] PROGMEM = "EEP String 2";
const char eep_str3    [] PROGMEM = "EEP String 3";
const char eep_str4    [] PROGMEM = "EEP String 4";
const char eep_str5    [] PROGMEM = "EEP String 5";
const char eep_str6    [] PROGMEM = "EEP String 6";
const char eep_str7    [] PROGMEM = "EEP String 7";
const char eep_str8    [] PROGMEM = "EEP String 8";
const char eep_str9    [] PROGMEM = "EEP String 9";
const char ram_str     []         = "RAM String";

void*      mass_eep_str[] = { (void*) eep_str0,(void*) eep_str4, (void*) eep_str8 }; // mass to RAM

////////////////////////////////////////////////////////////////////////////////
void setup() 
////////////////////////////////////////////////////////////////////////////////
{ 
char buff[30]; 
Serial.begin(115200); 
Serial.println("### Test AVR PROGMEM string :");

strcpy_P(buff, (const char*) mass_eep_str[0]); Serial.println(buff); // w.o. clear buff ...
strcpy_P(buff, (const char*) mass_eep_str[1]); Serial.println(buff);
strcpy_P(buff, (const char*) mass_eep_str[2]); Serial.println(buff);
}
////////////////////////////////////////////////////////////////////////////////
void loop () 
////////////////////////////////////////////////////////////////////////////////
{
;
}
////////////////////////////////////////////////////////////////////////////////

-->

### Test AVR PROGMEM string :
EEP String 0
EEP String 4
EEP String 8

т.о. можно создавать "разношерстные" массивы/списки строк, находящиеся в EEPROM, и перебирать их обычным способом

удачи :о)

p.s. к стати, можно и по "другому упаковать"

const char* mass_eep_str[] = { (const char*) eep_str0,(const char*) eep_str4, (const char*) eep_str8 };

strcpy_P(buff, mass_eep_str[0]); Serial.println(buff);

ver. Arduino-1.5.8

 

p.s.

по поводу EEP_xxx: писалось быстро, на коленке, переделкой старого кода, поэтому все так "как есть"... :o)

могу ошибаться, правьте :о)

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

Можешь, и ашыбаешьса. 

sunjob
sunjob аватар
Offline
Зарегистрирован: 18.07.2013

отлично, банкуй, ушастый... рассказывай, примеры на коленке накоцаны, возможно, что-то ступил, вот вы сейчас все и поправите :о)

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

Нет. Не поправлю. Думай.

sunjob
sunjob аватар
Offline
Зарегистрирован: 18.07.2013

я сюда зашел не для "запроса вашей помочи"...

первая часть кода, это правка к опубликованному коду, для старых версий ардуино, который будет выдавать ошибку
вторая часть кода, наработка как использовать массив указателей в SRAM, что значительно быстрее и удобнее...
третья часть кода, немного измененный вариант

все три варианта проверены ... а с массивами - давно уже рабочие... и данные примеры писались, естественно, быстро, на коленке, по памяти

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

но иногда, лучше, молчать...

b707
Offline
Зарегистрирован: 26.05.2017

Ну да, типично...
"ребята, три дня искал как кнопку прочитать, в гугле ничего похожего нет... выкладываю код, может кому пригодицца..."

Jonotan75
Offline
Зарегистрирован: 20.03.2022

Все приветствую, прошу помочь разобраться.. Есть рабочая программа

const char Lang_ln0[] PROGMEM = "Select MODE";   
const char Lang_ln1[] PROGMEM = "Select RU";   
const char Lang_ln2[] PROGMEM = "Select EN";   
const char Lang_ln3[] PROGMEM = "Select СH";   
const char Lang_ln4[] PROGMEM = "CONFIRM MAIN";  

const char Lang_ru0[] PROGMEM = "ВЫБОР";
const char Lang_ru1[] PROGMEM = "НАСТРОЙКИ";
const char Lang_ru2[] PROGMEM = "РАБОТА";
const char Lang_ru3[] PROGMEM = "ВЫХОД";
const char Lang_ru4[] PROGMEM = "ПОДТВЕРЖДЕНИЕ";

const char Lang_en0[] PROGMEM = "SELECT";
const char Lang_en1[] PROGMEM = "SETUP";
const char Lang_en2[] PROGMEM = "WORKING IN SYS";
const char Lang_en3[] PROGMEM = "EXIT 01";
const char Lang_en4[] PROGMEM = "CONFIRM 01";

const char Lang_ch0[] PROGMEM = "MODE";
const char Lang_ch1[] PROGMEM = "SYSTEM";
const char Lang_ch2[] PROGMEM = "WORKING IN SYS";
const char Lang_ch3[] PROGMEM = "EXIT 02";
const char Lang_ch4[] PROGMEM = "CONFIRM 02";

const char* const Lang_Str[][5] PROGMEM = 
{
  {Lang_ln0, Lang_ln1, Lang_ln2, Lang_ln3, Lang_ln4},
  {Lang_ru0, Lang_ru1, Lang_ru2, Lang_ru3, Lang_ru4},
  {Lang_en0, Lang_en1, Lang_en2, Lang_en3, Lang_en4},
  {Lang_ch0, Lang_ch1, Lang_ch2, Lang_ch3, Lang_ch4},
};

void setup()
{
  lcd.Init_LCD(); //initialize lcd
}

void loop()
{
  for (int j = 0; j < 4; j++)
  {
    for (int i = 0; i < 5; i++)
    {
      char buffer [strlen_P(Lang_Str[j][i]) + 1];
      strcpy_P(buffer, (char*)pgm_read_word(&(Lang_Str[j][i]))); 
      lcd.Print_String(buffer, CENTER, 230); // вывод на экран  
      lcd.Fill_Screen(BLACK);
      delay( 500 );
    }
  }

Дальше создаю функцию Print_PROGMEM

void Print_PROGMEM(const char* const Lang_Str_1, int Pos_x, int Pos_y)
{
  char buffer [strlen_P(Lang_Str_1) + 1];
  strcpy_P(buffer, Lang_Str_1);
  lcd.Print_String(buffer, Pos_x, Pos_y);
}

вставляю в loop

  for (int j = 0; j < 4; j++)
  {
    for (int i = 0; i < 5; i++)
    {
      Print_PROGMEM(Lang_Str[j][i], 0, 130);
      lcd.Fill_Screen(BLACK);
      delay( 500 );
    }
  }

после этого программа выводит абракадабру, если задаю переменные константами j = 1 и i = 1 без цикла, все работает. Что не так, плиз ???

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
Массив же в PROGMEM !!!
for (int j = 0; j < 4; j++)
{
  for (int i = 0; i < 5; i++)
  {
    Print_PROGMEM((char*)pgm_read_word(&(Lang_Str[j][i])), 0, 130);
    lcd.Fill_Screen(BLACK);
    delay( 500 );
  }
}

При установке констант - оптимизатор выкидывает ВСЕ не используемые строки и массив ! Остаётся только одна строка и у неё есть конкретный адрес - вот он и передаётся ...

Jonotan75
Offline
Зарегистрирован: 20.03.2022

Спасибо, разобрался