Текстовая часть превышает доступное место на плате

esp32 error

Когда я компилирую этот код, он выдает упомянутую выше ошибку. Я видел в Интернете способ решить эту проблему: Serial.println(F("..."));

Для фиксированного строкового литерала я изменил его, но все равно не работал. Я компилирую это для платы ESP32.

#include "RMaker.h"
#include "WiFi.h"
#include "WiFiProv.h"
#include <DHT.h>
#include <SimpleTimer.h>

const char *service_name = "PROV_SmartHome";
const char *pop = "1234";

// определяем идентификатор чипа
uint32_t espChipId = 5;

// определяем имя узла
char nodeName[] = "ESP32_Smarthome";

// определяем имена устройств
char deviceName_1[] = "Switch1";
char deviceName_2[] = "Switch2";
char deviceName_3[] = "Switch3";
char deviceName_4[] = "Switch4";

// определяем GPIO, связанный с реле и переключателями
static uint8_t RelayPin1 = 23;  //D23
static uint8_t RelayPin2 = 22;  //D22
static uint8_t RelayPin3 = 21;  //D21
static uint8_t RelayPin4 = 19;  //D19

static uint8_t SwitchPin1 = 13;  //D13
static uint8_t SwitchPin2 = 12;  //D12
static uint8_t SwitchPin3 = 14;  //D14
static uint8_t SwitchPin4 = 27;  //D27

static uint8_t wifiLed      = 2;   //D2
static uint8_t gpio_reset   = 0;
static uint8_t DHTPIN       = 18; // Контакт D18 подключен к DHT
static uint8_t LDR_PIN      = 39; // вывод VN, связанный с LDR

/* Variable for reading pin status*/
// Состояние реле
bool toggleState_1 = LOW; // Определяем целое число, чтобы запомнить состояние переключения для реле 1
bool toggleState_2 = LOW; // Определяем целое число, чтобы запомнить состояние переключения для реле 2
bool toggleState_3 = LOW; // Определяем целое число, чтобы запомнить состояние переключения для реле 3
bool toggleState_4 = LOW; // Определяем целое число, чтобы запомнить состояние переключения для реле 4

// Состояние переключения
bool SwitchState_1 = LOW;
bool SwitchState_2 = LOW;
bool SwitchState_3 = LOW;
bool SwitchState_4 = LOW;

float temperature1 = 0;
float humidity1   = 0;
float ldrVal  = 0;

DHT dht(DHTPIN, DHT11);  //Для DHT 11
//DHT dht(DHTPIN, DHT22); //Для DHT 22

SimpleTimer Timer;

// Фреймворк предоставляет некоторые стандартные типы устройств, такие как выключатель, лампочка, вентилятор, датчик температуры.
static Switch my_switch1(deviceName_1, &RelayPin1);
static Switch my_switch2(deviceName_2, &RelayPin2);
static Switch my_switch3(deviceName_3, &RelayPin3);
static Switch my_switch4(deviceName_4, &RelayPin4);
static TemperatureSensor temperature("Temperature");
static TemperatureSensor humidity("Humidity");
static TemperatureSensor ldr("LDR");

void sysProvEvent(arduino_event_t *sys_event)
{
  switch (sys_event->event_id) {
    case ARDUINO_EVENT_PROV_START:
#if CONFIG_IDF_TARGET_ESP32
      Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop);
      printQR(service_name, pop, "ble");
#else
      Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop);
      printQR(service_name, pop, "softap");
#endif
      break;
    case ARDUINO_EVENT_WIFI_STA_CONNECTED:
      Serial.printf("\nConnected to Wi-Fi!\n");
      digitalWrite(wifiLed, true);
      break;
  }
}

void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx)
{
  const char *device_name = device->getDeviceName();
  const char *param_name = param->getParamName();

  if (strcmp(device_name, deviceName_1) == 0) {

    Serial.printf("Lightbulb = %s\n", val.val.b ? "true" : "false");

    if (strcmp(param_name, "Power") == 0) {
      Serial.printf("Received value = %s for %s - %s\n", val.val.b ? "true" : "false", device_name, param_name);
      toggleState_1 = val.val.b;
      (toggleState_1 == false) ? digitalWrite(RelayPin1, HIGH) : digitalWrite(RelayPin1, LOW);
      param->updateAndReport(val);
    }

  } else if (strcmp(device_name, deviceName_2) == 0) {

    Serial.printf("Switch value = %s\n", val.val.b ? "true" : "false");

    if (strcmp(param_name, "Power") == 0) {
      Serial.printf("Received value = %s for %s - %s\n", val.val.b ? "true" : "false", device_name, param_name);
      toggleState_2 = val.val.b;
      (toggleState_2 == false) ? digitalWrite(RelayPin2, HIGH) : digitalWrite(RelayPin2, LOW);
      param->updateAndReport(val);
    }

  } else if (strcmp(device_name, deviceName_3) == 0) {

    Serial.printf("Switch value = %s\n", val.val.b ? "true" : "false");

    if (strcmp(param_name, "Power") == 0) {
      Serial.printf("Received value = %s for %s - %s\n", val.val.b ? "true" : "false", device_name, param_name);
      toggleState_3 = val.val.b;
      (toggleState_3 == false) ? digitalWrite(RelayPin3, HIGH) : digitalWrite(RelayPin3, LOW);
      param->updateAndReport(val);
    }

  } else if (strcmp(device_name, deviceName_4) == 0) {

    Serial.printf("Switch value = %s\n", val.val.b ? "true" : "false");

    if (strcmp(param_name, "Power") == 0) {
      Serial.printf("Received value = %s for %s - %s\n", val.val.b ? "true" : "false", device_name, param_name);
      toggleState_4 = val.val.b;
      (toggleState_4 == false) ? digitalWrite(RelayPin4, HIGH) : digitalWrite(RelayPin4, LOW);
      param->updateAndReport(val);
    }

  }
}

void readSensor() {

  ldrVal = map(analogRead(LDR_PIN), 400, 4200, 0, 100);
  Serial.print("LDR - "); Serial.println(ldrVal);
  float h = dht.readHumidity();
  float t = dht.readTemperature(); // или dht.readTemperature(true) для Фаренгейта

  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  else {
    humidity1 = h;
    temperature1 = t;
    Serial.print("Temperature - "); Serial.println(t);
    Serial.print("Humidity - "); Serial.println(h);
  }
}

void sendSensor()
{
  readSensor();
  temperature.updateAndReportParam("Temperature", temperature1);
  humidity.updateAndReportParam("Temperature", humidity1);
  ldr.updateAndReportParam("Temperature", ldrVal);
}

void manual_control()
{
  if (digitalRead(SwitchPin1) == LOW && SwitchState_1 == LOW) {
    digitalWrite(RelayPin1, LOW);
    toggleState_1 = 1;
    SwitchState_1 = HIGH;
    my_switch1.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_1);
    Serial.println("Switch-1 on");
  }
  if (digitalRead(SwitchPin1) == HIGH && SwitchState_1 == HIGH) {
    digitalWrite(RelayPin1, HIGH);
    toggleState_1 = 0;
    SwitchState_1 = LOW;
    my_switch1.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_1);
    Serial.println("Switch-1 off");
  }
  if (digitalRead(SwitchPin2) == LOW && SwitchState_2 == LOW) {
    digitalWrite(RelayPin2, LOW);
    toggleState_2 = 1;
    SwitchState_2 = HIGH;
    my_switch2.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_2);
    Serial.println("Switch-2 on");
  }
  if (digitalRead(SwitchPin2) == HIGH && SwitchState_2 == HIGH) {
    digitalWrite(RelayPin2, HIGH);
    toggleState_2 = 0;
    SwitchState_2 = LOW;
    my_switch2.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_2);
    Serial.println("Switch-2 off");
  }
  if (digitalRead(SwitchPin3) == LOW && SwitchState_3 == LOW) {
    digitalWrite(RelayPin3, LOW);
    toggleState_3 = 1;
    SwitchState_3 = HIGH;
    my_switch3.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_3);
    Serial.println("Switch-3 on");
  }
  if (digitalRead(SwitchPin3) == HIGH && SwitchState_3 == HIGH) {
    digitalWrite(RelayPin3, HIGH);
    toggleState_3 = 0;
    SwitchState_3 = LOW;
    my_switch3.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_3);
    Serial.println("Switch-3 off");
  }
  if (digitalRead(SwitchPin4) == LOW && SwitchState_4 == LOW) {
    digitalWrite(RelayPin4, LOW);
    toggleState_4 = 1;
    SwitchState_4 = HIGH;
    my_switch4.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_4);
    Serial.println("Switch-4 on");
  }
  if (digitalRead(SwitchPin4) == HIGH && SwitchState_4 == HIGH) {
    digitalWrite(RelayPin4, HIGH);
    toggleState_4 = 0;
    SwitchState_4 = LOW;
    my_switch4.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, toggleState_4);
    Serial.println("Switch-4 off");
  }
}


void setup()
{

  Serial.begin(115200);

  // Установите реле GPIO в качестве режима вывода
  pinMode(RelayPin1, OUTPUT);
  pinMode(RelayPin2, OUTPUT);
  pinMode(RelayPin3, OUTPUT);
  pinMode(RelayPin4, OUTPUT);
  pinMode(wifiLed, OUTPUT);

  // Настраиваем входные GPIO
  pinMode(SwitchPin1, INPUT_PULLUP);
  pinMode(SwitchPin2, INPUT_PULLUP);
  pinMode(SwitchPin3, INPUT_PULLUP);
  pinMode(SwitchPin4, INPUT_PULLUP);
  pinMode(gpio_reset, INPUT);

  // Записываем в GPIO состояние по умолчанию при загрузке
  digitalWrite(RelayPin1, !toggleState_1);
  digitalWrite(RelayPin2, !toggleState_2);
  digitalWrite(RelayPin3, !toggleState_3);
  digitalWrite(RelayPin4, !toggleState_4);
  digitalWrite(wifiLed, LOW);
  dht.begin();    // Включение датчика DHT

  Node my_node;
  my_node = RMaker.initNode(nodeName);

  //Стандартное переключающее устройство
  my_switch1.addCb(write_callback);
  my_switch2.addCb(write_callback);
  my_switch3.addCb(write_callback);
  my_switch4.addCb(write_callback);

  //Добавляем коммутатор к узлу
  my_node.addDevice(my_switch1);
  my_node.addDevice(my_switch2);
  my_node.addDevice(my_switch3);
  my_node.addDevice(my_switch4);
  my_node.addDevice(temperature);
  my_node.addDevice(humidity);
  my_node.addDevice(ldr);

  Timer.setInterval(2000);

  // Это необязательно
  RMaker.enableOTA(OTA_USING_PARAMS);
  //Если вы хотите включить планирование, установите часовой пояс для вашего региона с помощью setTimeZone().
  //Список доступных значений приведен здесь https://rainmaker.espressif.com/docs/time-service.html
  // RMaker.setTimeZone("Азия/Шанхай");
  // В качестве альтернативы включите службу часового пояса и позвольте приложениям телефона установить соответствующий часовой пояс
  RMaker.enableTZService();
  RMaker.enableSchedule();

  //Наименование услуги
  for (int i = 0; i < 17; i = i + 8) {
    espChipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
  }

  Serial.printf("\nChip ID:  %d Service Name: %s\n", espChipId, service_name);

  Serial.printf("\nStarting ESP-RainMaker\n");
  RMaker.start();

  WiFi.onEvent(sysProvEvent);
#if CONFIG_IDF_TARGET_ESP32
  WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name);
#else
  WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name);
#endif

  my_switch1.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, false);
  my_switch2.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, false);
  my_switch3.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, false);
  my_switch4.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, false);
}

void loop()
{
  // Чтение GPIO0 (внешняя кнопка для перезагрузки устройства
  if (digitalRead(gpio_reset) == LOW) { //Кнопка нажата
    Serial.printf("Reset Button Pressed!\n");
    // Обработка устранения дребезга клавиш
    delay(100);
    int startTime = millis();
    while (digitalRead(gpio_reset) == LOW) delay(50);
    int endTime = millis();

    if ((endTime - startTime) > 10000) {
      // Если клавиша нажата более 10 секунд, сбросить все
      Serial.printf("Reset to factory.\n");
      RMakerFactoryReset(2);
    } else if ((endTime - startTime) > 3000) {
      Serial.printf("Reset Wi-Fi.\n");
      // Если клавиша нажата более 3 секунд, но меньше 10, сброс Wi-Fi
      RMakerWiFiReset(2);
    }
  }
  delay(100);

  if (WiFi.status() != WL_CONNECTED)
  {
    //Serial.println("Wi-Fi не подключен");
    digitalWrite(wifiLed, false);
  }
  else
  {
    //Serial.println("Wi-Fi подключен");
    digitalWrite(wifiLed, true);
    if (Timer.isReady()) {
      //Serial.println("Отправка данных датчика");
      sendSensor();
      Timer.reset();      // Сброс второго таймера
    }
  }
  manual_control();
}

Информация о плате ESP32 здесь:

, 👍0

Обсуждение

Вы превышаете пространство для программы/флэш-память или оперативную память? Насколько? Вы проверили, сколько места занимают библиотеки?, @chrisl

@chrisl Я не знаю, как это проверить. Я посмотрю на это., @Just doin Gods work

Когда вы компилируете скетч в Arduino IDE, он выводит объем используемой программной памяти и объем оперативной памяти, используемый глобальными переменными. Это дает вам хороший намек., @chrisl

Где весь этот текст, который вы упомянули? Этот крошечный код должен легко поместиться во флэш-память ESP (если вы используете F(...))... какой размер флэш-памяти у вашего ESP? Возможно, вы можете обойти это, используя другую схему разделов., @Sim Son

Насколько велика скомпилированная программа? Не могли бы вы включить в свой вопрос последние строки вывода компилятора? Совет от @SimSon тоже хороший. Если вам не нужна файловая система или OTA, вы можете выбрать схему разделов, которая максимизирует доступное пространство для кода, например «Huge APP» или одну из схем «No OTA»., @StarCat

Набор инструментов esp32 автоматически использует строковые литералы из flash. макрос F ничего не делает, @Juraj


3 ответа


Лучший ответ:

1

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

Сохранение текущих настроек, за исключением переключения параметра раздела на "Огромное приложение (3 МБ без OTA/1 МБ SPIFFS)", привело к следующему:

Sketch uses 1790473 bytes (56%) of program storage space. Maximum is 3145728 bytes.
Global variables use 53260 bytes (16%) of dynamic memory, leaving 274420 bytes for local variables. Maximum is 327680 bytes.

Я даже не уверен, что "Huge APP" имеет самый смысл. Только эти 3 МБ больше, чем 1,2 МБ, и больше, чем ваша текущая сборка 1,8 МБ, по-видимому, необходима. Там написано "нет OTA". но вы, похоже, и этим не пользуетесь. Таким образом, кажется, что вы можете сделать это независимо от того, какие дополнения вы можете вырезать из текстового сегмента, настроив свой код.

,

Я сделал раздел для Rainmaker, теперь он работает нормально, @Just doin Gods work


1

Вы программируете встроенный процессор с ограниченным объемом памяти. И программное пространство, и оперативная память (ОЗУ). Каждая буква, заключенная в кавычки, занимает байт памяти. Если вы превысите лимит лишь немного, рассмотрите возможность сокращения текста. Например, рассмотрите возможность изменения таких строк, как:

"Инициализация начата с именем"

... к чему-то вроде ...

"Имя доверенного лица"

Если сильно, рассмотрите возможность изменения кода. Вы много раз цитируете похожий текст. Например, рассмотрите возможность изменения следующего кода:

// определяем имена устройств
char deviceName_1[] = "Switch1";
char deviceName_2[] = "Switch2";
char deviceName_3[] = "Switch3";
char deviceName_4[] = "Switch4";
...
// Фреймворк предоставляет некоторые стандартные типы устройств, такие как выключатель, лампочка, вентилятор, датчик температуры.
static Switch my_switch1(deviceName_1, &RelayPin1);
static Switch my_switch2(deviceName_2, &RelayPin2);
static Switch my_switch3(deviceName_3, &RelayPin3);
static Switch my_switch4(deviceName_4, &RelayPin4);

... к чему-то вроде ...

//Фреймворк предоставляет некоторые стандартные типы устройств, такие как выключатель, лампочка, вентилятор, датчик температуры.
String stringOne = "Switch";
String stringTwo = "1"
static Switch my_switch1(stringOne + stringTwo, &RelayPin1);
stringTwo = "2"
static Switch my_switch2(stringOne + stringTwo, &RelayPin2);
stringTwo = "3"
static Switch my_switch3(stringOne + stringTwo, &RelayPin3);
stringTwo = "4"
static Switch my_switch4(stringOne + stringTwo, &RelayPin4);

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

,

Если вы обнаружите проблему в приведенном выше коде или у вас есть представление о более эффективном подходе, отправьте сообщение. Я буду более чем счастлив отредактировать ответ, чтобы сделать его лучше., @st2000

Я буду реализовывать их с этого момента., @Just doin Gods work


1

Вы можете использовать перечисления для замены всех строк символов (которые занимают много места в памяти) на целые числа за сценой.

enum deviceNames {Switch1, Switch2, Switch3, Switch4};
deviceNames deviceName; // определяем переменную типа deviceNames

для сравнения вы можете использовать:

if (deviceName == Switch1); //Switch1 равен 0 для компилятора, Stich2 равен 1 и т. д. и т. д.

Однако сначала вам следует взглянуть на функции getDeviceName() и getParamName(). Не могли бы вы опубликовать их? Нужно будет посмотреть, сможете ли вы внести эти изменения и там. Поскольку вы получаете оттуда переменную device_name, опять же в виде строки. Перечисления бесполезны без дополнительных изменений в функциях.

*Также я заметил, что вы до сих пор не используете Serial.print(F()); Прочтите о сохранении строк и переменных в PROGMEM, если вам интересно.

*РЕДАКТИРОВАТЬ: Как заметил в комментариях пользователь Juraj: «Инструментарий esp32 автоматически использует строковые литералы из flash. макрос F ничего не делает».

Возможно, вы можете извлечь выгоду из форматирования newlib nano для printf? Я не уверен, так как не знаю, какая разница в вашей версии esp32. Вам придется проверить.

,

Обратите внимание, что F() предназначена для сохранения оперативной памяти (раздел данных), а не флэш-памяти (текстовый раздел)., @Edgar Bonet

Да, пока мы не знаем, какое из этих двух пространств памяти превышено., @Gaai

Набор инструментов esp32 автоматически использует строковые литералы из flash. макрос F ничего не делает, @Juraj

В заголовке вопроса говорится, какое из этих двух пространств памяти превышено., @Edgar Bonet

@Juraj Ага, этого я не знал. Спасибо, что заметили это., @Gaai