• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
optymalizacja kodu
#1
Witajcie
Chciałbym prosić bardziej doświadczonych formumowiczów o pomoc w optymalizacji kodu.
Jest to moja kolejna wersja/modernizacja sterownika akwarystycznego. Poprzednia bazowała na wyświetlaczu LCD 64x20 i generalnie było OK.  Na chwilę obecną chciałem rozbudować ten sterownik o parę nowych opcji plus możliwość sterowania z pozycji klawiszy zamiast za każdym razem wgrywania kodu.
Moje obecnie problemy to zbyt wolno działający kod co widać szczególnie w godzinach gdzie sekundy przeskakują co 7 sekund :-( . temperatury a raczej cyfry są zniekształcone (czasami).
Całość jakoś działa ale moim celem jest dodanie do programu menu z pozycji którego mógłbym ustawiać:
- bieżącą godzinę i datę (jak się rozkoduje zegar),
- zmieniać godzinę/ minuty świtu oraz zmierzchu,
- ustawiać temperaturę dla której miałby się włączać wentylator (plus histereza),
- wprowadzać wartość KH wg. której będzie wyliczana zawartość Co2.

Całość obsługiwana jest przez arduino nano (ze względu na rozmiar),
wyświetlacz to graficzny LCD 128x64 ST7920,
termometry dwa na jednym kablu w funkcji złodzieja (bez pełnego zasilania),
obsługa dwóch styczników (na razie sterowany jest tylko jeden - drugi w zapasie),
zegar RTC DS1307, Sonda pH,
i do tego przetwornica step-down,
klawisze na chwilę obecną podpięte do pinów 9,8,7,4.


Załączone pliki
.zip   sterownik_aqua_v2.zip (Rozmiar: 55.98 KB / Pobrań: 6)
 
Odpowiedź
#2
Wróć do przykładu blinkWhithoutDelay i zerknij tutaj https://forum.arduinopolska.pl/watek-nak...a-przez-bt, zastanów się jak często musisz wykonywać poszczególne rzeczy i rozdziel między nie czas procesora. Np. mierzenie temperatury, można robić co 10s, odpowiednio robione zajmie 27 + 3ms, jak robisz to zgodnie z najprostszym przykładem i co 1s to zajmie 7,5s w każdych dziesięciu. Czasami mierzenie trzeba robić co 0,1s, ale wtedy przy ds trzeba zrezygnować z 12bitów. Do tego parę rzeczy robionych wg takiej fizolowi z delayami i czekasz 7s na zmianę wartości na ekranie. Właściwie OK masz napisaną funkcję drawPH(void). Powiel to na inne. A wywoływać poszczególne pomiary w 1, 2, 3 sekundzie, tak by w między czasie była moc obliczeniowa na inne rzeczy. Do ustawień potrzebne jest menu, temat rzeka. Trzeba też poznać dobrze biblioteki, np. domyślnie funkcja DS czeka na skończenie pomiaru i jej wywołanie trwa 750ms. Trzeba włączyć tryb asynchroniczny i samemu dbać o to kiedy pomiar zlecić, a kiedy odczytać.  Całości  nie przetestuję, nie mam tego sprzętu.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#3
(13-03-2018, 04:37)kaczakat napisał(a): Wróć do przykładu blinkWhithoutDelay i zerknij tutaj https://forum.arduinopolska.pl/watek-nak...a-przez-bt, zastanów się jak często musisz wykonywać poszczególne rzeczy i rozdziel między nie czas procesora. Np. mierzenie temperatury, można robić co 10s, odpowiednio robione zajmie 27 + 3ms, jak robisz to zgodnie z najprostszym przykładem i co 1s to zajmie 7,5s w każdych dziesięciu. Czasami mierzenie trzeba robić co 0,1s, ale wtedy przy ds trzeba zrezygnować z 12bitów. Do tego parę rzeczy robionych wg takiej fizolowi z delayami i czekasz 7s na zmianę wartości na ekranie. Właściwie OK masz napisaną funkcję drawPH(void). Powiel to na inne. A wywoływać poszczególne pomiary w 1, 2, 3 sekundzie, tak by w między czasie była moc obliczeniowa na inne rzeczy. Do ustawień potrzebne jest menu, temat rzeka. Trzeba też poznać dobrze biblioteki, np. domyślnie funkcja DS czeka na skończenie pomiaru i jej wywołanie trwa 750ms. Trzeba włączyć tryb asynchroniczny i samemu dbać o to kiedy pomiar zlecić, a kiedy odczytać.  Całości  nie przetestuję, nie mam tego sprzętu.
Dzięki za sugestię co i gdzie szukać  - będę dzisiaj walczył z tematem. Z tego co rozumię to funkcja delay jest najgorszym rozwiązaniem i raczej starać się jej używać jak najmniej? 
Menu - i tu jest problem - bo niby jak zauważyłeś temat rzeka ale tak naprawdę nigdzie nie ma dostępnych różnych konstruktów na bazie których można by się oprzeć i coś zmajstrować (jakie komendy/funkcje, co za co odpowiada i kiedy użyć) - do tego dochodzi sterowanie takim wyświetlaczem jak ten 128x64 ST7920 i jak dla mnie problem gotowy :-).
 
Odpowiedź
#4
Najprościej dane zaktualizować wysyłając je komendą przez UART. Podłączanie kablem nie jest koniczne, teraz w każdym domu jest laptop z BT, telefon z BT (a na androida mnóstwo terminali, nakładek Arduino które wyślą Ci całą komendę po wpisaniu danych w okienka) moduł HC-05 to koszt 20zł, 10zł z Chin. W bibliotece do zegarka by Eric Ayars jest przykład z ustawieniem czasu przez Uart, wystarczy go wysłać w formacie YYMMDDwHHMMSS.
Ja to robię inaczej, przykład zabawy z silniczkiem krokowym:

Kod:
#include <AccelStepper.h>
#define HALFSTEP 8
//http://42bots.com/tutorials/28byj-48-stepper-motor-with-uln2003-driver-and-arduino-uno/

// Motor pin definitions
#define motorPin1  2     // IN1 on the ULN2003 driver 1
#define motorPin2  3     // IN2 on the ULN2003 driver 1
#define motorPin3  4     // IN3 on the ULN2003 driver 1
#define motorPin4  5     // IN4 on the ULN2003 driver 1
#define onerev  4076    // jeden pelny obrot dla silnicza z przekladnia
#define hourestep onerev/12 //obrot o 1/12
#define LED 13
#define buffsize 32
char input[buffsize];

// Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
unsigned long czaspoprzedni;
uint8_t minuta, sekunda, setka, flagaminuta, flagasekunda, flagasetka;
uint8_t interwal = 100;
int16_t dane [4];
byte index = 0;
boolean stringComplete = false;  // whether the string is complete
int16_t maxspeed = 600, maxtime = 1800;

void inline parsujpolecenia();

void setup() {
 Serial.println("cmd=moveto,speed,maxspeed,acceleration");
 stepper1.setMaxSpeed(800.0);
 stepper1.setAcceleration(500.0);
 stepper1.setSpeed(100);
 stepper1.moveTo(onerev);
 Serial.begin (115200);
 pinMode(LED, OUTPUT);
}//--(end setup )---

void loop() {
 unsigned long czasteraz = millis();
 stepper1.run();
 if (stringComplete) parsujpolecenia();

 if ( czasteraz - czaspoprzedni >= interwal)
 {
   czaspoprzedni = czasteraz;
   flagasetka = 1;
   setka++;
 }
 if (setka >= 10)
 {
   setka = 0;
   flagasekunda = 1;
   sekunda++;
   Serial.println(stepper1.currentPosition());
   if (stepper1.distanceToGo() == 0)
   {
     // stepper1.moveTo(stepper1.currentPosition()+hourestep);
   }
   digitalWrite(LED, !digitalRead(LED));
 }
 if (sekunda >= 60)
 {
   sekunda = 0;
   flagaminuta = 1;
   minuta++;
 }
 if (flagaminuta)
 { flagaminuta = 0;
   stepper1.moveTo(0);
 }
}

void serialEvent() {
 while (Serial.available() > 0) {

   char aChar = Serial.read();
   if (aChar == '\n')
   {
     // End of record detected. Time to parse
     input[index] = 0;
     index = 0;
     stringComplete = true;
   }
   else
   {
     input[index] = aChar;
     if (index < buffsize) index++;
     input[index] = '\0'; // Keep the string NULL terminated
   }
 }
}


void inline parsujpolecenia()
{
 uint8_t index = 0;
 char * polecenie = input;
 Serial.print("Otrzymane polecenie: ");
 Serial.println(polecenie);

 char* command1 = strtok(polecenie, "=");

 if (strcmp(command1 , "cmd") == 0) {
   while (command1 != 0)
   {
     Serial.println(command1);
     command1 = strtok(NULL, ",");
     if (index < 4) dane[index] = atoi(command1);
     index++;
   }
   stepper1.moveTo(stepper1.currentPosition() + dane[0]);
   if (dane[2] > 0 && dane[2] < 800) stepper1.setMaxSpeed(dane[2]);
   if (dane[1] > 0 && dane[1] < stepper1.maxSpeed()) stepper1.setSpeed(dane[1]);
   if (dane[3] > 0 && dane[3] < 800) stepper1.setAcceleration(dane[3]);
 }
 else Serial.println("Polecenie nieprawidlowe");
 Serial.println("Aktualne parametry:");
 Serial.print("Go to   = ");
 Serial.println( stepper1.targetPosition());
 Serial.print("runSpeed =   ");
 Serial.println( stepper1.runSpeed());
 Serial.print("maxSpeed =   ");
 Serial.println(stepper1.maxSpeed());
 Serial.print("speed = ");
 Serial.println(stepper1.speed());

 stringComplete = false;
}
Opis różnych funkcji do parsowania jest w tym wątku: https://forum.arduinopolska.pl/watek-sterowanie-przeka%C5%BAnikiem-za-pomoc%C4%85-sms-arduino-uno-modu%C5%82-gsm
Delay jest dla ludzi jak każda inna funkcja, jeśli wiesz, że wysyłając komendę do modemu masz czekać 2s i nic w tym czasie nie zamierzasz robić to wstaw delay, ale możesz też napisać funkcję jak do odczytu ph, funkcja jest uruchamiana za każdym obiegiem pętli loop, ale nie jest wykonywana jej "czasochłonna" część częściej niż wymagany czas. Tak samo można zrobić sobie funkcję temp(). Wywołujesz funkcję, dajesz rozkaz pomiaru temperatury, odpalasz tu timer programowy na zmiennych static i ustawiasz flagę (też zmienna static), że zlecono pomiar, wychodzisz z funkcji. Funkcja jest wywoływana przez kolejne obiegi pętli ale nic już nie robi (no mierzy tylko czas), ustawiona flaga zleconego pomiaru i brak flagi odliczenia czasu timerem powoduje wyjście (jakiś if przed rozkazem pomiaru temperatury, jakiś if przed rozkazem odczytu temperatury) .  W czasie tych 750ms zostanie pewnie wywołana tysiące razy, tak samo inne funkcje pętli loop, jak naciśniesz jakiś przycisk lub zmieni się sekunda jest czas by coś z tym zrobić natychmiast. Po którymś sprawdzeniu czas minął, w końcu ustawiasz flagę czasu, odczytujesz temperaturę zerujesz flagę zleconego pomiaru i albo zlecasz kolejny pomiar albo blokujesz tak samo tę funkcję na 9,25s lub inny potrzebny interwał czasu.  DS18B20 nigdy nie będzie rajdówką, nawet odczyt analogowy arduino to około 100us vs 27ms odczytu z DS, za to komunikacja jest cyfrowa, jak ktoś urwie kabel, pomiar jest zły to od razu wiadomo.  Jak zmienisz płytkę na DUE/Bluepill/ESP8266 to tak pisany program od razu zauważy dodatkowego kopa, millis() i micros() będą działać tak samo, a pętla zamiast być wykonana tysiąc razy na 8bitowym procku 16MHz będzie wykonywana miliony razy na procku 32bitowym  160MHz.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#5
teoretycznie wydaje się to proste i rozumiem o mechanizm działania ale ni czorta nie potrafię tego przełożyć na kod. Po kilku próbach nie uciekłem z interwału 7 sekund na zegarze i obawiam się, że zaśmieciłem sobie kod. Dzięki za wyjaśnienie. Mam jeszcze jedno pytanie odnośnie zegara. A mianowicie - obsługa LCD wymaga stosowania bloków void draw (void), void drawCzas(void) itd. Aby pomóc w obliczeniach rozbiłem główny blok z czasem na blok zegara void drawCzas(void) i blok sterowania PWM void drawPWM (void). W obu blokach muszę wywołać:
< tmElements_t tm; > gdyż inaczej nie zadziała program w bloku. Czy jest inny sposób (deklaracji) na początku programu aby nie powtarzać tej czynności w każdym bloku tylko odwołanie samo zadziała? Odnoszę wrażenie, że przez to dublowanie sam zegar jest wyświetlany z tym opóźnieniem 7s.
 
Odpowiedź
#6
Dla czego musisz wywoływać cośtam?
Tworzysz sobie taki obiekt z obiektu, który dla niepoznaki nazywa się w języku noobów inicjalizacją.
tmElements_t, jest już obiektem typu zdefiniowanego struktury anonimowej:
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday;
uint8_t Day;
uint8_t Month;
uint8_t Year;
} tmElements_t, TimeElements, *tmElementsPtr_t;

Struktury anonimowe, to taki wymysł GCC, który normalnie nie powinien istnieć, bo według mnie właśnie są z nim takie problemy, jak powyższy.
Nie możesz zadeklarować obiektu tm poza funkcją go używającą.
tm pobiera z zegara zmienną czasu i rozkłada ją na składowe, przypisując do zmiennych wewnątrz struktury, a potem sobie pobierasz tm.Minute,
tm.Second
Można oczywiście kombinować.
Ja bym pogrzebał w pliku TimeLib.h i zrobił z tego normalną strukturę albo klasę.
Wywołanie obiektu pobierało by aktualny czas, bo teraz linijka tmElements_t tm przypisuje czas do tm i czas się nie zmienia aż do następnej "inicjalizacji" zegara, czy tam obiektu go obsługującego.

A co się tyczy kodu, to deklarujesz sobie znak stopni Celsiusza, a biblioteka U8Glib ma sporo znaków specjalnych.
Więc zamiast tej bitmapy piszesz "\xb0".
Na stronie https://github.com/olikraus/u8glib/wiki/...upadobex11 masz tablice znaków.
Jak byś miał problem z ich użyciem, to mogę wyjaśnić.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#7
A cha, i jeszcze jedno.
Nie powinno się odświeżać ekranu, jeśli to nie jest konieczne.
Odświeżaj tylko elementy które zostały zmienione.
Na przykład, w Twoim przypadku, powinieneś odświeżać tylko zegar.
Nie musisz sprawdzać millis, bo mając RTC to było by dziwne, że nie korzystasz akurat z niego.
Jak chcesz aby parametry odświeżały się co 10 sekund, to w funkcji wyświetlającej czas, sprawdzaj czy minęło 10 sekund i wywołuj funkcję sprawdzającą te parametry i je wyświetlającą.
Dzięki temu nie musisz liczyć millis, tylko liczysz tiki z funkcji tm.Second.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości