Жиреет скетч

vlad072
Offline
Зарегистрирован: 01.08.2017

Проект предназначен (в том числе) для обмена с mqtt - брокером, но не суть. Проблема в том что размер скетча подошёл к критическим 30кБ скомпилированного кода, а не реализована ещё наверно половина. Начал разбираться, что за чёрт. Оказалось, что при вызове библиотченых фенкции (в частности PubSubClient) скетч жиреет просто нереально. К примеру во многих местах приходится публиковать данные, что то типа

mqtt.publish( "info/armed", armed ? "1":"0" );

И каждый ска вызов одной и той же функции прибавляет к коду пату - тройку сотен байт. Что за бред, компилятор создаёт экземпляр фунции при каждом её вызове что ли?

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

Добро пожаловать из мира мигающих светодиодов в мир "Где мои байты, чувак". Анализ вызова отдельной функции тут ничего может и не дать. Свинья может быть зарыта в любом месте исходного кода.

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

кто ж на ардуине сетевые стеки реализует? - берите ЕСП8266 - у нее 256Кфлеша и порядка 80К оперативки

А ардуины подходят только для ногодрыгов

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

b707 пишет:

кто ж на ардуине сетевые стеки реализует? - берите ЕСП8266 - у нее 256Кфлеша и порядка 80К оперативки

А ардуины подходят только для ногодрыгов

При всем уважении.... Т е учиться нормально программировать при ограниченных ресурсах вы призывать людей не хотите?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

andycat пишет:
b707 пишет:

кто ж на ардуине сетевые стеки реализует? - берите ЕСП8266 - у нее 256Кфлеша и порядка 80К оперативки

А ардуины подходят только для ногодрыгов

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

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

Ну, там, здоровый образ жизни, фитнес там ...

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

andriano пишет:

andycat пишет:
b707 пишет:

кто ж на ардуине сетевые стеки реализует? - берите ЕСП8266 - у нее 256Кфлеша и порядка 80К оперативки

А ардуины подходят только для ногодрыгов

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

Конечно не решение, очевидно же что если у ТС скетч жиреет от библиотеки, значит надо её выкинуть и переписать самому.

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

Может он там строки адские к каждому запросу приделывает. А думает, что либа виновата.

vlad072
Offline
Зарегистрирован: 01.08.2017

Никакие не адские, вызывается одни и те же функции с одними и теми же топиками и короткими пэйлоадами.

bool pubArmed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/armed", armed ? "1":"0" );
  return false; }
bool pubDoorClosed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/doorClosed", door.active() ? "0":"1" );
  return false; }
bool pubHoodClosed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/hoodClosed", hood.active() ? "0":"1" );
  return false; }
bool pubIgnition() {
  if (mqtt.state() == 0) return mqtt.publish( "info/ignOn", ignition.active() ? "1":"0" );
  return false; }
bool pubEngRunning() {
  if (mqtt.state() == 0) return mqtt.publish( "info/engRunning", engRunning ? "1":"0" );
  return false; }
bool pubWarmup() {
  if (mqtt.state() == 0) return mqtt.publish( "info/warmup", warmup ? "1":"0" );
  return false; }

 

vlad072
Offline
Зарегистрирован: 01.08.2017

b707 пишет:

берите ЕСП8266

Нужен GSM

vlad072
Offline
Зарегистрирован: 01.08.2017

andycat][quote=andriano пишет:

если у ТС скетч жиреет от библиотеки, значит надо её выкинуть и переписать самому.

Это крайний вариант

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

vlad072 пишет:

Это крайний вариант

Крайний с какого края?

vlad072
Offline
Зарегистрирован: 01.08.2017

С края лени. На кой чёрт тогда библиотеки

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

Ну, посмотрите, может там эта publish (или state) - inline, Если так - уберите inline (и вынесете в другой файл) - перестанет расти.

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

Что-то я не вижу в publish() ничего такого, что тянуло бы на 300 байт.

vlad072
Offline
Зарегистрирован: 01.08.2017

нет инлайнов

vlad072
Offline
Зарегистрирован: 01.08.2017

sadman41 пишет:

Что-то я не вижу в publish() ничего такого, что тянуло бы на 300 байт.

В том то и дело. по идее если мы её один раз заюзали, у компилятора есть её адрес и дальше он её по нему вызывает. Ну как бы так по логике должно быть.

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

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

vlad072
Offline
Зарегистрирован: 01.08.2017

Выложить весь код? Кто в нём здесь будет ковыряться?

typedef unsigned long dword;
#include "consts.h"
#include "classes.h"

#include <CyberLib.h>

#include <DallasTemperature.h>
#include <OneWire.h>

#include <SoftwareSerial.h>
#define TINY_GSM_MODEM_SIM800;
#include <TinyGsmClient.h>
#include <PubSubClient.h>
SoftwareSerial gsm(SIM800RX_PIN, SIM800TX_PIN); // RX Arduino (TX sim800), TX Arduino (RX sim800)
TinyGsm modem(gsm);
TinyGsmClient client(modem);
PubSubClient mqtt(client);

Input  lock(LOCK_PIN), door(DOOR_PIN), hood(HOOD_PIN), fuelpump(FUELPUMP_PIN);
Output siren(SIREN_PIN), flash(FLASH_PIN), dvr(DVR_PIN), led(LED_PIN);
InOut  ignition(IGN_SUPPLY_PIN, IGN_PIN), starter(STARTER_SUPPLY_PIN, STARTER_PIN);
Shock  shock;
Period t7sec(7000), t1min(60*1000), t1hour(60*60*1000), t1day(24*60*60*1000);

OneWire           oneWire(ONEWIRE_PIN);
DallasTemperature sensors(&oneWire);
DeviceAddress     engSensor = {0x20, 0x0C, 0x01, 0x07, 0x27, 0x74, 0x09, 0x15};
DeviceAddress     outSensor = {0x20, 0x0C, 0x01, 0x07, 0x27, 0x74, 0x09, 0x15};

bool    armed = false;
bool    warmup = false;
byte    stopTimer = 10;
float   stopTemp = 90;
float   engTempC = -127;
float   outTempC = -127;

int    getAnalog(int);
float  vBatt(void);
float  iBatt(void);
bool   neutral(void);
bool   engRunning(void);
bool   drive(void);
int    getDoors(void);
int    triggDoors(void);
float  tempC(DeviceAddress);
void   raiseAlarm(char[], dword);
bool   fullConnect(void);
bool   mqttConnect(void);
void   mqttCallback(char*, byte*, word);
void   sendMetrics(void);

void setup()
{
    //analogReference(EXTERNAL);
  pinMode(NEUTRAL_PIN,  INPUT_PULLUP);  
  Serial.begin(9600);
  gsm.begin(9600);
  mqtt.setServer(broker, brokerPort);
  mqtt.setCallback(mqttCallback);
  while (!fullConnect());
  //------------------------------------
  sendMetrics();
  //------------------------------------
  sensors.begin();
  sensors.setResolution(engSensor, 9);
  sensors.setResolution(outSensor, 9);
}

void loop() //=======================================================================================================================
{
  
  if (Serial.available()) { Serial.readString(); sendMetrics(); }
  static int connState = mqtt.state();
  if (connState != 0) {
    Serial.print("disconnect reason = "); Serial.println(connState);
    for (byte att = 0; att <= 5; att++) {
      if (mqttConnect()) break;
      if (att == 5) fullConnect();
    }
  }
    
  if (lock.change() == 1)                                                 // ========= следим за командой закрытия ЦЗ
    if (!drive()) {
      if (getDoors() > 0) siren.pulse(200);
      shock.reset();
      armed = true;
    }    
  if (lock.change() == 0) {
    armed = false;
    shock.reset();
    shock.disable();
  }

  if (drive()) dvr.pulse(60000);                                   // ========= управление регистратором
  
  //led.set( armed && ((millis() & 0x3ff) > 0x370) );                 // ========= в охранке раз в секунду мигаем диодом на стойке

  if ( !neutral() || (stopTimer < 1) || (engTempC > stopTemp) || (iBatt() > 15.0) ) { // ======== глушим движку
    ignition.set(false);
    warmup = false; pubWarmup();
  }

   if (t1day.now()) {
    pubBalance(); 
  } else if (t1hour.now()) {
    pubOutTemp(); pubVBatt(); 
  } else if (t1min.now()) {
    if (stopTimer > 0) stopTimer--;
    pubStopTimer();
    if (!warmup) { pubIBatt(); pubEngTemp(); }
  } else if (t7sec.now()) {
    sensors.requestTemperatures(); engTempC = sensors.getTempC(engSensor); outTempC = sensors.getTempC(outSensor);
    pubSignal(); pubArmed(); pubHoodClosed(); pubDoorClosed(); pubIgnition(); pubEngRunning(); pubWarmup();
    if (warmup) { pubIBatt(); pubEngTemp(); }
  } 
    
  flash.processing(); siren.processing(); dvr.processing(); mqtt.loop();

} //================================================================================================================================

int getAnalog(int pin) {
  word _lvl[5];
  for (byte i = 0; i < 5; i++) _lvl[i] = analogRead(pin);
  return find_similar(_lvl, 5, 3);
  return(analogRead(pin));
}
float vBatt() {
  int _lvl = getAnalog(BATT_PIN);
  return(_lvl/65.0);
}
float iBatt() {
  int _lvl = getAnalog(CURRENT_PIN);
  return (_lvl - 640) / 20.46;
}
bool neutral() {
  return(getAnalog(NEUTRAL_PIN) < 130); // ~591 при INTERNAL
}
bool engRunning() {
  return( fuelpump.active() || (vBatt() > 13.8) );
}
bool drive() {
  return( engRunning() && !neutral() );
}
int getDoors() {
  return ( hood.active() + (door.active() << 1) );
}
int triggDoors() {     // 2 = door open + 1 = hood open
  static int _old = 0;
  int _ret = 0;
  int _state = getDoors();
  if (_state > _old) _ret = _state;
  _old = _state;
  return _ret;
}

byte preStart() {
  if (engRunning()) return 1;           // движок уже работает
  if (!neutral())   return 2;           // на скорости
  ignition.set(true);
  if (starter.active()) { ignition.set(false); return 4; }            // пробило мосфет стартера
  if (!fuelpump.active()) { ignition.set(false); return 8; }          // не включился бензонасос
  for ( dword _t = millis(); (millis() - _t) < 3000; )
    if (fuelpump.change() == 0)
      { ignition.change(); return 0; }
  ignition.set(false);                      // сбрасываем фронт импульса чтоб не сработала сигналка
  return 1;
}
byte nowStart() {
  if (engRunning()) return 1;
  shock.disable();
  float _relVolt = vBatt() - 2.0; dword _begin = millis();
  if (!starter.set(true)) { ignition.set(false); shock.reset(); return 4; }
  delay(500);
  while ( ((millis() - _begin) < 1500) && (vBatt() < _relVolt) );
  if (!starter.set(false)) { ignition.set(false); shock.reset(); return 4; }
  delay(2000);
  shock.reset();
  return (engRunning() ? 0:0x10);         // если недокрутил, то 0x10
}

float tempC(DeviceAddress sid){
  sensors.requestTemperaturesByAddress(sid);
  return sensors.getTempC(sid);
}

void raiseAlarm(const char msg[], dword dur) {
  siren.pulse(dur); flash.pulse(dur);
  mqtt.publish("messages/notify", msg);
}

bool fullConnect() {
  Serial.print("modem restart... ");
  modem.restart(); delay(5000);
  Serial.println(modem.getModemInfo());
  Serial.print("wait for network... ");
  modem.waitForNetwork();
  if (modem.isNetworkConnected())
    Serial.println("OK");
  else {
    Serial.println("error!");
    return false; } 
  Serial.print("gprs connecting... ");
  modem.gprsConnect(apn, apnUser, apnPass);
  if (modem.isGprsConnected())
    Serial.println("OK");
  else {
    Serial.println("error!");
    return false; }
  return mqttConnect();
}
bool mqttConnect() {
  Serial.print("broker connecting... ");
  if (mqtt.connect("BlackBox", brokerUser, brokerPass)) {
    Serial.println("OK");
    mqtt.subscribe("cmd/#");
    mqtt.publish("messages/notify", "Mazda is connecting");
  } else {
    Serial.println("error!"); }
  return mqtt.connected();
}
void mqttCallback(char* topic, byte* payload, word len) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.write(payload, len);
  Serial.println();

  if (String(topic) == "cmd/start") {
    if (warmup) {
      warmup = !ignition.set(false);
      return;
    }
    static unsigned long _press = 0;
    if ((char*)payload == '1') _press = millis();
    if ((char*)payload == '0') {
      if ( (_press != 0) && ((millis() - _press) > 5000) && ((millis() - _press) < 10000) )
        if (preStart() == 0)
          warmup = (nowStart() == 0);
      _press = 0;
    }
    pubWarmup();
    //led.set(String((char*)payload).toInt());
  }
  if (String(topic) == "cmd/stopTemp") {
    stopTemp = String((char*)payload).toInt();
  }
  if (String(topic) == "cmd/stopTime") {
    stopTimer = String((char*)payload).toInt();
  }
}
bool pubSignal() {
  if (mqtt.state() == 0) return mqtt.publish( "info/signal", String(modem.getSignalQuality()).c_str() );
  return false; }
bool pubArmed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/armed", armed ? "1":"0" );
  return false; }
bool pubDoorClosed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/doorClosed", door.active() ? "0":"1" );
  return false; }
bool pubHoodClosed() {
  if (mqtt.state() == 0) return mqtt.publish( "info/hoodClosed", hood.active() ? "0":"1" );
  return false; }
bool pubIgnition() {
  if (mqtt.state() == 0) return mqtt.publish( "info/ignOn", ignition.active() ? "1":"0" );
  return false; }
bool pubEngRunning() {
  if (mqtt.state() == 0) return mqtt.publish( "info/engRunning", engRunning ? "1":"0" );
  return false; }
bool pubWarmup() {
  if (mqtt.state() == 0) return mqtt.publish( "info/warmup", warmup ? "1":"0" );
  return false; }
bool pubVBatt() {
  if (mqtt.state() == 0) return mqtt.publish( "info/vBatt", String(vBatt()).c_str() );
  return false; }
bool pubIBatt() {
  if (mqtt.state() == 0) return mqtt.publish( "info/iBatt", String(iBatt()).c_str() );
  return false; }
bool pubEngTemp() {
  if (mqtt.state() == 0) return mqtt.publish( "info/engTemp", String(engTempC).c_str() );
  return false; }
bool pubOutTemp() {
  if (mqtt.state() == 0) return mqtt.publish( "info/outTemp", String(outTempC).c_str() );
  return false; }
bool pubBalance() {
  if (mqtt.state() == 0) return mqtt.publish( "info/balance", String(modem.sendUSSD("*105#").substring(8).toFloat()).c_str() );
  return false; }
bool pubStopTimer() {
  if (mqtt.state() == 0) return mqtt.publish( "info/stopTimer", String(stopTimer).c_str() );
  return false; }

void sendMetrics() {
}
//=======classes.h==========
volatile byte shSt;

class Input {
  public:
    Input(int pin) {
      _pin = pin;
      pinMode(pin, INPUT_PULLUP);
      _old = active();
    }
    bool active(void) {
      byte _cntr;
      do {
        _cntr = 0;
        for (byte _i = 1; _i <= 10; _i++) _cntr += (int)digitalRead(_pin);
      } while ((_cntr > 0) && (_cntr < 10));
      return(_cntr == 0);
    }
    int change() {
      bool _state = active();
      if (_state == _old) return -1;
      _old = _state;
      return (int)_state;
    }
  private:
    bool _old;
    int  _pin;
}; // Input

class Output {
  public:
    Output(int pin) {
      _pin = pin;
      pinMode(pin, OUTPUT);
      digitalWrite(pin, LOW);
    }
    void set(bool val) {
      digitalWrite(_pin, val ? HIGH:LOW);
      _state = val;
      _actual = false;
    }
    void pulse(dword timeout) {
      _begin = millis();
      _timeout = timeout;
      _actual = true;
    }
    void processing() {
      if (_actual) {
        bool _timely = (millis() - _begin) < _timeout;
        if (_timely && !_state) { digitalWrite(_pin, HIGH); _state = true;}
        if (!_timely && _state) { digitalWrite(_pin, LOW); _state = false; _actual = false; }
      }        
    }
  private:
    int _pin;
    dword _begin = 0;
    dword _timeout = 0;
    bool _actual = false;
    bool _state = false;
}; // Output

class InOut : public Input, Output {
  public:
    InOut(int inPin, int outPin) : Input(inPin), Output(outPin) { }
    bool set(bool val) {
      Output::set(val);
      return (active() == val);
    }
 }; //InOut

class Shock {
  public:
    Shock() {
      pinMode(2, INPUT_PULLUP);
      pinMode(3, INPUT_PULLUP);
      shSt = 0;
    }
    void reset() {
      shSt = 0;
      attachInterrupt(0, hi, FALLING);
      attachInterrupt(1, lo, FALLING);
    }
    void disable() {
      detachInterrupt(0);
      detachInterrupt(1);
    }    
    byte state() {
    if (shSt == 1) 
      for (dword _begin = millis(); (millis() - _begin) < 100;)
        if (shSt > 1) return 2;
    if (shSt> 2) shSt = 2;
    return shSt;
    }
  private:
    //static volatile byte _st;
    static void hi() {
      shSt = 2;
      detachInterrupt(0);
    }
    static void lo() {
      shSt |= 1;
      detachInterrupt(1);
    }
}; // Shock

class Period {
  public:
    Period (unsigned long dur) {
      _dur = dur;
      _begin = millis();
    }
    bool now(void) {
      bool _ret = (millis() - _begin) > _dur;
      if (_ret) _begin = millis();
      return _ret;
    }
  private:
    unsigned long _begin = 0;
    unsigned long _dur = 0;
};
//==========consts.h==========

#define IGN_PIN              4  // включение зажигания - транспондер через оптрон (2)
#define STARTER_PIN          5  // включение стартера (3)
#define SIREN_PIN            6  // сирена (4)mqtt
#define DVR_PIN              7  // видеоргистратор (5)
#define IGN_SUPPLY_PIN      A5  // контроль зажигания (6)
#define FLASH_PIN            8  // аварийка (7)
#define SIM800RX_PIN         9  // RX Arduino (TX sim800L) (8)
#define SIM800TX_PIN        10  // TX Arduino (RX sim800L) (9)
#define FUELPUMP_PIN        11  // контроль работы бензонасоса ! (оптрон) 10
#define STARTER_SUPPLY_PIN  A4  // контроль питания стартера ! (оптрон) (11)
#define ONEWIRE_PIN         12  // датчики температуры
#define LED_PIN             13  // светодиод
#define HOOD_PIN            A0  // концевик дверей (через диод) PULLUP
#define DOOR_PIN            A1  // концевик капота (через диод) PULLUP
#define NEUTRAL_PIN         A2  // контроль нейтрали ! (через диод) PULLUP
#define LOCK_PIN            A3  // центральный замок (через диод) PULLUP
#define KNOCK1_PIN           3  // датчик удара1 (через диод) PULLUP (A4)
#define KNOCK2_PIN           2  // датчик удара2 (через диод) PULLUP (A5)
#define BATT_PIN            A7  // напряжение аккума (делитель)
#define CURRENT_PIN         A6  // ток в цепи +12В

const char*  apn  = "internet.tele2.ru";
const char*  apnUser = "tele2";
const char*  apnPass = "tele2";
const char*  broker = "m23.cloudmqtt.com";
const word   brokerPort = 17777;
const char*  brokerUser = "fvevefvffeve";
const char*  brokerPass = "v344fr4rf3rfef3r3";

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

человек! Ты хоть пароль от брокера убери! ;)))

vlad072
Offline
Зарегистрирован: 01.08.2017

wdrakula пишет:

человек! Ты хоть пароль от брокера убери! ;)))

лоин/пароль неверный ессенно

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

И да. Я всегда до новичков докапываюсь, ту не стану. Нормально все написано, аккуратно. Немножно без скидки на контроллер, как для ПК, но аккуратно.

Всякие строковые темы нужно попробовать урезать, НО! -  самое первое - выкинуть даллас-темприча! Сразу дышать станет легче. Она вообще никому и никогда не нужна, Onewire всегда достаточно. На три строки больше написать придется! ;) Выкинь и посмотри, что выйдет.

vlad072
Offline
Зарегистрирован: 01.08.2017

wdrakula пишет:

И да. Я всегда до новичков докапываюсь, ту не стану. Нормально все написано, аккуратно. Немножно без скидки на контроллер, как для ПК, но аккуратно.

Всякие строковые темы нужно попробовать урезать, НО! -  самое первое - выкинуть даллас-темприча! Сразу дышать станет легче. Она вообще никому и никогда не нужна, Onewire всегда достаточно. На три строки больше написать придется! ;) Выкинь и посмотри, что выйдет.

Пробовал - экономия на спичках, меня это никак не спасёт. Он хавает совсем немного. Всё по отдельности пробовал выкидывать, слёзы экономии. PubSub ска жрёт как мерен

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

Естественно - большой код никто за вас отлаживать не будет, тем более если всё это завязано на какие-то доп. сервисы. Если хотите попытаться докопаться до причины - сокращаете его так, чтобы остались только подозреваемые фрагменты. Потом проводите следственный эксперимент, добавляя и убавляя кол-во вызовов publish. Если гипотеза про 300 байт подтверждаетеся - значит стоит ковырять либу, если не подтверждается - чешете голову дальше.

В целом - кто-то работает, наверное, с этими TinyGSM и пр. Сможет примерно прикинуть: пожрут ли они 20кб или нет. На глазок - тут килобайт 10 в юзерспейсе, если так можно выразится. Ну вот эти float, Serial, String... если от них избавится - освободят progmem/ram. Для интереса скомпилируйте что-нить простое с float и без - посмотрите, как будет изменяться объём прошивки.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

vlad072] </p> <p>[quote=andycat пишет:
andriano пишет:

если у ТС скетч жиреет от библиотеки, значит надо её выкинуть и переписать самому.

Это крайний вариант

Что там крайнего, рядом лежат готовые функции, примитив, пихаете в отправку своего gsm и все

http://arduino.ru/forum/programmirovanie/snova-mqtt-1#comment-404716

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

[/quote] Что там крайнего, рядом лежат готовые функции, примитив, пихаете в отправку своего gsm и все http://arduino.ru/forum/programmirovanie/snova-mqtt-1#comment-404716[/quote]

Да действительно, совсем фигня... если только "лампочку мигать"