Arduino Polska Forum

Pełna wersja: Podział dużego projektu
Aktualnie przeglądasz uproszczoną wersję forum. Kliknij tutaj, by zobaczyć wersję z pełnym formatowaniem.
Stron: 1 2
Witajcie,

moje pytanie dotyczy ogólnie pisania kodu w Arduino IDE, a może nawet ogólnie w C++

Do tej pory różne programy, które pisałem na Arduino bywały większe lub mniejsze, ale zawsze w jednym "pliku" - i wszystko grało. 

Ostatnio, jako że stałem się posiadaczem niewielkiego morskiego jachtu żaglowego, postanowiłem zbudować taki system "Smart Boat". Będzie tam sporo czujników - napięcia w akumulatorach, ciśnienia, temperatur w kilku miejscach, obrotów silnika i śruby, poziomu wody, paliwa itp. Całość podzielona na serce - czyli Arduino Mega oraz dwa arduino Nano - jedno obsługujące aku i zbiorniki, drugie silnik. Komunikacja między nimi będzie po USART. I poszczególne elementy sa już zbudowane. 

Ostatnio zabrałem się za zebranie tych "modułów" do kupy i napotkałem pewien problem programistyczny, który nie wiem jak "ugryźć" - mianowicie nie wyobrażam sobie, jak to miało by być pisane w jednym pliku. Jeszcze praktycznie nic nie jest uruchomione, a już jest to niemal 1000 linijek kodu. No to funkcje out. I tu moje zaskoczenie - bo tego kompletnie nie rozumiem. Poszczególne funkcje, które w nagłówku są dołączone do głównego programu (pliki *.cpp i *.h) nie widzą zmiennych globalnych, nie są w stanie odwoływać się do wyświetlacza itd itp. 

Do tej pory wydawało mi się, że własne funkcje dodane w nagłówku sa niejako "wklejane" w kod programu w miejscu ich wywołania i dopiero wówczas kompilator i debuger zabierają się do pracy. A tu niespodzianka. Jak powinno się prawidłowo podejść do tematu? 

Taki przykład kodu głównego
Kod:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);


#include "stronyMenu.h"


byte menuPage = 0;

void setup()
{
  lcd.backlight();
  lcd.noCursor();
  lcd.init();

  lcd.setCursor(0,5);
  lcd.print("s/y Sirius");

}

void loop() {
  switch(menuPage)
  {
    case 0:
    menuZero();
    break;

    case 1:
    menuOne();
    break;
  }
 

}

Następnie plik "stronyMenu.h

Kod:
#ifndef STRONYMENU_H
#define STRONYMENU_H

void menuZero();
void menuOne();

#endif

Oraz "stronyMenu.cpp"
Kod:
#include "stronyMenu.h"

void menuZero()
{
  lcd.setCursor(0,2);
  lcd.print("Menu glowne");
  //dalsza część tego menu
}

void menuOne()
{
  lcd.setCursor(0,2);
  lcd.print("Menu silnik");
  //dalsza część tego menu
}


Kompilator zaś wypluwa tak:


Cytat:C:\Users\{...}\stronyMenu.cpp: In function 'void menuZero()':
C:\Users\{...}\stronyMenu.cpp:5:3: error: 'lcd' was not declared in this scope
  lcd.setCursor(0,2);
  ^~~
C:\Users\{...}\stronyMenu.cpp: In function 'void menuOne()':
C:\Users\{...}\stronyMenu.cpp:12:3: error: 'lcd' was not declared in this scope
  lcd.setCursor(0,2);
  ^~~

exit status 1

Compilation error: 'lcd' was not declared in this scope


Jak rozumiem - funkcje z osobnego pliku nie są przed kompilacją dodawane do głównego programu, bo "nie widzą" istniejącego już obiektu lcd i nie potrafią się do niego odwołać...

Przepraszam, jeśli moje tłumaczenie jest zawiłe, lub problem okaże się trywialny, ale z zawodu nie jestem programistą... I z góry dzięki za porady!
Nie no, komplikujesz nam tu miganie led, głównie na tym się tu znamy.
Nie robiłem takiego podziału, ale domyślam się, że tam gdzie używasz lcd musisz dodać bibliotekę i tam stworzyć obiekt lcd, potem w głównym pliku wpisać tylko informację, że obiekt będzie stworzony w innym pliku:
zamiast LiquidCrystal_I2C lcd(0x27,20,4); wpisz extern LiquidCrystal_I2C lcd;
Nie wiem co się wydarzy, gdy będziesz chciał używać tego w wielu plikach załączanych, pewnie w każdym załączyć bibliotekę i dopisać extern.
W sumie to zrobiłem taki zestaw trzech plików i nieważne czy obiekt stworzę w ino, czy w cpp, a w drugim podam extern lcd to kompilują się tak samo.
Nie wiem czy to działa, bo nie mam pod ręką LCD, ale możesz przetestować.
Nie sądzę też, by to był dobry sposób na menu, wg mnie menu powinno być wirtualne w pamięci, na pamięci przesuwasz która opcja jest aktywna, którą modyfikujesz, gdzie jesteś w menu, a potem wystarczy w kontekście tych danych wywołać funkcje print na LCD/SERIAL/OLED lub reagować na wciśnięcie OK, GÓRA, DÓŁ, jak zmienisz sterowanie na przyciski analogowe, joystik, klawiaturę czy enkoder to zmieniasz tylko funkcję "manipulator", a nie każdą opcję menu.
Niedawno poruszyłem podobny problem w tym wątku

Widziałem jeszcze gdzieś w menu pozycję "własna biblioteka", ale jeszcze tego nie sprawdzałem.

O ile zasady samego podziału kodu w 'normalnym' C/C++ na pliki są szeroko opisane, to Arduino średnio się do tego nadaje. Szczególnie, że będziesz potrzebował projekt, który tak naprawdę składa się z kilku kompletnych programów - na każdy z modułów, a jednocześnie przynajmniej definicje protokołu komunikacyjnego powinny być wspólne.

Szczególny problem widzę w tym, że ustawienia nie są zapisywane z projektem, tylko są globalne - czyli gdy pracuje się na projektami na różne platformy za każdym razem gdy przechodzi się do innego projektu trzeba przekonfigurować Arduino.
(28-01-2024, 01:34)kaczakat napisał(a): [ -> ]Nie no, komplikujesz nam tu miganie led, głównie na tym się tu znamy.
Bardzo przepraszam, że wrzuciłem taki prosty kod... Specjalnie wręcz go napisałem na potrzeby tego pytania, by łatwiej przedstawić problem. We właściwym kodzie menu jest również oparte o switch - case, gdyż ze względu na łatwość zmiany sposobu obsługi (o czym piszesz). 

Cytat:pewnie w każdym załączyć bibliotekę i dopisać extern.

Spróbuję - ale czy to nie spowoduje, że w wynikowym kodzie biblioteki dodane się zdublują? 

Cytat:Nie sądzę też, by to był dobry sposób na menu, wg mnie menu powinno być wirtualne w pamięci

Tak de facto będzie - lcd jest tylko w tym przykładowym kodzie, który napisałem. Faktycznie jest to ekran eInk, 400x300 i w małej jego części jest wybór "menu", którego zatwierdzenie powoduje przeładowanie całego ekranu na właściwą "stonę". Ale tego wszystkiego robi się po prostu za dużo na jeden plik, ciężko się po tym poruszać po prostu.

Cytat:Niedawno poruszyłem podobny problem w tym wątku

Przeczytałem - znaczy funkcją #include dodajesz pliki .ino zamiast .h?

Ogólnie dziękuję za pomoc i podpowiedzi, sprawdzę i dam znać.
(28-01-2024, 11:50)QubiV8 napisał(a): [ -> ]Przeczytałem - znaczy funkcją #include dodajesz pliki .ino zamiast .h?

Ogólnie dziękuję za pomoc i podpowiedzi, sprawdzę i dam znać.

Nie, zwyczajnie mam pliki nagłówkowe i te, ze zmiennymi i funkcjami, ale te drugie nie nazywają się .c/.cpp tylko .ino - wtedy ten mechanizm kompilacji Arduino przetworzy je tak, by w trakcie ich kompilacji były widoczne standardowe funkcje biblioteki Arduino. Jeśli ich nie używasz (np. moduł 'czysto-obliczeniowy') to taka zabawa jest niepotrzebna, mogą zostać .c/.cpp.
Pliki .ino lub te dodatkowe .c(pp) wystarczy, że będą znajdowały się w katalogu projektu i zostaną skompilowane i zlinkowane z projektem.

I tutaj wychodzi kolejny drobny problem - ArduinoIDE nie potrafi otworzyć dowolnego pliku a tym samych utworzyć go. Nowy plik to dla niego nowy projekt. Należy stworzyć pliki jakimś innym narzędziem (mogą być puste - np. na linuxie po użyciu touch tworzy się pusty plik) i dopiero potem uruchomić IDE. Wtedy te dodatkowe pliki zostaną otwarte w kolejnych zakładkach edytora. Głupie to trochę i świadczy że to Arduinowe IDE nie jest przystosowane do wieloplikowych projektów, z wyjątkiem sytuacji gdy mamy jeden plik programu + biblioteki.

Właśnie robie prosty projekt, ale taki, w którym daje łatwo wydzielić kawałek funkcjonalności, sprawdzę to praktycznie.
Oki, dzięki - sprawdzę to dzisiaj wieczorem. Póki co przyglądam się uważnie srodowisku PlatformIO do Visual Studio Code - może po prostu czas porzucić arduinowe IDE...

Dziękuję za pomoc!
Dawno już powinieneś rzucić jak zamierzasz duże programy robić bo tam bardzo duże ułatwienie
@OscarX nie wiem która wersja, ale u mnie da się utworzyć nowy plik .cpp czy .h zaraz po otwarciu/utworzeniu ino. Nie jest to jakiś nachalne menu, ma kilka pixeli obok ikony SerialMonitor, trójkącik w dół, albo skrót CTR+SHIFT+N - NEW TAB, pod oknem naszego szkicu wyskakuje pasek przez całą szerokość okna, gdzie w jego prawej strony należy podać pełną nazwę nowego pliku, nowej ZAKŁADKI w tym projekcie.
Oczywiście, że PlatformIO jest lepsze do dużego projektu, Arduino IDE jest lepsze do 100 jednocześnie otwartych okienek z różnymi wariacjami migania ledem.
Dzięki za info, faktycznie można w ten sposób dodać zakładkę z dowolnym plikiem języka c (bo np. odmówił utworzenia pliku txt - wszystkich możliwości nie sprawdzałem).

A wracając do poprzednich postów - czy jeśli ktoś porzuca środowisko ArduinoIDE to powinien też porzucić forum, czy są tutaj tematy o pracy w innych środowiskach? Wink
Nie znam polskiego forum poświęconego konkretnemu programowi IDE, no może to MK od AVR i ECLIPSE - polecam Wink... omijać, Arduino jest popularne i możesz go uprawiać od notatnika po kombajny typu MS STUDIO, na początku wstawiasz arduino.h i jedziesz dalej. Z innych fajnych jest Elektroda i Forbot, jest sporo ludzi znających temat.
Jak na tym forum chcesz się czegoś nauczyć, skoro częściej odpowiadasz niż pytasz, to tylko klepiąc ludziom przykłady i utrwalając sobie posiadaną wiedzę, znajdując odpowiedzi w Google w tych tematach, gdzie jeszcze nie ogarniasz. Oczywiście brzydzę się robieniem całości za kogoś, raczej pokazuję idee, które można wykorzystać we własnych projektach jeśli się ktoś chce nauczyć, nie będę odbierał chleba biednym informatykom.
Stron: 1 2