• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Как я "переползал" с Ардуйни 2.4.2 на Ардуюню 3.0.2

Mоnk

Member
Долго "сидел" на 2.4.2, потому что бинарники, скомпилированные в следующих за ядром 2.4.2 итерациях, через WebUpdate во флеш китайских "умных" розеток не влезали. Не лезет 483к в ESP8285, который к тому же 1Мх128К. Пользуюсь до сих пор IotManager версии 1.5.5, причем без json. Ну нравится мне, и все тут.

Однако все течет, все изменяется. Решил "прикрутить" ТелеграммБот, и понеслось... Бот от Гайвера с BearSSL заработал начиная с 2.5.0. Мелочится не стал, накатил 3.0.2. Бинарник на простом градуснике с часами по рассчета ArduinoIDE получился размером 54%, что и следовало ожидать.

Первое, что перестало работать, ESP8266 sketch data upload. Google изрядно потупел за последние годы, поэтому поиск решения занял немалое время с установкой Питона и другими ненужными танцами. Случайно наткнуля на ссылку https://github.com/esp8266/arduino-esp8266fs-plugin/releases, снес все лишнее, закинул папку ESP8266FS пятой версии сюда => C:\Program Files (x86)\Arduino\tools, и загрузка SPIFFS по кабелю заработала.

Дальше. Почему-то перестали прилетать push уведомления от onesignal.com. Понятно почему (теперь). Потому что теперь после "WiFiClientSecure push_client;" надо добавить "push_client.setInsecure();".
C++:
// ================================= Push notifications ==================================

void push(String push_msg)
{
  if (WiFi.status() == WL_CONNECTED)              // если подключились
  {
    WiFiClientSecure push_client;
    [B]push_client.setInsecure();[/B]
    if (!push_client.connect("onesignal.com", 443)) return;

    String push_data = F("{\"app_id\": \"8871958c-5f52-11e5-8f7a-c36f5770ade9\",\"include_player_ids\":[\"") + ids + F("\"],\"android_group\":\"IoT Manager\",\"contents\": {\"en\": \"") + push_msg + "\"}}";

    push_client.println("POST /api/v1/notifications HTTP/1.1");
    push_client.print("Host:");
    push_client.println("onesignal.com");
    push_client.println("User-Agent: esp8266.Arduino.IoTmanager");
    push_client.print("Content-Length: ");
    push_client.println(push_data.length());
    push_client.println("Content-Type: application/json");
    push_client.println("Connection: close");
    push_client.println();
    push_client.println(push_data);
  }
}

// ================================= End Push notifications ==================================
Самое обидное - при обращении к BME280 еспэшка уходила в ребут.
Пройдемся еще раз по тупому Гуглю. Задаём вопрос "esp8266 bme280 без библиотеки", и получаем список ответов типа "...необходимо две библиотеки", "...необходимо установить две специальных библиотеки", "...библиотека под bme280". "Точное соответствие" - по барабану. Тупая американская скотина, одним словом.
Не то, что бы я не любил чужие библиотеки. Просто когда пишешь код сам, понимаешь, как это работает.
Ларчик открывался просто. Накосячил с типом переменной. А Ардуйня все туже затягивает гайки в плане строгого соответствия спецификации языка.

Кабель конечно хорошо, но хочется как и раньше менять прошивки в гараже, в деревне, дома наконец с работы. Что я и делал, пока недобитые фашисты не отключили TeamViewer. Эту проблему я конечно решу. (Решил. Раскопал в недрах своей фалопомойки R-Admin. Конечно не настолько удобно теперь, но работает.) А пока хотя бы WebUpdate в пределах квартиры.
На просторах интернета увидел интересный вопрос возмущенного пользователя, дословно "почему сжатый gzip bin-SPIFFS разворачивается по воздуху пустым"? Та-ак! Идущий да обрящет! Специалисты в один голос утверждают, "должен быть #define ATOMIC_FS_UPDATE", собственно поиски которого приводят на https://arduino-esp8266.readthedocs.io/en/3.0.2/ota_updates/readme.html#compression. Бинго? Фиг вам!
Надо сначала чтобы в устройстве была прошивка с этим ATOMIC_FS_UPDATE! А появилась эта фича только в 2.6.0.

Ну что-же, поехали...
Идея проста. Делаем чистую WebUpdate прошивку с ATOMIC_FS_UPDATE в 3.0.2, заливаем бинарник (смешные 301к) "по воздуху" в устройство, компилим нормальную прошивку для устройства в 3.0.2, сжимаем в gzip 9-м уровнем (максимально), заливаем архив "по воздуху" в устройство.
C++:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>

#define ATOMIC_FS_UPDATE          //---------- Возможность прошить сжатым бинарником

const char* ssid = "Роутер";
const char* password = "Пароль_ВиФи";
const char* ssidAP = "WebUpdate";
const char* passwordAP = "Пароль_ТочкиДоступа_есп";

ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
IPAddress ip(192,168,1,96); IPAddress gateway(192,168,1,1); IPAddress subnet(255,255,255,0); // ориентируясь на свой роутер

void setup(void)
{
  WiFi.mode(WIFI_AP_STA);
    WiFi.setAutoConnect(false);
    WiFi.setAutoReconnect(true);
    WiFi.config(ip, gateway, subnet, ip);
    WiFi.begin(ssid, password);
    int ii = 80;
    while (WiFi.status() != WL_CONNECTED && ii){delay(100); ii--;}
    if (WiFi.status() != WL_CONNECTED) WiFi.mode(WIFI_AP);  // если не подключились
    WiFi.softAP(ssidAP, passwordAP);

    server.on("/", HTTP_GET, [](){
      server.sendHeader("Connection", "close");
      server.sendHeader("Access-Control-Allow-Origin", "*");
      server.send(200, "text/html", serverIndex);
    });
    server.on("/update", HTTP_POST, [](){
      server.sendHeader("Connection", "close");
      server.sendHeader("Access-Control-Allow-Origin", "*");
      server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
    delay(1000);
      ESP.restart();
    },[](){
      HTTPUpload& upload = server.upload();
      if(upload.status == UPLOAD_FILE_START)
      {
        WiFiUDP::stopAll();
        uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
        if(!Update.begin(maxSketchSpace)) //start with max available size
        {Update.printError(Serial);}
      }
      else if(upload.status == UPLOAD_FILE_WRITE)
      {
        if(Update.write(upload.buf, upload.currentSize) != upload.currentSize)
        {Update.printError(Serial);}
      }
      else if(upload.status == UPLOAD_FILE_END)
      {
        if(Update.end(true)) //true to set the size to the current progress
        {  }
        else
        {Update.printError(Serial);}
      }
      yield();
    });
    server.begin();
}
 
void loop(void)
{
  server.handleClient();
  delay(1);
}
Ура? Не-а! "Толстый слой шоколада" в виде огромного количества Стрингов (надо же было так назвать "много букв"!) кушает весьма много "оперативки". И несколько проектов даже имея 21К свободного heap отказались посылать push, и уходили в ребут. Чертов SSL!

Лезем опять в Гугль, смотрим, как народ борется с нехваткой памяти. Находим чудесные вещи с лаконичным названием PSTR() и F(), и начинаем их применять.
C++:
     //------- Обновлялка. Грузилка. Запасной вариант. -------------
  server.on("/refit", HTTP_GET, [](){
  server.sendHeader("Connection", "close");
  server.sendHeader("Access-Control-Allow-Origin", "*");
  const char * serverIndex = PSTR("<html><head><meta charset=utf-8><title>Обновлялка</title>\
  <style>body {background-color: #cccccc; font-size: 16pt; Color: #ff0000;}</style></head><body>\
  <P>Обновлялка.</P><form method='POST' action='/update' enctype='multipart/form-data'>\
  <input type='file' name='update'><input type='submit' value='Прошить'></form>\
  <P>SPIFFS Upload</P><form method='POST' action='/edit' enctype='multipart/form-data'>\
  <input type='file' name='data'><input class='button' type='submit' value='Upload'></form></body></html>");
  server.send(200, "text/html", serverIndex);
  });

или

void Tun_ing()
{
  server.sendHeader("Connection", "close");
  server.sendHeader("Access-Control-Allow-Origin", "*");
  String tHTML = F("<!DOCTYPE html><html lang=ru><head><meta charset=\"utf-8\"><title>Tuning.</title><style>body {background-color: #cccccc; font-size: 16pt; font-family: arial;}</style></head><body><form method=\"post\" action=\"/tunsave\"><input required id=\"pI\" name=\"_pageId\" maxlength=\"2\" placeholder=\"порядковый номер\" value=\"");
  tHTML += String(pageId);
  tHTML += F("\"><br><input required id=\"dN\" name=\"_devName\" maxlength=\"10\" placeholder=\"имя в IoTmanager\" value=\"") + devName;
  tHTML += F("\"><br><input required id=\"dI\" name=\"_deviceId\" maxlength=\"24\" placeholder=\"идентификатор устройства\" value=\"") + deviceId;
  tHTML += F("\"><br><input required id=\"pg\" name=\"_page\" maxlength=\"10\" placeholder=\"закладка в IoTmanager\" value=\"") + page;
...
...
Память резко освобождается до 32К+. Push начинает работать. Можно продолжать развлекаться.
 

enjoynering

Well-known member
просто оставлю это здесь:

const char [] PROGMEM - тут.

еще одна магия R"=EOF=(текст тут)=EOF=":
- R"=EOF=()=EOF=" or R"=====()=====":
- R means treat everything between "=====(" & ")=====" as a raw string.
- "=====" can be anything you like (within certain bounds) as long as it's the
same at both the start & end of the string. You could use R"-=(This is "text")=-"
which would assign the string 'This is "text"'. The important thing to remember
is that ")=====" must not appear in your text anywhere, so make sure it's
something really obscure.
пример использования всего что выше:
Код:
const char indexHTML[] PROGMEM = R"=EOF=(
ваша страница htlm тут
)=EOF=";
остальное коментировать не буду, но вам опреленно надо почитать книги по C и C++
 

Mоnk

Member
остальное коментировать не буду
Жаль. Возможно это помогло бы Вам научиться выражать свои мысли четко и ясно для других. А другим Буржуинам было бы интересно узнать "страшную военную тайну, которую скрывает Мальчиш-Кибальчиш". И горько заплакать над своим криво написанным кодом.
 
  • Like
Реакции: PAV

enjoynering

Well-known member
Если "разжуёте", что это, и как работает, буду признателен.
Ели я ещё буду жевать за вас, то зачем вы в этом уравнении?

Ловить рыбу за вас нет ни малейшего желания. Извините, что потратил свое время на вас. Больше этого не повторится. Тысяча извинений.
 
  • Angry
Реакции: PAV

Atom

Member
Да, пасибо. На "R" натыкался, но кроме того, что это "необработанный Стринг", ничего толком не нашел. Если "разжуёте", что это, и как работает, буду признателен.
Это возможность указать компилятору, что текст из несколько линий ниже будет входить в значение константы. Точно не помню какие там правила, но нужно быть осторожным с пробелами в конце терминационной строки и вроде как в каждой линии приписать \n.
 

Atom

Member
Что за версия
Ардуюню 3.0.2

Если речь о Arduino IDE, то новая это 2.0.0.

Но не в этом цимис. К нему цепляется Esp Exception decoder?
 

PAV

Member
Ох, чую зря я вчера обновился ;)))


Пока, из того, что нашел - для BME сходу заработала библиотека Seeed_BME280.h Остальные отправляли контроллер в ребут.
 

Mоnk

Member
для BME сходу заработала библиотека
C++:
#define D3  0
#define D4  2

#include <Wire.h>                 //---------- I2C
#define BME280                    //---------- Используется датчик si7021 AM2302 BME280

#define SCL_pin     D3            //---------- I2C SCL 0
#define SDA_pin     D4            //---------- I2C SDA 2

#ifdef BME280
  #define BME280_ADDRESS  0x76
  float   Weather_p;
  String  Weather_pS    = "#";
  bool    BME280present = false;  // ===== BME280 найден?
  // Calibration data
  uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3;
  uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3;
  int16_t dig_P4;  int16_t dig_P5; int16_t dig_P6;
  int16_t dig_P7;  int16_t dig_P8; int16_t dig_P9;
  uint8_t dig_H1;  int16_t dig_H2; uint8_t dig_H3;
  int16_t dig_H4;  int16_t dig_H5; int8_t  dig_H6;
  int32_t t_fine;
#endif

  String  Weather_tS      = "#";
  String  Weather_hS      = "#";
  float   Weather_t, Weather_h;
  int     timer10s        = 0;      // ===== 10 sec
  unsigned long timeNow   = 0;

void setup()
{
  Wire.begin(SDA_pin, SCL_pin);       //---------- I2C
}

void loop()
{
  timeNow = millis();
  if ((timeNow - timer10s) > 10000)  //---------- 10 секунд
  {
  getWeather();
  timer10s = timeNow;
  }
}

#if defined(BME280)
//=====================================================
void getWeather()
{
  if (BME280Read8(0xD0) == 0x60)
  {
    if (!BME280present)
    {
      dig_T1 = BME280Read16LE(0x88);
      dig_T2 = BME280ReadS16LE(0x8A);
      dig_T3 = BME280ReadS16LE(0x8C);

      dig_P1 = BME280Read16LE(0x8E);
      dig_P2 = BME280ReadS16LE(0x90);
      dig_P3 = BME280ReadS16LE(0x92);
      dig_P4 = BME280ReadS16LE(0x94);
      dig_P5 = BME280ReadS16LE(0x96);
      dig_P6 = BME280ReadS16LE(0x98);
      dig_P7 = BME280ReadS16LE(0x9A);
      dig_P8 = BME280ReadS16LE(0x9C);
      dig_P9 = BME280ReadS16LE(0x9E);

      dig_H1 = BME280Read8(0xA1);
      dig_H2 = BME280Read16LE(0xE1);
      dig_H3 = BME280Read8(0xE3);
      dig_H4 = (BME280Read8(0xE4) << 4) | (0x0F & BME280Read8(0xE4 + 1));
      dig_H5 = (BME280Read8(0xE5 + 1) << 4) | (0x0F & BME280Read8(0xE5) >> 4);
      dig_H6 = (int8_t)BME280Read8(0xE7);

      writeRegister(0xF2, 0x05);  //Choose 16X oversampling
      writeRegister(0xF4, 0xB7);  //Choose 16X oversampling

      BME280present = true;
    }
    getTemperature();
      Weather_tS = Weather_t < 0 ? String(Weather_t) : "+" + String(Weather_t);
    getHumidity();
      Weather_hS = String(Weather_h);
    getPressure();
      Weather_pS = String(Weather_p);
/*
    if (hide[CLIMATE])     // Здесь собственно вывод данных куда надо (датчик доступен)
    {
      hide[CLIMATE] = false;
      pubConfigX(CLIMATE);
    }
*/
  }
  else
  {
    BME280present = false;
    Weather_tS = "#";
    Weather_hS = "#";
    Weather_pS = "#";
/*
    if (!hide[CLIMATE])     // Здесь собственно вывод данных куда надо (датчик не доступен)
    {
      hide[CLIMATE] = true;
      pubConfigX(CLIMATE);
    }
*/
  }
}

void getTemperature()
{
  int32_t var1, var2;
  int32_t adc_T = BME280Read24(0xFA);

  adc_T >>= 4;
  var1 = (((adc_T >> 3) - ((int32_t)(dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
  var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
  t_fine = var1 + var2;

  Weather_t = ((t_fine * 5 + 128) >> 8)/100.0;
}

void getHumidity()
{
  int32_t v_x1_u32r;
  int32_t adc_H = BME280Read16(0xFD);

  v_x1_u32r = (t_fine - ((int32_t)76800));
  v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((
    int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((
      int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
  v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
  v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
  v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
    
  Weather_h = (uint32_t)(v_x1_u32r >> 12) / 1024.0;
}

void getPressure()
{
  int64_t var1, var2, p;
  int32_t adc_P = BME280Read24(0xF7);

  adc_P >>= 4;
  var1 = ((int64_t)t_fine) - 128000;
  var2 = var1 * var1 * (int64_t)dig_P6;
  var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
  var2 = var2 + (((int64_t)dig_P4) << 35);
  var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
  var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
  if (var1 == 0) { return; }    // избежать исключения, вызванного делением на ноль
  p = 1048576 - adc_P;
  p = (((p << 31) - var2) * 3125) / var1;
  var1 = (((int64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
  var2 = (((int64_t)dig_P8) * p) >> 19;
  p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
    
  Weather_p = ((uint32_t)p / 256) / 133.32239F;
}

uint8_t BME280Read8(uint8_t reg)
{
  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(BME280_ADDRESS, 1);  // return 0 if slave didn't response
    
  if (Wire.available() < 1)
  {
    BME280present = false; return 0;
  }

  return Wire.read();
}

uint16_t BME280Read16(uint8_t reg)
{
  uint8_t msb, lsb;

  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(BME280_ADDRESS, 2);  // return 0 if slave didn't response

  if (Wire.available() < 2)
  {
    BME280present = false; return 0;
  }
  msb = Wire.read();
  lsb = Wire.read();

  return (uint16_t) msb << 8 | lsb;
}

uint16_t BME280Read16LE(uint8_t reg)
{
  uint16_t data = BME280Read16(reg);
  return (data >> 8) | (data << 8);
}

int16_t BME280ReadS16(uint8_t reg) {return (int16_t)BME280Read16(reg);}

int16_t BME280ReadS16LE(uint8_t reg) {return (int16_t)BME280Read16LE(reg);}

uint32_t BME280Read24(uint8_t reg)
{
  uint32_t data;

  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(BME280_ADDRESS, 3);  // return 0 if slave didn't response

  if (Wire.available() < 3)
  {
    BME280present = false; return 0;
  }
  data = Wire.read();
  data <<= 8;
  data |= Wire.read();
  data <<= 8;
  data |= Wire.read();

  return data;
}

void writeRegister(uint8_t reg, uint8_t val)
{
  Wire.beginTransmission(BME280_ADDRESS); // start transmission to device
  Wire.write(reg);                        // send register address
  Wire.write(val);                        // send value to write
  Wire.endTransmission();                 // end transmission
}

//=====================================================
#endif
 
Сверху Снизу