Впихнуть Невпихуемое

Messiah
Offline
Зарегистрирован: 10.11.2018

Прошу прощения за мой плохой французский в названии темы. Есть Uno, у нее 32 килобайта. Получил вот такую штуку:

Скетч использует 32362 байт (100%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 1759 байт (85%) динамической памяти, оставляя 289 байт для локальных переменных. Максимум: 2048 байт.

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

#include <SoftwareSerial.h>
#include <SPI.h>
#include <swRTC.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include "SdFat.h"
#include <dht11.h>

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

Все нужное, уточню что для чего. По Ethernet, а точнее EthernetUdp идет общение с внешним миром и синхронизация часов. Через SoftwareSerial опрашивается и управляется одна железяка. swRTC для часов реального времени. SdFat и SPI - чтобы на карту памяти что-то писать (логи, статистика). Все нужное. До получения данных с датчика температуры дело еще не дошло.

Теперь, собственно, вопрос. В каком направлении мне двигаться? (Гусары, молчать!)

Есть ли какая-то легкая замена SdFat, жрущая меньше памяти? Много ли я выиграю если откажусь от чисел с плавающей точкой (при желании, умножая на 1000 падает точность но укладываюсь в integer, говорят 2кб экономии), если перейду со String на массив байт? Никто случайно не пробовал кастрировать библиотеки Adruino, например, выпилить из Ethernet все что не нужно для UDP протокола (ICMP, TCP, предполагаю что от них солидный оверхед). Опыт возни есть, в свое время кастрировал системные модули Borland Studio и Delphi, и даже на assembler писал. У кого есть опыт написания "тяжелых" скетчей чтобы поделиться рекомендациями что из выше перечисленного будет эффективнее? Столкнулся еще с подвисанием железяки если код занимает слишком много памяти, видимо, где-то динамическое выделение валится...

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Прежде, чем выносить какие-то оценочные суждения - надо бы увидеть хотя бы часть кода. Возможно, и даже скорее всего - там есть места, поддающиеся оптимизации. 

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

если твоё рабочее время стоит дороже 500 р за день, то двигаться надо в сторону покупки Меги.

Messiah
Offline
Зарегистрирован: 10.11.2018

Моего кода там мало, основной объем тянется из стандартных библиотек. Вот например если убрать прием - отправку UDP пакетов - Скетч использует 24052 байт (74%) памяти устройства. Глобальные переменные используют 1392 байт (67%). С ним - Скетч использует 30002 байт (93%) памяти устройства. Глобальные переменные используют 1647 байт (80%). Больше всего экономиться если не использовать SdFat.h

Где-то писали что использовать String это плохая идея, но у меня в коде один раз формируется строка, она отправляется в USB для отладки (это можно и убрать) и передается функции, которая пишет ее в лог с текущим временем. Еще преимущество этого класса - конвертация чисел в строку. Вот начну я сейчас трахаться с самописным inttostr а окажется что экономия не существенна. Или наоборот, убрать в Delphi модуль SysUtils и получить -40 кб кода оптимизации. К сожалению, не знаю прикуп.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Если не рефлексировать - то самый простой путь, как посоветовали выше - взять Мегу 2560. Потому как надо ещё подумать - стоит ли тратить на оптимизацию время, если задача - частная, и не проще ли и дешевле (во всех смыслах) взять подходящий камень.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

А если хотите подешевле - берите STM32f103c8t6 стоимостью с пачку сигарет. Без плясок с бубнами тут, конечно, не обойтись - но зато возможности!

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

Присоединяюсь к #1.

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

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

Присоединяюсь к #1.


а какая разница, что у ТС за код? оптимизация -- задача глубокого изучения программирования, если даже штатные функции работы с С-строками у ТС "самописные". Мега стоит 500р. А дальше уже автору решать, какая у него задача: улучшать навыки в программировании или получить результат. Путь к результату -- Мега. А Путь изучения -- долог м тернист! ;)))))

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

Постоянно впихиваю невпихуемое. Выкидывание float освободит 2-3, процедура получения температуры отожрет 3-4 кб. Закон подлости такой. Причём, библиотека Dallas снова притащит за собой float.

Тут надо 80% библиотек перешерстить, простой перестановкой кроватей делу не помочь.

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

wdrakula пишет:
А дальше уже автору решать, какая у него задача: улучшать навыки в программировании или получить результат.

Вот я именно про это. Всё от целей зависит. Нужен быстрый результат или есть спортивный интерес. Это как "ехать на работу на машине или на велике" - на выбор влияет огромная куча обстоятельств. Я вот, например, не езжу на велике потому, что в офисе душа нет.

А оптимизации и "стабилизации" там немеряно, это сразу видно. Например, если получение времени тащит за собой аж 10к (как он написал), почему бы не получить его at-командами практически даром? Если есть сомнения, что всё виснет из-за динамической памяти, почему бы уж хотя бы не проверять факт её успешного выделения (никто из новичков этого не делает)? Стринги сами по себе не так страшны, как навороченные операции с ними, которые и не такую память зафрагментируют, ну и т.д.

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

Евгений, как можно получить AT-командами время, если в хидерах чистейший Ethernet?

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

Вам дать код?

Он на самописном "сериале", прямой разбор буфера полученного по сериалу от ESP-шки - никаких библиотек. Все про всё (сериал + NTP +отладочная печать (через I2C)) -  менее 500 строк.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

 простой перестановкой кроватей делу не помочь.

не скажите, иногда смена кровати значительно улучшает настроение )))

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

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

Вам дать код?

Он на самописном "сериале", прямой разбор буфера полученного по сериалу от ESP-шки - никаких библиотек. Все про всё (сериал + NTP +отладочная печать (через I2C)) -  менее 500 строк.

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

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

Вам дать код?

Он на самописном "сериале", прямой разбор буфера полученного по сериалу от ESP-шки - никаких библиотек. Все про всё (сериал + NTP +отладочная печать (через I2C)) -  менее 500 строк.

Это что за монстр, которому на еспэшке места мало, нейросеть ?

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

sadman41 пишет:

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

Ну, сами подумайте, как мы можем обсуждать код и железо ТС, если кроме "Uno" он не сказал ничего? Ни что там ещё, ни схемы, ни собственно кода - вообще ничего?

Нет предмета для обсуждения, мы просто так треплемся (ну, по крайней мере, я)  :)))

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

brokly пишет:

Это что за монстр, которому на еспэшке места мало

Не мало (и не много) - даже попыток никаких не делалось. Изначальное проектное решение было использовать есп-шку исключительно как внешний девайс с интерфейсом AT-команд и ничего более. Т.е. она вообще не прошивалась, а работала с прошивкой, которую китайцы залили.

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

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

Нет предмета для обсуждения, мы просто так треплемся (ну, по крайней мере, я)  :)))

Формально - так. На уровне интуиции  - я по инклюдам понимаю, чего он там творит.

Messiah
Offline
Зарегистрирован: 10.11.2018

Мегу взять можно, но я хочу приключений. Если серьезно - уже придумал корпус и собрал всю схему, отладил ее модульно (кусками) и убедился что все работает, собрал - а потом понял что немножечко не хватает памяти. Хотя для меня 32 килобайта это выше крыши, писал уже что и на ассемблере работать приходилось. Еще не хочу мегу потому что это будет выглядеть как из пушки по воробьям, у меня еще на Uno свободных пинов куча осталась.

У меня только Uno и к нему шилд Ethernet + microSD, ну еще целая самопальная но это к делу не относится. Получить время из сырых заголовков входящих IP пакетов думаю реально но не думаю что это освободит место, скорее займет, потому время тикает с помощью swRTC а получается по NNTP (скетч чуть более чем стандартный, только вместо delay между запросом и ответом выполняются другие полезные задачи).

а какая разница, что у ТС за код? оптимизация -- задача глубокого изучения программирования

Как-то именно так. Пробую значит сегодня избавиться от String, а после начинаю ковырять стандартные модули. Вчера с ethernet начал но не успешно, там простой копипаст библиотеки с переименованием приводит к конфликтам инклюдов, нужно чуть глубже копать.

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

Я бы на вашем месте с такого сложного не начинал. Для начала бы в dht поковырялся, float убрал, от стрингов избавился. swRTC заменил бы на нормальные часы типа DS3231 и пяток простых функций  доступа к ним.

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

Messiah пишет:

Вчера с ethernet начал но не успешно, там простой копипаст библиотеки с переименованием приводит к конфликтам инклюдов, нужно чуть глубже копать.

Вы надеялись создать копию библиотеки, просто переместив ее в другую папку? - это не работает. Ардуино ИДЕ вообще по барабану название папок, оно различает библиотеки по именам инклюдов. Если вы хотите создать свою копию библиотеки для редактирования - мало переместить ее в другую папку - нужно переименовать все инклюды уникальным образом, поменяв заодно имена включаемых файлов во всех директивах #include внутри всех файлов библиотеки.

Так что если библиотека большая и разветвленная - проще не связываться с копиями а просто править оригинал. И еще - имейте в виду, что если вы поправили библиотеку, то изменения вступят в силу только после перезапуска Ардуино или после очистки директории, где собираются временные люьектные файлы для сборки прошивки.

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

b707 пишет:

где собираются временные люьектные файлы для сборки прошивки.

какие?  мошт, я пропустил чего?

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

b707 пишет:

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

Не совсем так страшно надо делать, но процесс геморный )

bwn
Offline
Зарегистрирован: 25.08.2014

sadman41 пишет:

Не совсем так страшно надо делать, но процесс геморный )

Ну не наю, если правлю, оставляю ее одну в папке библиотек. С проектом закончил-отладил, перемещаю в облако вместе с проектом, и возвращаю оригинал на место. Это ИМХО.

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

DetSimen пишет:

b707 пишет:

где собираются временные люьектные файлы для сборки прошивки.

какие?  мошт, я пропустил чего?

ну я имел в виду, что если ты поправил строчку в библиотеке и хочешь, что изменения попали в текущий скетч - надо бы выполнить что-то типа    make clean

Но послько такой команды в ИДЕ нет, то приходится либо перезапукать ИДЕ, либо, скажем, менять в настройках плату, компилировать, снова менять и компилировать вторично

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

bwn пишет:

sadman41 пишет:

Не совсем так страшно надо делать, но процесс геморный )

Ну не наю, если правлю, оставляю ее одну в папке библиотек. С проектом закончил-отладил, перемещаю в облако вместе с проектом, и возвращаю оригинал на место. Это ИМХО.

Я в project-name\src складирую, ибо надёжней иметь компилируемый комплект, чем через полгода влезать в отладку и вспоминать, чего ты там с бодуна нафиксил за это время в либе и почему всё перестало компилиться.

Messiah
Offline
Зарегистрирован: 10.11.2018

> Для начала бы в dht поковырялся, float убрал

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

> swRTC заменил бы на нормальные часы типа DS3231

Часов на Ethernet+SD шилде, кажется, нет. Утяжелять схему еще и часами - идея так себе. 

мало переместить ее в другую папку - нужно переименовать все инклюды 

походу и имена объектов внутри инклюдов, это то на что я нарвался. Оно цепляло все библиотеки и ругалось на дубликат описания класса, хотя из 2 библиотек инклюдилась только одна... вообщем надо разобраться будет.

а просто править оригинал

Тоже вариант. Это мой единственный проект на Arduino, собрать, заархивировать включая каталог с библиотеками, если что нужно будет писать на стандартных - восстановить из бекапа. Все к этому и идет.

файлы для сборки прошивки

Object файлы чтоли? :)

 

Вот из моих заметок. прирост % памяти и динамической памяти в скобках при раскомментировании директивы компилятора, ниже по коду все в #ifdef сидит чтоб не вспоминать где что забыл раскомментировать:

//#define WEBSERVER;      //+6214b mem +186b vars (+19%, +9%)
#define UDPNTPTIME;     //+5696b mem +255b vars (+17%, +12%)
              //TCP+UDP   +7908b mem +315b vars (+24%, +15%) итого 12% общего кода, 5% UDP, 7% TCP
#define SDCARDLOG;        //+8060b mem +715b vars (+25% +35%)
//#define TEMPERATURE;

По swRTC не замерял хотя можно. От вебсервера я окончательно отказался, пока на 1 месте по прожорливости SdFat, за ним Ethernet. Пошел строки колупать, пока что от float отказываться не буду.

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

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

Замеры потерь program space и ram методом отключения некоторого функционала могут дать ошибку процентов... в 20, скажем. Даже в разных версиях IDE можно получить разный ресурсный импакт . Потому что ход работы оптимизирующего компилятора практически непредсказуем для простого "пользователя".

Ethernet меньше чем в 10кб не всадить, SDFAT - не знаю, в лучшем случае пользовательский код со всем этим обвесом будет стартовать с отметки в 18-20кб. +3кб на float, +Serial... Ну и остаётся там килобайт 6-8 может. О каком там вебе говорить?

Logik
Offline
Зарегистрирован: 05.08.2014

sadman41 пишет:

Замеры потерь program space и ram методом отключения некоторого функционала могут дать ошибку процентов... в 20, скажем. Даже в разных версиях IDE можно получить разный ресурсный импакт . Потому что ход работы оптимизирующего компилятора практически непредсказуем для простого "пользователя".

 

Но всеже койчего предсказать можна. Если некую базовую функцию использует несколько частей общего функционала то базовая функция уберется из кода только если уберутся все использующие её. Отсюда и погрешности. Нельзя просто суммировать замеры. Там может и поболе 20% набратся.

SDFAT действительно очень тяжолая.

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

Предсказать можно, если есть опыт и уже знаешь, как эти библиотеки изнутри выглядят и что у них общего. А так, с бухты-барахты - практически тыканье пальцем в небо.

Я уже дошёл до того, что между for и while выбираю последнее:

#pragma GCC optimize ("O0")

void setup() {
  volatile unsigned char i;
  i = 10;
  while (1) {
    i--;
  }
//  for (i = 10; i; i--);
}

void loop() { }

 

Logik
Offline
Зарегистрирован: 05.08.2014

sadman41 пишет:

Предсказать можно, если есть опыт и уже знаешь, как эти библиотеки изнутри выглядят и что у них общего. А так, с бухты-барахты - практически тыканье пальцем в небо.

Я уже дошёл до того, что между for и while выбираю последнее:

#pragma GCC optimize ("O0")

void setup() {
  volatile unsigned char i;
  i = 10;
  while (1) {
    i--;
  }
//  for (i = 10; i; i--);
}

void loop() { }

 

Смотрю на код. Уверены что замена эквивалентная?

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

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

Во-во рефакториг это . Переписывание кода по частям . А по плач Ярославны DIYMan ничего не говорил. Хотя может технологически тоже надо.

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

qwone пишет:
Хотя может технологически тоже надо.
А то!

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

Да, что-то я там единицу зря воткнул в условие.  Однако я помню, что ко while я склонился на основе опытов. Надо будет вернуться к ним.

Всё, вспомнил. Экономил условием сравнения с нулём. Два байта цельных без оптимизации компилятором и 34 (в данном раскладе) - с оптимизацией.

//#pragma GCC optimize ("O0")

//#define USE_DECREMENT_AND_WHILE
void setup() {
  uint8_t i;
  uint8_t a[10];
  Serial.begin(115200);

#ifdef USE_DECREMENT_AND_WHILE
  // Sketch uses 2292 bytes (7%) of program storage space -- optimize ("O0")
  // Sketch uses 1934 bytes (5%) of program storage space -- optimize default
  i = sizeof(a);
  while (i) {
    i--;
    a[i] = random();
    Serial.println(i);
  }
#else
  // Sketch uses 2294 bytes (7%) of program storage space -- optimize ("O0")
  // Sketch uses 1968 bytes (6%) of program storage space -- optimize default
  for (i = 0; sizeof(a) > i; i++) {
    a[i] = random();
    Serial.println(i);
  }
#endif
}

void loop() { }

 

Messiah
Offline
Зарегистрирован: 10.11.2018

Товарищи, а substr(string, index_from, count) для char array существует в природе или писать самому? Мне нужно из буфера данных повырезать нужные позиции по несколько символов и потом сконвертировать их в числовые значения...

Feofan
Offline
Зарегистрирован: 28.05.2017

Посмотрите  тут: http://cppstudio.com/cat/309/325/

Messiah
Offline
Зарегистрирован: 10.11.2018

Спасибо. Memcpy вещь интересная, но уже написал свою с циклом. Благо там 2-5 знаков в подстроке, не так много итераций. Если будет не лень - перепишу еще раз.

Такс, первые результаты. 2 тестовых скетча, в одном из которых присутствует String Reply = ""; а в другом его нет (и больше никаких использований класса String отличаются на 1072 байта. Всего убрав substring, toFloat, toInt, toCharArray и что у меня там еще использовалось, заменив это кучей вызовов strcpy и strcat, а также посимвольным присвоением в цикле, получил экономию 2396 байт кода, что уже неплохо. Иду дальше...

Снова всплыл какой-то интересный баг с зависанием наглухо при вызове Udp.beginPacket(), который лечился исключительно выключением устройства на какое-то время.

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

Messiah пишет:

Товарищи, а substr(string, index_from, count) для char array существует в природе или писать самому? Мне нужно из буфера данных повырезать нужные позиции по несколько символов и потом сконвертировать их в числовые значения...

Изучи, дорогой, полный набор стандарной библиотеки. Интересующие тебя функции в string.h.

https://www.nongnu.org/avr-libc/user-manual/modules.html

Это официальная страница avr-libc, на сайте Atmel есть копия.

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

==================

(после редактирования):точно есть, её дал первый ответ яндекса: http://avr-libc.narod.ru/

Что не знаю, так это версию, которую там перевели, но для Уно и Нано, это не имеет значения.

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

Messiah пишет:

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

Вы уж определеитесь, чего Вам не хватает: памяти или пинов?

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