OLED Вывод информации

sinnpriest
Offline
Зарегистрирован: 16.09.2015

Добрый день.

Вы мне очень помогаете! И вновь приходится обращаться к Вам за помощью по поводу Вывода информации на OLED экран.

Вот такой скетч:

// "Недорогой контроллер на Ардуино нано + TLC5940 + DALLAS-DS18B20"
// шести канальный контроллер, 12-bit шим
// Комплектация: Arduino nano, RTC-modul, TLC5940, DALLAS-DS18B20.
// перед началом компиляции откройте монитор порта в правом верхнем углу окна программы

#include <OLED_I2C.h>         // Подключение библиотеки для дисплея
OLED  myOLED(SDA, SCL, 8);    
extern uint8_t MegaNumbers[]; // Подключение больших шрифтов
extern uint8_t SmallFont[];   // Подключение маленьких шрифтов
extern uint8_t TinyFont[];    // Подключение мини шрифтов

#include "Tlc5940.h"

#include <OneWire.h>
OneWire ds(5); // Температурный сенсор DALLAS-DS18B20 подключен к D5

#include <DS1307.h>
DS1307 rtc(A0, A1); //RTC-модуль подключен к:  SDA - > A0, SCL -> A1
    Time t;

    byte tic[6][5];
    unsigned long Value[6];
    unsigned long Array[6][6];
    unsigned long Event[6][3];
    float power = 100; // общая мощность освещения [0 - 100]
    unsigned long time_all;
    unsigned long last_time_all;
    byte temp = 0;
    unsigned int level; // значение уровня канала в цикле периода (рассвет, закат)
    byte q;
    byte w;
    int nm[6];
    byte v;
    byte ventpin = 6; // управление вентилятором осуществляется с D6
    int i = 0;
    unsigned long day_start;
    unsigned long day_end;
    byte day_spam = 0;

void setup() {
    Serial.begin ( 115200 );  // Указвать скорость на которой работает Ваш COM-порт
    Tlc.init();
    Tlc.clear();

    myOLED.begin();
    myOLED.clrScr();
    
    pinMode (ventpin, OUTPUT);
    
  rtc.halt(false);
/* MONDEY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
   Три строчки ниже необходимо раскомментировать (убрать двойную дробь) при стартовой загрузки программы.
   Сразу после первой загрузки, нужно закомментировать данные строки и 
   обращаться к этой процедуре в случае сбоя в работе часов. RTC-модуль запомнит время. */
// rtc.setDOW(TUESDAY);     // изменить день недели. примеры указаны на четыре строчки выше
// rtc.setTime(17, 04, 0);   // время
// rtc.setDate(13, 10, 2015);  // дата

// Настройки светильника: <время восхода>, <продолжительность рассвета>, <мощность канала в течении "дня">, <время заката>, <продолжительность заката>
// первый канал
Array[0][0] = 540; // начало рассвета первого канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[0][1] = 541; // конец рассвета первого канала. например: 11:15 записывается как <675> (в минутах)
  Array[0][2] = 1050; // начало заката первого канала. например: 20:50 записывается как <1250> (в минутах)
   Array[0][3] = 1080; // окончание заката первого канала. например: 21:50 записывается как <1310> (в минутах)
    Array[0][4] = 100; // мощность первого канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[0][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика
// второй канал
Array[1][0] = 540; // начало рассвета второго канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[1][1] = 545; // конец рассвета второго канала. например: 11:15 записывается как <675> (в минутах)
  Array[1][2] = 1050; // начало заката второго канала. например: 20:50 записывается как <1250> (в минутах)
   Array[1][3] = 1080; // окончание заката второго канала. например: 21:50 записывается как <1310> (в минутах)
    Array[1][4] = 80; // мощность второго канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[1][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика
// третий канал
Array[2][0] = 540; // начало рассвета тертьего канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[2][1] = 550; // конец рассвета тертьего канала. например: 11:15 записывается как <675> (в минутах)
  Array[2][2] = 1050; // начало заката тертьего канала. например: 20:50 записывается как <1250> (в минутах)
   Array[2][3] = 1080; // окончание заката тертьего канала. например: 21:50 записывается как <1310> (в минутах)
    Array[2][4] = 80; // мощность тертьего канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[2][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика
// четвертый канал
Array[3][0] = 540; // начало рассвета четвертого канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[3][1] = 555; // конец рассвета четвертого канала. например: 11:15 записывается как <675> (в минутах)
  Array[3][2] = 1050; // начало заката четвертого канала. например: 20:50 записывается как <1250> (в минутах)
   Array[3][3] = 1080; // окончание заката четвертого канала. например: 21:50 записывается как <1310> (в минутах)
    Array[3][4] = 80; // мощность четвертого канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[3][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика
// пятый канал
Array[4][0] = 540; // начало рассвета пятого канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[4][1] = 560; // конец рассвета пятого канала. например: 11:15 записывается как <675> (в минутах)
  Array[4][2] = 1050; // начало заката пятого канала. например: 20:50 записывается как <1250> (в минутах)
   Array[4][3] = 1080; // окончание заката пятого канала. например: 21:50 записывается как <1310> (в минутах)
    Array[4][4] = 100; // мощность пятого канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[4][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика
// шестой канал
Array[5][0] = 540; // начало рассвета шестого канала в минутах. например: 10:00 записывается как <600> (в минутах)
 Array[5][1] = 570; // конец рассвета шестого канала. например: 11:15 записывается как <675> (в минутах)
  Array[5][2] = 1050; // начало заката шестого канала. например: 20:50 записывается как <1250> (в минутах)
   Array[5][3] = 1080; // окончание заката шестого канала. например: 21:50 записывается как <1310> (в минутах)
    Array[5][4] = 100; // мощность шестого канала в процентах в течении "дня" (в процентах). не может быть больше 100
     Array[5][5] = 0; // инвертор драйвера. Имеет значения 0 и 1. 0 - прямая логика, 1 - обратная логика

// параметры каналов
nm[0] = 395;
 nm[1] = 420;
  nm[2] = 452;
   nm[3] = 475;
    nm[4] = 495;
     nm[5] = 6500;
     
// Перевод массива в секунды
  for (q=0; q<6; q++) {
    for (w=0; w<4; w++) {
      Array[q][w] = Array[q][w] * 60;
    }
  }

// Определение параметров экрана для перехода в screen saver
day_start = Array[0][0];
 for (q=1; q<6; q++) {
  day_start = min (day_start, Array[q][0]);
 }
day_start = day_start - 60;  //screen saver отключается за 60 секунд до рассвета

day_end = Array[0][3];
 for (q=1; q<6; q++) {
  day_end = max (day_end, Array[q][3]);
 }
day_end = day_end + 60;  //screen saver включается после 60 секунд от заката

// Сброс суточного массива
  for (q=0; q<6; q++) {
    for (w=0; w<5; w++) {
      tic[q][w] = 0;
    }
  }

// Вывод проверочной стартовой таблицы
  
  Serial.println ( F("" ));Serial.println ( F("" ));
  Serial.print ( F("  Time: " )); Serial.print ( rtc.getTimeStr() );  Serial.println ( F("   <- Check this after reset." ));Serial.println ( F("" ));
  Serial.println ( F("  Chanal  :  Sanrise  :   Day     :  Sanset   : Night     :  Led chanal: Inverse" ));
  
  for (q=0; q<6; q++) {
   
     Serial.print ( F("  PWM" ));
      Serial.print ( q+1 );
     for (w=0; w<5; w++) {
       Serial.print ( F("    :  " ));
         if (w < 4) {
           if ( ( Array[q][w]/3600 ) < 10) {Serial.print ( F("0" )); Serial.print ( Array[q][w]/3600 );
           } else {Serial.print ( Array[q][w]/3600 );}
           Serial.print ( F(":" )); 
           if ((( Array[q][w]-((Array[q][w]/3600)*3600))/60) < 10) {Serial.print ( F("0" ));Serial.print (( Array[q][w]-((Array[q][w]/3600)*3600))/60 );
           } else {Serial.print (( Array[q][w]-((Array[q][w]/3600)*3600))/60 );}
         } else {
              if (Array[q][w] < 100) {Serial.print ( F(" "));}
              Serial.print ( Array[q][w]);Serial.print ( F("%"));Serial.print ( F("      :   "));
              
              Serial.print ( Array[q][w+1]);
                }
     }
     Serial.println ( F("" ));
  }
      Serial.println ( F("-------------------------------------------------" ));
      
      // Проверка корректности ввода исходных данных
      for (q=0; q<6; q++) {if (Array[q][4] >= 86400) {Array[q][4] = 86399; Serial.print ( F("Error, check PWM" )); Serial.print ( q+1 ); Serial.println ( F(", fixed the end of the sunset to 24:00" ));}}
      for (q=0; q<6; q++) {if (Array[q][5] >= 101) {Array[q][5] = 100; Serial.print ( F("Error, check PWM" )); Serial.print ( q+1 ); Serial.println ( F(", fixed the power led to 100%" )); }}
  for (q=0; q<6; q++) {
    for (w=0; w<3; w++) {
 if ( Array[q][w] > Array[q][w+1] ) {Serial.print ( F("Error, check PWM" )); Serial.print ( q+1 ); Serial.println ( F(", not continuity sanrise and sanset. Controller will not work correctly. Check setup. " ));
 }

    }
  }
  Serial.print ( F("  All powerful LEDs: " ));  Serial.print ( power ); Serial.println ( F("%" ));
  Serial.println ( F("  In service. OK." ));



// Изменение мощности из процентов в значение
  for (q=0; q<6; q++) {
     Value[q] = map (Array[q][4], 0, 100, 0, 4095) * (power/ 100);
  }

}

void loop() {
  

  
  // Константы температуры
  float tmin = 26.00; // минитальное значение на датчике, при достижении которого запускается вентилятор. Указывается значение в градусах. Например: "32", означает 32градуса цельсия
  float tmax = 36.00; // максимальное значение на датчике, служит для плавного увеличения скорости вращения вентилятора. Указывается значение в градусах. Например: "36", означает 36градуса цельсия. На превышающих значениях вентилятор работает на 100% мощности

  // Блок программы работающий с температурным датчиком и вентилятором
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius;

ds.search(addr);
ds.reset(); 
ds.select(addr);
ds.write(0x44, 1);        
ds.reset();
present = ds.reset();
ds.select(addr);    
ds.write(0xBE);
for ( i = 0; i < 9; i++) {           
    data[i] = ds.read();
      }

  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; //
    if (data[7] == 0x10) {
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;  
    else if (cfg == 0x20) raw = raw & ~3; 
    else if (cfg == 0x40) raw = raw & ~1; 

  }
  celsius = (float)raw / 16.0;
      if (temp == 0) {
          Serial.print(F("  Temperature = "));
          Serial.print(celsius);
          Serial.println(F(" Celsius \n "));
          temp = 1;}
     if ( tmin > celsius )analogWrite (ventpin, 0); 
     if ( tmin <= celsius && tmax >= celsius ) analogWrite (ventpin, map(celsius, tmin , tmax, 0, 255));
     if ( tmax < celsius) analogWrite (ventpin,255);

      
        
// конец блока температуры

    String stringOne = rtc.getTimeStr();
    t = rtc.getTime();
     int long h = t.hour;
     int long m = t.min;
     int long s = t.sec;
     time_all = ( (h * 3600)+(m * 60)+ s  );



// Контроллер

    for (q=0; q<6; q++) {
      
     if (time_all >= 0 && time_all < Array[q][0] && tic[q][0] == 0) { 
          
         if (Array[q][5]==0)Tlc.set (q, 0);
         if (Array[q][5]==1)Tlc.set (q, 4095);
         tic[q][0] = 1;
         Serial.print ( rtc.getTimeStr() );Serial.print ( F(" PWM:" ));Serial.print ( q+1 ); Serial.println ( F(" - mon" ));
         }
     if (time_all >= Array[q][0] && time_all <= Array[q][1] ) { 
          
          if (tic[q][1] == 0){Event[q][0] = (time_all - Array[q][0])*1000;
                              Event[q][1] = ((Array[q][1]-Array[q][0])*1000);
                              Event[q][2] = millis();
                              Serial.print ( rtc.getTimeStr() );Serial.print ( F(" PWM:" ));Serial.print ( q+1 ); Serial.println ( F(" -  on" ));
                              tic[q][1] = 1;}
         level = (((float)millis() - Event[q][2]+Event[q][0])*Value[q])/(Event[q][1]);
         if(level <= 0) level = 0; if(level >= Value[q]) level = Value[q];
         if (Array[q][5]==0)Tlc.set (q, level);
         if (Array[q][5]==1)Tlc.set (q, 4095-level);
         Serial.println ( level );
         }
     if (time_all > Array[q][1] && time_all < Array[q][2] && tic[q][2] == 0) { 
         
         if (Array[q][5]==0)Tlc.set (q, Value[q]);
         if (Array[q][5]==1)Tlc.set (q, 4095-Value[q]);
          tic[q][2] = 1 ;
         Serial.print ( rtc.getTimeStr() );Serial.print ( F(" PWM:" ));Serial.print ( q+1 ); Serial.println ( F(" - day " ));
         }
     if (time_all >= Array[q][2] && time_all <= Array[q][3] ) { 
         if (tic[q][3] == 0){ Event[q][0] = (time_all - Array[q][2])*1000;
                              Event[q][1] = ((Array[q][3]-Array[q][2])*1000);
                              Event[q][2] = millis();
                              Serial.print ( rtc.getTimeStr() );Serial.print ( F(" PWM:" ));Serial.print ( q+1 ); Serial.println ( F(" -  off" ));
                              tic[q][3] = 1;}
         level = (((float)millis() - Event[q][2]+Event[q][0])*Value[q])/(Event[q][1]);
         if(level <= 0) level = 0; if(level >= Value[q]) level = Value[q];
         if (Array[q][5]==0)Tlc.set (q, Value[q]-level);    // Закат сделан по принципу рассвета.
         if (Array[q][5]==1)Tlc.set (q, 4095-Value[q]+level);         // Разница в этих двух строках.
         Serial.println ( Value[q]-level );
         }
     if (time_all > Array[q][3] && time_all <= 86399 && tic[q][4] == 0) { 
         
         if (Array[q][5]==0)Tlc.set (q, 0);
         if (Array[q][5]==1)Tlc.set (q, 4095);
          tic[q][4] = 1 ;
         Serial.print ( rtc.getTimeStr() );Serial.print ( F(" PWM:" ));Serial.print ( q+1 ); Serial.println ( F(" - nig" ));
         }
    }  
Tlc.update();



// Сброс состояния контроллера на новый день.
              if (time_all < last_time_all) {
                   // Сброс суточного массива
                   for (q=0; q<6; q++) {
                   for (w=0; w<5; w++) {
                   tic[q][w] = 0;
                   }
                   }
              }



// Блок вывода информации на экран
if ( time_all - last_time_all == 1) {

 if ( time_all >= day_start && time_all <= day_end) {
    myOLED.clrScr();
    myOLED.setBrightness(100);
    myOLED.setFont(SmallFont);
    myOLED.print(rtc.getTimeStr(), 0, 0);
    myOLED.print( "t:", 88, 0);
    myOLED.print(String(int (celsius)), 95, 0);
 

    myOLED.setFont(TinyFont);
    myOLED.print(String(int (power)), 0, 10); if (power == 100)myOLED.print("%", 12, 10); else myOLED.print("%", 10, 10);
    myOLED.print(String(int (power/2)), 0, 33); myOLED.print("%", 10, 33);
    
    myOLED.print("fan:", 55, 1);
    v = map(celsius, tmin , tmax, 0, 100);
    if (v >= 0 && v <= 100) {
      myOLED.print(String(int (v)), 72, 1);
        if (v <= 9) myOLED.print("%", 76, 1);
        if (v >= 10 && v <= 99) myOLED.print("%", 80, 1);
        if (v == 100) myOLED.print("%", 85, 1);} else myOLED.print("off", 72, 1);
 

   for (q=0; q<6; q++) {
   
    myOLED.drawLine(((q+1)*18)-1, 11, ((q+1)*18)+1, 11);  // разметка
    myOLED.drawLine(((q+1)*18)-1, 33, ((q+1)*18)+1, 33);  // разметка
    myOLED.drawLine(((q+1)*18), 55, ((q+1)*18), 63);      // разметка
    myOLED.print(String(nm[q]), 4+((q+1)*18), 58);        // подписи nm
    
          if (time_all >= Array[q][0] && time_all <= Array[q][1]) { level = ((time_all - Array[q][0]) * ((Array[q][4]*44)/100)) / (Array[q][1] - Array[q][0]);}
          if (time_all >= Array[q][1] && time_all <= Array[q][2]) { level = ((Array[q][4]*44)/100);}
          if (time_all >= Array[q][2] && time_all <= Array[q][3]) { level = (((Array[q][4]*44)/100) * (Array[q][3] - time_all)) / (Array[q][3] - Array[q][2]);}
          if (time_all <= Array[q][0] || time_all >= Array[q][3]) { level = 0;}

   
   for (w=0; w<8; w++) {
    myOLED.drawLine(((q+1)*18)+w+5, 55 - level, ((q+1)*18)+w+5, 55);
   }
   }
   } else {         // Ночные настройки
      myOLED.clrScr();
      myOLED.setBrightness(0);
      myOLED.setFont(MegaNumbers);
  myOLED.print(stringOne.substring(0, 2), 4, 12);
  switch (day_spam)
  {
    case 0:
      myOLED.print("/", 51, 12);
      day_spam = 1;
      break;
    case 1:
      myOLED.print("-", 51, 12);
      day_spam = 0;
      break;}   
   myOLED.print(stringOne.substring(3, 5), 75, 12);
   }
   
 myOLED.update();
}

last_time_all = time_all;
}

Вот это выдает после компилляции:

Sketch uses 22 958 bytes (74%) of program storage space. Maximum is 30 720 bytes.
Global variables use 1 791 bytes (87%) of dynamic memory, leaving 257 bytes for local variables. Maximum is 2 048 bytes.
Low memory available, stability problems may occur.

По сути вопрос только по выводу информации на экран: с 320строки.

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

Вот тут, где обведено красным была информации, но она теперь не выводится.

Заметил, что информация не выводится в таких выражениях:

myOLED.print(String(int (celsius)), 95, 0);

myOLED.print(stringOne.substring(3, 5), 75, 12);

А в таких выводится без проблем:

myOLED.print("fan:", 55, 1);

 

Прошу, подскажите, с чем это может быть связано? Возможно с отсутсвием памяти, т.к. если сделать отдельный скетч под вывод, он работает без проблем. Как сделать так, чтоб вывод работал?

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

Ну, не пишет со String понятно почему - не хватает памяти для создания String.

Попробуйте хоть немного экономить память. Ну, например, не пихать String везде куда не попадя (а лучше вообще без него обойтись). И там много по программе мест, где можно сэкономить. например, так ли обязательно массиву Array иметь тип long. Вы уверены, что он в int не помещается? Особенно, если его арифметику аккуратно прописать?

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

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

В общем, надо садиться и оптимизировать использование памяти.

Кроме того, ровно половину всей доступной памяти у Вас кушает массив scrbuf[1024] в библиотеке OLED_I2C, но от него избавиться не так легко - нужно использовать другую библиотеку.

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

18-я строка.

ЗАЧЕМ ТАК ДЕЛАТЬ?!

sinnpriest
Offline
Зарегистрирован: 16.09.2015

Спасибо большое.

Действительно, освобождение памяти все решило.

"Век учись, век живи, все равно дураком помрешь..." (с)

Sloper
Sloper аватар
Offline
Зарегистрирован: 30.03.2015

ЧТо бы сэкономить память (dinamic memory), очень помагает макрос F() в строках.

Например:

Serial.println(F("This string will be stored in flash memory"));

Вместо:

Serial.println("This string will be stored in dinamic memory");