Помогите оптимизировать код

MARKSIST
Offline
Зарегистрирован: 16.07.2015
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP085.h>
#include <Wire.h>
#include <DS1307RTC.h>
#include <Time.h>
#include <Servo.h>

#define OLED_RESET 4
#define Trig 7
#define Echo 6

Adafruit_SSD1306 display(OLED_RESET);
Adafruit_BMP085 bmp;
tmElements_t tm;
Servo myservo;  // create servo object to control a servo

void setup()   {                
  Serial.begin(9600);
if (!bmp.begin()) {
	Serial.println("Could not find a valid BMP085 sensor, check wiring!");
	while (1) {}
 }
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
  display.clearDisplay();
  pinMode(Trig, OUTPUT); 
  pinMode(Echo, INPUT); 
  myservo.attach(10);  // attaches the servo on pin 9 to the servo object 
  display.setTextSize(2);
  display.setTextColor(WHITE); 
}
unsigned int impulseTime=0; 
unsigned int distance_sm=0;
int pos = 0;    // variable to store the servo position 

void loop() {  
                                           // ДАЛЬНОМЕР
//---------------------------------------------------------------------------------------------------- 
  
  digitalWrite(Trig, HIGH);
  /* Подаем импульс на вход trig дальномера */
  delayMicroseconds(25); // равный 10 микросекундам
  digitalWrite(Trig, LOW); // Отключаем 
  impulseTime=pulseIn(Echo, HIGH); // Замеряем длину импульса 
  distance_sm=impulseTime/58; // Пересчитываем в сантиметры 
  Serial.println(distance_sm); // Выводим на порт 
  if (distance_sm < 30) // Если расстояние менее 30 сантиметром 
  {        

                                          //ВЫВОДИМ НА ДИСПЛЭЙ
//-----------------------------------------------------------------------------------------------------
  //Температура
  
  display.setCursor(0,0);
  display.print("T= ");
  display.print(bmp.readTemperature());
  display.println(" C");

  //Давление
  
  display.setCursor(0,20);
  display.print("P= ");
  display.print(bmp.readPressure());
  
  //Часы

  display.setCursor(0,40);
  print2digits(tm.Hour);
  display.print(":");
  print2digits(tm.Minute);
  display.print(":");
  print2digits(tm.Second);
  display.display();
  delay(1000);
  display.clearDisplay();
  }  
  else 
  {   
    display.setCursor(0,0);
    display.print(" ");
    display.display();
    display.clearDisplay();
  }   
  delay(100); 
                                   //СЕРВО ПРИВОД
//--------------------------------------------------------------------------------------------
  if(bmp.readTemperature()>30)
  {
     myservo.write(20);
  }else
  {
    myservo.write(90); 
  }
} 
 
void print2digits(int number) {       //Функция добавления нуля если число (часы минуты секунды) от 0 до 10
  if (number >= 0 && number < 10) {
    display.print('0');
  }
  display.print(number);
}

При компиляции пишет:

Sketch uses 16 212 bytes (50%) of program storage space. Maximum is 32 256 bytes.
Global variables use 1 681 bytes (82%) of dynamic memory, leaving 367 bytes for local variables. Maximum is 2 048 bytes.
Low memory available, stability problems may occur.
 
А я планирую еще добавлять датчики боюсь не зватит места. 
Платка Arduino UNO. 
 
Подскажите как правильно уменьшить вес прошивки.
Спасибо.

 

Andrey-S
Offline
Зарегистрирован: 02.01.2015

Global variables use 1 681 bytes (82%) of dynamic memory - кажись многовато...Хренова туча библиотек.. Самое простое - УНО на МЕГА если есть возможность...А так попробуйте строки типа display.print("P= "); превратить в display.print(F("P= "));

MARKSIST
Offline
Зарегистрирован: 16.07.2015

а что это за  display.print(F); ?

Andrey-S
Offline
Зарегистрирован: 02.01.2015

MARKSIST пишет:

а что это за  display.print(F); ?

Ну вроде как загоняет всю последующую ерунду во флэш-память... У меня была такая проблема с УНО, когда я на на W5100 нагородил страничку большую... И тут на форуме мне посоветовали как раз строки типа client.print("Заданная температура основного водонагревателя"); превратить в client.print(F("Заданная температура основного водонагревателя")); И сразу все решилось... Думаю с display.print тоже должно сработать

Radjah
Offline
Зарегистрирован: 06.08.2014

> delayMicroseconds(25); // равный 10 микросекундам

Это в какой системе счисления? o_0

68-84

Или добавь чтение времени из часов, или выкинь совсем. tm у тебя не нициализируется нигде.

Еще можно вместо OLED использовать дисплей 2x16 например. Памяти расходовать будет намного меньше.

Andrey-S
Offline
Зарегистрирован: 02.01.2015

Radjah пишет:

> delayMicroseconds(25); // равный 10 микросекундам

Ну он же не сам код писал))) Куски навставлял и все дела))

MARKSIST
Offline
Зарегистрирован: 16.07.2015

Andrey-S 

Спасибо немного помогло

Код писал сам. Просто правил код а коментарии забыл :)

Так у меня и bmp нигде не инициализируется.

А как же тогда без tm время выводить на дисплей?

Andrey-S
Offline
Зарегистрирован: 02.01.2015

Тут, судя по бибиотекам, к УНЕ прихреначено немало... Если собираетесь туда впихивать еще прибамбасов, то все же советовал бы МЕГУ... 

Radjah
Offline
Зарегистрирован: 06.08.2014

MARKSIST пишет:

Andrey-S 

Спасибо немного помогло

Код писал сам. Просто правил код а коментарии забыл :)

Так у меня и bmp нигде не инициализируется.

А как же тогда без tm время выводить на дисплей?

21-24 что тогда?

У тебя в tm мусор. Если хочешь выводить время, то читай в tm время из RTC.

MARKSIST
Offline
Зарегистрирован: 16.07.2015

А можно про tm по подробнее. Просто я пока только учусь. спасибо

Radjah
Offline
Зарегистрирован: 06.08.2014

Куда еще подробнее?

Она объявлена (16), из нее идет чтение (69-73), в нее нигде не идет запись, она нигде не инициализируеется.

MARKSIST
Offline
Зарегистрирован: 16.07.2015

в bmp тоже ничего не записывается. А как влияет инициализация?

Radjah
Offline
Зарегистрирован: 06.08.2014

bmp.readTemperature() - вызов метода класса. Функция возвращает значение.

tm.Hour - чтение поля структуры. Читается значение переменной.

Чтобы из переменной что-то прочитать, в нее надо что-то записать.

Ну это же самые основы. Как вообще можно писать без их знания?

MARKSIST
Offline
Зарегистрирован: 16.07.2015

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

Radjah
Offline
Зарегистрирован: 06.08.2014
MARKSIST
Offline
Зарегистрирован: 16.07.2015

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

Radjah
Offline
Зарегистрирован: 06.08.2014
Andrey-S
Offline
Зарегистрирован: 02.01.2015

MARKSIST пишет:

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

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

MARKSIST
Offline
Зарегистрирован: 16.07.2015
#include <DS1307RTC.h>
#include <Time.h>
#include <Wire.h>

void setup() {
  Serial.begin(9600);
  while (!Serial) ; // wait for serial
  delay(200);
  Serial.println("DS1307RTC Read Test");
  Serial.println("-------------------");
}

void loop() {
  tmElements_t tm;

  if (RTC.read(tm)) {
    Serial.print("Ok, Time = ");
    print2digits(tm.Hour);
    Serial.write(':');
    print2digits(tm.Minute);
    Serial.write(':');
    print2digits(tm.Second);
    Serial.print(", Date (D/M/Y) = ");
    Serial.print(tm.Day);
    Serial.write('/');
    Serial.print(tm.Month);
    Serial.write('/');
    Serial.print(tmYearToCalendar(tm.Year));
    Serial.println();
  } else {
    if (RTC.chipPresent()) {
      Serial.println("The DS1307 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } else {
      Serial.println("DS1307 read error!  Please check the circuitry.");
      Serial.println();
    }
    delay(9000);
  }
  delay(1000);
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

А где в этом примере происходит инициализация tm?

Radjah
Offline
Зарегистрирован: 06.08.2014

16 строка

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

Radjah пишет:

 можно вместо OLED использовать дисплей 2x16 например. Памяти расходовать будет намного меньше.

Да. Похоже либка к SSD1306 зажрала 1к RAM. Видать и сдесь сунули буфер под весь экран. Попробуйте закоментировать всю работу с экраном для проверки этого.

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

Да, библиотека Adafruit отжирает 1К под экранный буфер.

Если нужна попиксельная графика, без этого никак.

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

В частности, библиотека, которую я здесь публиковал:

http://arduino.ru/forum/programmirovanie/kirillitsa-na-displee-ili-chto-...

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

Правда, выводить текст придется несколько угловатым шрифтом высотой 16 пикселей. Зато есть кириллица.

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

andriano пишет:

Да, библиотека Adafruit отжирает 1К под экранный буфер.

Если нужна попиксельная графика, без этого никак.

Нет. Во первых сложности только при подключении по I2C. Видео ОЗУ недоступно на чтение получается и канву надо хранить. Если верить описанию, то при работе по другим интерфейсам оно читаемо и никакой проблемы попиксельно работать без буфера нет, ну кроме тормозов может. Но и при подключении I2C не безнадежно. Можна напрямую выводить картинку из PRG, можно по формировать байты изображения на лету, как некоторую функцию от адреса, я так на нем осцилограф сделал. Если уж так дороги точки, линии,  кружечки - тоже не беда - выделить буфер не глобально, а на время рисования и освободить его после передачи. К тому же буфер можно выделять не на весь экран, а только на его часть. Еще можна без буфера вертикальные и горизонтальные линии выводить, типа таблички а потом поверху текстом без буфера писать. В общем решаемо. Жаль что авторы библиотек о таком не парятся.

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

Logik пишет:

andriano пишет:

Если нужна попиксельная графика, без этого никак.

Нет.

Да, я несколько утрировал. Но, думаю, на данном уровне обсуждения это вполне допустимо.
Цитата:

Во первых сложности только при подключении по I2C.

Лично я других 1306 не видел.
Цитата:

Но и при подключении I2C не безнадежно.

А вот здесь начинается масса ЧАСТНЫХ случаев. И никакой универсальности.
Цитата:

...я так на нем осцилограф сделал.

Буфкр не нужен в одном единственном случае - когда изображение формируется целиком с нуля и не нужно хранить фон под ним.

Очевидно, осциллограф этим требованиям удовлетворяет.

Скажу больше, даже в примерах использования моей библиотеки есть случаи явно нетекстового вывода. Например вывод в стиле "спектранализатор" или даже анимация в стиле Packman. Но это не отменяет общности утверждения, что все кроме текста можно только в частных случаях.

Цитата:

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

См выше - если при новом рисовании не нужно хранить нарисованное ранее.
Цитата:

К тому же буфер можно выделять не на весь экран, а только на его часть.

Ну да, если используеми не весь экран, буфер можно сделать поменьше.
Цитата:

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

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

А вообще, если проявить фантазию, можно очень много чего. Только, как я уже говорил, это все ЧАСТНЫЕ случаи, которые, как правило, нужны лишь одному человеку и никому больше. Поэтому помещать их в библиотеку, а тем более в библиотеку для устройства, имеющего существенные ограничения как по постоянной, так и по оперативной памяти, я думаю, нецелесообразно.

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

andriano пишет:

Лично я других 1306 не видел.

http://ru.aliexpress.com/item/0-96inch-OLED-A-Module-128-64-Pixel-I2C-II...

andriano пишет:

А вот здесь начинается масса ЧАСТНЫХ случаев. И никакой универсальности.

Цитата:

...я так на нем осцилограф сделал.

Буфкр не нужен в одном единственном случае - когда изображение формируется целиком с нуля и не нужно хранить фон под ним.
Так фон под ним не с Венеры прилетел! Наша программа его и формировала, только немного ранее, значить можем и переформировать заново при выводе изображения поверх, обединить по ИЛИ с новым изображением и вывести. Такое возможно почи всегда.
andriano пишет:

Очевидно, осциллограф этим требованиям удовлетворяет.

Скажу больше, даже в примерах использования моей библиотеки есть случаи явно нетекстового вывода. Например вывод в стиле "спектранализатор" или даже анимация в стиле Packman. Но это не отменяет общности утверждения, что все кроме текста можно только в частных случаях.

Цитата:

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

См выше - если при новом рисовании не нужно хранить нарисованное ранее.
Цитата:

К тому же буфер можно выделять не на весь экран, а только на его часть.

Ну да, если используеми не весь экран, буфер можно сделать поменьше.
Цитата:

Так для этого нужен механизм библиотеки, а не забабахаь половину памяти намертво под буфер.

andriano пишет:

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

Строго говоря, либо только вертикальные (в режиме по умолчанию), либо только горизонтальные (если переопределить режим). 
 Конечно режим переключатьт.
andriano пишет:
Иначе последующий текст их сотрет. 
Чего вдруг? Текст внутри ячеек таблички.
andriano пишет:

А вообще, если проявить фантазию, можно очень много чего. Только, как я уже говорил, это все ЧАСТНЫЕ случаи, которые, как правило, нужны лишь одному человеку и никому больше. Поэтому помещать их в библиотеку, а тем более в библиотеку для устройства, имеющего существенные ограничения как по постоянной, так и по оперативной памяти, я думаю, нецелесообразно.

Целесообразней забрать сразу 50% памяти? Не думаю. Слишком много частных случаев означает только то, что автор библиотеки не интересовался реальным применением, формально подошел к вопросу и получил быдлокод.  С чем и столкнулся ТС.  Его либка слишком ресурсоемка и не обеспечивает эффективной работы с железом.

А ведь все вышеназваные частные случаи укладываются элементарно в одну функцию из либки с такими параметрами: координаты окна, его ширина и высота (причем Y  и  высота с точностю 8 пикселей), тип содержимого (заполненый буфер\PRG\callback\) и void*. Я думаю Вы уже догадались че там дальше ;) 

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

Не догадался.

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

andriano пишет:

Не догадался.

А божеж!

/* Инициализируем окно вывода, задаем положение, размер и режим */
void SSD1306Graphic::InitFrame(byte bLeft, byte bPageTop, byte bWidth, byte bPages, byte bMode, byte* buf)
{
    Canva = buf;
    Width = bWidth;
    Top   = bPageTop;
    Left  = bLeft;
    Pages = bPages;
    UpMode= bMode;
}



/* Перерисовываем окно */
void SSD1306Graphic::update(void)
{
    if(!Canva)
        return;

    sendTWIcommand(SSD1306_MEMORY_ADDR_MODE);
    sendTWIcommand(0x00);

    sendTWIcommand(SSD1306_SET_COLUMN_ADDR);
    sendTWIcommand(Left);
    sendTWIcommand(Left+Width-1);

    sendTWIcommand(SSD1306_SET_PAGE_ADDR);
    sendTWIcommand(Top);
    sendTWIcommand(Top+Pages-1);

    sendStart(SSD1306_ADDR<<1);
    waitForAck();
    writeByte(SSD1306_DATA_CONTINUE);
    waitForAck();

    int t=Width*Pages;

    switch (UpMode)
    {
    case MODE_PGM:
        for (int b=0; b<t; b++,Canva++)		// Send data
        {
            writeByte(pgm_read_byte(Canva));
            waitForAck();
        }
        break;
    case MODE_BUFER:
        for (int b=0; b<t; b++)		// Send data
        {
            writeByte(Canva[b]);
            waitForAck();
        }
        break;
    case MODE_CALLBACK:
        for (int b=0; b<t; b++)		// Send data
        {
            writeByte(((byte (*)(uint16_t))Canva)(b));
            waitForAck();
        }
        break;
    }

    sendStop();

}

А в скетче так

  
void OutGraph()
{
  byte b[FRAME_P*FRAME_W];

  myOLED.InitFrame(FRAME_X, FRAME_PY, FRAME_W, FRAME_P, SSD1306Graphic::MODE_BUFER, b);
  
  memset(b, 0, sizeof(b)); //очищаем канву

 //выводим пиксели, перерисуем потом
  myOLED.Pixel(0,0);
  myOLED.Pixel(1,1);

  myOLED.drawLine(0,2,70,2);
  myOLED.update();
 }

byte Blym;

byte Grid(uint16_t a)
{
  byte r=1; 
  byte y=(a>>4)&0x38; 
  
  if(y && ((a&0x0f) || (Blym&0x1f))) r=0;  // не верх рамки и  мигающая сетка
    switch (y)
    {
      case 0x18: if(!(a&2)) r=0x01; break; //осевая
      case 0x28:   r|=  0x80; break;     // низ рамки
      
    }
    byte x=a&0x7f;
    if((!x)||(x==127))
      return 0xff;
иначе формируем кривую
    return r;
}

void loop()
{

......................

OutGraph();// после выхода ОЗУ свободно

...........................

 //вывод битмапа напрямую из PGM
  myOLED.InitFrame(0, 6, 20, 2, SSD1306Graphic::MODE_PGM, fontAlt);
  myOLED.update();
 
.................................
//рисуем  Grid
     myOLED.InitFrame(0, 1, 128, 6, SSD1306Graphic::MODE_CALLBACK, (byte*)&Grid);
 
    Blym++;//моргаем в   Grid