-
Data: 2017-11-01 03:37:36
Temat: Re: programowanie w C - bardzo ogólne pytanie o filozofię. Arduino w roli programatora pralki
Od: Jacek Radzikowski <j...@s...die.die.die.piranet.org> szukaj wiadomości tego autora
[ pokaż wszystkie nagłówki ]On 10/23/17 17:07, ToMasz wrote:
> witam
> bardzo bardzo hobbisticznie zajmuję się arduino. w wolnych chwilach,
> żeby synowi zaszczepić.... Nie. Żeby się nie bał elektroniki. Wpadłem na
> pomysł aby pralkę w której umarła elektronika, naprawić za pomocą
> arduino. Tyle ze ja pokolenie temu programowałem zx spectrum, więc
> jestem skażony na całe życ[...]
Ja bym zaczął od porządnego zdefiniowania problemu: Każde z zadań składa
się z serii kroków, a każdy krok ma określony zestaw wyjść, warunek jaki
musi być spełniony żeby przejść do kolejnego kroku, oraz numer
następnego kroku. Te wszystkie informacje da się opisać jako w miarę
prostą strukturę:
typedef WarunekWyjścia bool (*f)();
struct {
int nastepnyKrok;
int wyjscia[liczbaWyjsc];
int czasTrwania;
WarunekWyjscia funkcjaTestujaca;
} KrokOperacji;
Co oznaczają pola w strukturze:
* nastepnyKrok - to jest indeks w tabeli opisującej zestaw kroków dla
danej operacji, do którego kroku przejeść po spełnieniu warunku wyjścia
dla danego kroku.
* wyjścia - Tabela opisująca stan wyjść dla danego kroku. W najprostszym
przypadku to mogą być wartości jakie należy wysłać na piny
mikrokontrolera: np. pierwszej pozycji w tabeli odpowiada pin sterujący
wolnymi obrotami w lewo. Druga pozycja ro wolne obroty w prawo. Trzecia
to włączenie grzałki, itd. "Wyjścia" mogą też uruchamiać funkcje
wykonujące bardziej skomplikowane operacje. Wszystko zależy od
implementacji.
* czasTrwania - Jeśli ustawione jest na wartość większą od zera, to mówi
przez jak długo ma trwać dany krok. Jeśli ostawione jest na zero, to
wywoływana jest funkcja testująca warunek stopu.
* funkcjaTestująca - funkcja zwracająca true jeśli pralka osiągnęła stan
kiedy można przejść do kolejnego kroku, albo false, jeśli trzeba czekać
dalej (np. sprawdzanie czy temperatura wody osiągnęła wymaganą
temperaturę, albo czy pralka jest napełniona wodą.)
Opis każdej operacji będzie zawarty w tabeli takich struktur,
składającej się w tylu wierszy, ile jest kroków:
struct KrokOperacji operacjaPranie[] = {
{1, {0, 0, 0, 1, .... }, 0, czekajNaNapełnienieBębna}, // napełnij
beben pralki wodą
{2, {1, 0, 0, 0, .... }, 10, NULL}, //kręć powoli silnikiem w lewo
...
};
struct KrokOperacji operacjaWirowanie[] = {
.....
};
itd.
Na pierwszy rzut oka to może wyglądać jak niepotrzebne komplikowanie
sobie życia, ale w rzeczywistości to przekłada się na olbrzymie
uproszczenie programu sterującego, który nie zależy od tego jak są
zdefiniowane operacje. Zmiana w opisie operacji pociągnie za sobą
jedynie zmianę tabeli ja opisującej, bez potrzeby modyfikowania kodu.
Główna pętla programu sterującego będzie wyglądać mniej-więcej w taki
sposób:
void loop() {
switch(operacja) {
case 0: // czekaj na wybranie programu
operacja = wybórOperacji();
krok = 0;
break;
case 1: // pranie
//Funkcja zwróci numer kroku do którego należy przejść. Jeśli
krok = nastepnyKrok(operacjaPranie, krok)
// krok < 0 oznacza koniec operacji i wracamy do wyboru
programu
if(krok < 0)
operacja = 0
break;
case 2: // pranie
krok = nastepnyKrok(operacjaWirowanie, krok)
if(krok < 0)
operacja = 0
break;
... // pozostałe operacje
default:
operacja = 0; // kiedy operacja nie jest zdefiniowana,
powróć do wyboru programu
}
}
//Funkcja sprawdzająca czy użytkownik che uruchomić jakąś operację, i
zwracająca jej indeks.
int wybórOperacji() {
if(wybranePranie)
operacja = 1;
else if (wybraneWirowanie)
operacja = 2;
else ....
....
else
operacja = 0; //w pozostałych przypadkach zawsze wróć do wyboru
operacji
return operacja
}
Tak implementacja głównej pętli jest bardzo rozwlekła, ale zależało mi
na pokazaniu jak ręcznie obsłużyć każdy z programów. Kosztem
niewielkiego zwiększenia skomplikowania tabeli opisującej programy, i
(pozornego) zmniejszenia czytelności głównej pętli, można ją mocno skrócić.
I teraz chyba najważniejsza funkcja programu, wyliczająca następny krok
programu. Jeśli nie nadszedł czas na przejście do następnego, to zwraca
numer obecnego kroku
int nastepnyKrok(struct KrokOperacji *opis, int krok)
{
// jeśli przerywamy program, to wróć do wyboru operacji
if(przerwanieProgramu)
{
zatrzymajPralkę();
return -1;
}
opisKroku = opis[krok]; //opis wykonywanego kroku
//Ustaw wyjścia dla aktualnego kroku.
//Ustawianie wyjść tutaj ma tę zaletę, ze pożądany stan wyjśc będzie
ustawiany przy każdym obiegu pętli. Wadą jest to że ustawianie wyjść
jest wywoływane przy każdym obiegu pętli, co w wyjątkowych sytuacjach
może mieć niepożądane następstwa.
ustawWyjścia(opisKroku.wyjścia);
//jeśli obecna operacja ma trwać określony czas, to sprawdź czy
jeszcze odlicza timer.
if(timerAktywny)
{
if(timer > 0) // czas nie upłynął. Zwróć numer aktualnego kroku
return krok
//Czas upłynął. Zatrzymaj timer i zwróć indeks kolejnego kroku
zatrzymajTimer();
return opisKroku.nastepnyKrok;
}
//Jeśli timer nie jest aktywny, to albo trzeba zbadać warunek
wyjście, albo ustawić timer dla kolejnego kroku.
if(opisKroku.czasTrwania > 0) // krok trwa określony czas)
{
uruchomTimer(opisKroku.czasTrwania);
return krok;
}
//sprawdź warunek wyjścia
if(opisKroku.funkcjaTestujaca())
//funkcja zwróciła true, przechodzimy do kolejnego kroku
return opisKroku.nastepnyKrok
//nic się nie dzieje, pozostajemy dalej w tym samym kroku
return krok
}
Z doprowadzeniem kodu do postaci kompilowalnej i działającej,
zaimplementowaniem obsługi timera odmierzającego czas i funkcji
pomocniczych testujących warunki i ustawiających wyjścia powinieneś
poradzić sobie sam :)
Jacek.
Następne wpisy z tego wątku
- 01.11.17 08:00 slawek
- 01.11.17 08:03 slawek
- 01.11.17 09:04 jacek
- 01.11.17 17:21 Jacek Radzikowski
- 01.11.17 21:06 slawek
- 01.11.17 21:07 slawek
- 01.11.17 21:09 slawek
- 01.11.17 21:46 Jacek Radzikowski
- 01.11.17 23:29 slawek
Najnowsze wątki z tej grupy
- pozew za naprawę sprzętu na youtube
- gasik
- Zbieranie danych przez www
- reverse engineering i dodawanie elementów do istniejących zamkniętych produktów- legalne?
- Problem z odczytem karty CF
- 74F vs 74HCT
- Newag ciąg dalszy
- Digikey, SN74CBT3253CD, FST3253, ktoś ma?
- Szukam: czujnik ruchu z możliwością zaączenia na stałe
- kabelek - kynar ?
- Podnieść masę o 0.6V
- Moduł BT BLE 5.0
- Pomiar amplitudy w zegarku mechanicznym
- ale zawziętość i cierpliwość
- Chiński elektrolizer tester wody
Najnowsze wątki
- 2025-01-06 Jeździ, skręca, hamuje
- 2025-01-06 Białystok => System Architect (Java background) <=
- 2025-01-06 Gliwice => Specjalista ds. public relations <=
- 2025-01-06 Białystok => Solution Architect (Java background) <=
- 2025-01-06 Zielona GĂłra => Konsultant WdroĹźeniowy Comarch XL/Optima (KsiÄgowoĹ
- 2025-01-06 Popr. 14. Nauka i Praca Programisty C++ w III Rzeczy (pospolitej)
- 2025-01-06 Ostrów Wielkopolski => Area Sales Manager OZE <=
- 2025-01-06 Do IO i innych elektrooszolomow, tu macie prawdziwe smrody
- 2025-01-06 Białystok => Full Stack .Net Engineer <=
- 2025-01-06 Kraków => Business Development Manager - Network and Network Security
- 2025-01-06 Katowice => Regionalny Kierownik Sprzedaży (OZE) <=
- 2025-01-06 Warszawa => Spedytor Międzynarodowy <=
- 2025-01-06 Lublin => Programista Delphi <=
- 2025-01-06 Gdańsk => Specjalista ds. Sprzedaży <=
- 2025-01-06 śnieg