Skocz do zawartości

Jak pisać biblioteki na Arduino?


Pomocna odpowiedź

46 minut temu, farmaceuta napisał:

To jaka bedzie roznica czy zadeklaruje zmienne w .cpp czy w pliku .h jako private: ?

To zależy od tego co chcesz osiągnąć. W pierwszym przypadku będziesz miał jeden wspólny stan (przez współdzielenie tej globalnej zmiennej w .cpp) dla każdego obiektu danej klasy, w drugim będziesz miał odrębne stany dla każdego obiektu klasy. W pierwszym przypadku zmiana w jednym obiekcie, będzie powodować zmianę dla wszystkich obiektów tej klasy - bo współdzielisz tą zmienną globalną. W drugim przypadku zmiana w jednym obiekcie, wpływa tylko na ten obiekt - i to jest to co chcesz zazwyczaj osiągnąć, raczej nie chcesz żeby zmiana w jednym obiekcie powodowała zmianę w każdym obiekcie tej klasy (chyba, że to chcesz osiągnąć - ale wtedy lepiej zastosować zmienną w postaci statycznego membera). Ten prosty program pokazuje mniej lub bardziej tą różnicę:

// case.h
#pragma once

class CaseB
{
  public:
    int getA() const;

    void changeA(const int a);

  private:
    int m_a;
};

class CaseA
{
  public:
    int getA() const;

    void changeA(const int a);
};

// case.cpp
#include "lib.h"

int a_global;

int CaseB::getA() const
{
    return m_a;
}

void CaseB::changeA(const int a)
{
    m_a = a;
}

int CaseA::getA() const
{
    return a_global;
}

void CaseA::changeA(const int a)
{
    a_global = a;
}

// main.cpp
#include "lib.h"
#include <assert.h>
#include <iostream>

int main()
{
    CaseA a1;
    a1.changeA(10);

    CaseA a2;
    a2.changeA(20);

    CaseB b1;
    b1.changeA(10);

    CaseB b2;
    b2.changeA(20);

    assert(10 == a1.getA());
    assert(20 == a2.getA());
    assert(10 == b1.getA());
    assert(20 == b2.getA());
}

 

  • Pomogłeś! 1
15 godzin temu, Matthew11 napisał:

W pierwszym przypadku zmiana w jednym obiekcie, będzie powodować zmianę dla wszystkich obiektów tej klasy - bo współdzielisz tą zmienną globalną. W drugim przypadku zmiana w jednym obiekcie, wpływa tylko na ten obiekt - i to jest to co chcesz zazwyczaj osiągnąć,

Aha rozumiem...czyli dopoki mialbym jedna instancje to globalna zmienna "ujdzie" ,ale przy kilku to juz nie bo niezaleznie w ktorej instancji dokonam modyfikacji to zmienna jest wspolna dla wszystkich instancji bo jest globalna a jesli utworze ta zmienna w private: to zostanie ona jakby zdublowana dla kazdej instancji i tylko tam bedzie modyfikowana nie zaleznie od calej reszty instancji.

 

Jeszcze zapytam...powiedzmy ze mam klase w niej trzy funkcje, wszystkie identyczne (o roznych nazwach np. t1, t2, t3) to do kazdej tej funkcji musze utworzyc osobna zmienna w private: tak?  

 

1 godzinę temu, farmaceuta napisał:

Jeszcze zapytam...powiedzmy ze mam klase w niej trzy funkcje, wszystkie identyczne (o roznych nazwach np. t1, t2, t3) to do kazdej tej funkcji musze utworzyc osobna zmienna w private: tak?  

To ponownie zależy od tego co chcesz osiągnąć. Jeśli potrzebujesz 3 osobnych zmiennych to musisz stworzyć trzy. Jeśli te trzy funkcje mogą korzystać z jednej zmiennej to tworzysz jedną. Stan jakiegoś obiektu jakiejś klasy może być reprezentowany przez jedną lub wiele zmiennych, gdzie jedna metoda może nie dokonywać zmian stanu lub zmieniać stan przez zmiany w wielu zmiennych itp - i to jest zupełnie normalnie. 

  • Pomogłeś! 1
11 minut temu, Matthew11 napisał:

 Stan jakiegoś obiektu jakiejś klasy może być reprezentowany przez jedną lub wiele zmiennych, gdzie jedna metoda może nie dokonywać zmian stanu lub zmieniać stan przez zmiany w wielu zmiennych itp - i to jest zupełnie normalnie. 

Ok teraz rozumiem mniej wiecej:-)

W moim przypadku te kilka identycznych funkcji bedzie zapisywalo indywidualne czasy wiec juz wiem ze musze "tradycyjnie" stworzyc osobna zmienna do kazdej funkcji:-)

Dziekuje Ci ponownie za pomoc i poswiecony czas! 🙂 uklony w twoja strone 🙂

No i poleglem...🤔 niby dobrze nabazgralem a nie dziala tak jak bym chcial...

plik .h

#ifndef Milis_h
#define Milis_h

//#include "WProgram.h"
#include "Arduino.h"

class Milis
{
   public:
     uint8_t t0(uint32_t);
     uint8_t t1(uint32_t);
     uint8_t t2(uint32_t);
    
   private:
      uint32_t czas0;
      uint32_t czas1;
      uint32_t czas2;
};

#endif

plik .cpp

//#include "WProgram.h"
#include "Arduino.h"
#include "Milis.h"


uint8_t Milis::t0(uint32_t tim)
{
   if (millis() - czas0 > tim) {
     czas0 = millis();
     return true;
   } else {
     return false;
    }
}

uint8_t Milis::t1(uint32_t tim)
{
   if (millis() - czas1 > tim) {
     czas1 = millis();
     return true;
   } else {
     return false;
    }
}

uint8_t Milis::t2(uint32_t tim)
{
   if (millis() - czas2 > tim) {
     czas2 = millis();
     return true;
   } else {
     return false;
    }
}

no i kod..

#include <Milis.h>

Milis mili;

void setup() {
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
}

void loop() {
   if (mili.t0(100))  {
    digitalWrite(8, !digitalRead(8));
  }
  if (mili.t1(1000))  {
    digitalWrite(9, !digitalRead(9));
  }
}

no i tak...pomijam calkiem fakt przydatnosci takiego czegos itp. tak dla testu sobie to robilem..

dzialanie? dwie diody sie zaswieca, ale tylko jedna bedzie migac i to z regoly ta ktora ma krotszy czas..czasem zapala sie obie i juz swieca,jest tak rowniez gdy dam taki sam czas jako argument..wczesniej w private: czas0/1/2 mialem jako tablice ale bez zmian. inne nazwy dla argumentu funkcji "tim" tez bez zmian...nie mam pojecia co ja tu zle zrobilem...ma to poprostu dzialac tak ze diody maja mi migac z rozna czestotliwoscia niezaleznie z uzyciem millis(); 

ktos sie ulituje nademna nieszczesnym kmiotem pseudo_programistycznym?😪

Ledwo pamiętam C++, ale zmienne czas0, czas1 i czas2 mają nieokreśloną wartość w momencie tworzenia obiektu. Zdaje się jest szansa, że ich wartość będzie dużo większa, niż liczba milisekund zwracana przez millis(), czyli nie wiadomo ile czasu minie zanim dioda mignie. Spróbuj je zainicjować przed pierwszym użyciem, najlepiej w setup(). Coś w rodzaju czas1 = czas2 = czas0 = millis(). Albo chociażby nadanie wartości 0.

  • Pomogłeś! 1
(edytowany)

Mala aktualizacja...wyglada na to ze..dziala:-o 

Tak mi przyszlo do glowy zeby zamiast diod sprobowac w serial monitorze wyswietlic cos za kazdym spelnionym if'em i ladnie cyklicznie wyswietla co ustalony czas...ale te diody nie chca za cholere dzialac..:-/ zwykle ledy zolte chyba 3mm (byc moze 2mm)...co prawda nie dalem rezystorow, ale to chyba by takich cyrkow nie bylo co? Arduino zasilane z usb..jednak jedna dioda tez nie koniecznie dziala...np. miga przez 20s gasnie na kilka s lub swieci i po chwili znow startuje...wyglada to jak by cos zaklocalo sie wieszalo? Jak macie jeszcze jakies sugestie chetnie wyslucham...dzieki za pomoc:-)

Edytowano przez farmaceuta

Odpisze tu bo byc moze sie komus przyda...ta "biblioteka"  dziala prawidlowo...mianowicie z serial bezblednia a z led problem, wiec przyjrzalem sie temu co bylo podejrzane czyli

digitalWrite(8, !digitalRead(8))

Nigdy wczesniej nie negowalem stanu w ten sposob , ale uzylem bo juz kilkukrotnie widzialem taka skladnie i wydalo mi sie to wygodne...po zmianie na zwykle zmienne stanu diody dziala bez zarzutu...widocznie powyzszy zapis sprawia problemy w niektorych sytuacjach jak w mojej np...

(edytowany)

 

Od kilku tygodni zastanawiałem się, dlaczego w Arduino zdecydowano się na C++. Obiekty i ośmiobitowiec? Podobno narzut po skompilowaniu w stosunku do C jest żaden, ale po co? Trochę sobie odświeżyłem wiedzę o operatorach new i delete, i pewnie po części o to chodzi. Dynamiczna alokacja pamięci w ramach języka. Bez wywoływania funkcji bibliotecznych w rodzaju malloc().

Z ciekawości popracowałem nad tą klasą od migania diodami, i teraz niby w czasie tworzenia obiektu można sobie w zależności od potrzeb zadeklarować ile (do 256). Z góry sorki za styl kodowania.

typedef uint8_t token ;

class Tst
{
	struct dioda
	{
		token pin ;
		uint32_t czas ;
		uint32_t czas_przel ;
	} ;

	dioda * diody ;
	token znacznik ;
public:
	Tst( uint8_t ile_diod ) ;
	token dodaj( uint8_t pin, uint32_t czas ) ;
	uint8_t czy_czas( token dioda ) ;
	uint8_t pin( token dioda ) ;
} ;

Tst::Tst( uint8_t ile_diod )
{
	diody = new dioda[ ile_diod ] ;
	znacznik = 0 ;

	for( uint8_t i = 0 ; i < ile_diod ; i ++ )
		diody[ i ].pin = diody[ i ].czas = diody[ i ].czas_przel = 0 ;
}

token Tst::dodaj( uint8_t pin, uint32_t czas )
{
	diody[ znacznik ].pin = pin ;
	diody[ znacznik ].czas = czas ;
	diody[ znacznik ].czas_przel = millis() ;

	return znacznik ++ ;
}

uint8_t Tst::czy_czas( token dioda )
{
	if( millis() - diody[ dioda ].czas_przel >= diody[ dioda ].czas )
	{
		diody[ dioda ].czas_przel = millis() ;
		return true ;
	}

	return false ;
}

uint8_t Tst::pin( token dioda )
{
	return diody[ dioda ].pin ;
}

 

Rzeczywiście część "użytkownika" robi się prosta, chociaż jeszcze np. stan pinu trzeba pilnować "ręcznie":

 

Tst * tst = NULL ;
token t0 ;

void setup()
{
	tst = new Tst( 16 ) ;
	pinMode( 13, OUTPUT ) ;

	t0 = tst->dodaj( 13, 800 ) ;
}

void loop()
{
	static uint8_t stan = 0 ;

	if( tst->czy_czas( t0 ) )
	{
		digitalWrite( tst->pin( t0 ), stan ) ;
		stan = ~stan ;
	}
	delay( 10 ) ;
}

 

No i rzeczywiście to działa! Kompiluje, ładuje, miga. Jak zobaczyłem, to uwierzyłem. UWAGA! Pisałem to kilkadziesiąt minut, przetestowałem w zasadzie raz na jedynej pokładowej diodzie. Używacie na własną odpowiedzialność.

Edytowano przez jargraw

A jeszcze przy okazji zapytam...jest jakis prosty sposob na odwolanie sie do struktury/tablicy z poziomu biblioteki? Powiedzmy tworze strukture/tablice normalnie w szkicu i mam nad nimi pelna kontrole z poziomu biblioteki...cos takiego mozliwe??

2 godziny temu, farmaceuta napisał:

jest jakis prosty sposob na odwolanie sie do struktury/tablicy z poziomu biblioteki? Powiedzmy tworze strukture/tablice normalnie w szkicu i mam nad nimi pelna kontrole z poziomu biblioteki...cos takiego mozliwe??

Jeśli dobrze zrozumiałem o co Ci chodzi to jednym sposobem jest tzw. dependency injection, masz strukturę A:

struct A
{
    int a;
    int b;
};

i chcesz korzystać z obiektu struktury A, w klasie B:

A a;
B b; // przykład

void setup()
{
    a.a = 10;   // a.a = 10
    b.fun();    // np. fun() ustawia a.a na 1
}

To do klasy B możesz przekazać referencje (lepsza opcja) lub wskaźnik na obiekt struktury A przez konstruktor i wygląda to tak:

class B
{
  public:
    B(A& a) : m_a {a} {}

    void fun()
    {
        m_a.a = 1;
    }

  private:
    A& m_a;
};

W klasie B przechowujesz referencje, którą musisz ustawić w konstruktorze (bo referencja musi mieć na co wskazywać) i wtedy użycie wygląda tak:

A a;
B b(a);

void setup()
{
    a.a = 10;   // a.a = 10
    b.fun();    // a.a = 1
}

W przypadku tablicy klasa mogła by wyglądać tak:

// klasa:
class C
{
  public:
    C(uint8_t* buffer, const uint8_t size) : m_buffer {buffer}, m_size {size} {}

  private:
    uint8_t* m_buffer;
    const uint8_t m_size;
};

// użycie:
uint8_t table[10];
C c(table, 10);

W przypadku tablicy musisz przekazać wskaźnik na pierwszy element i wypada przekazać rozmiar.

  • Pomogłeś! 1
  • 3 miesiące później...

Witam Panowie...nabazgralem sobie takie pseudo cos co mi ma zmieniac stan pinu okreslona ilosc razy i okreslony czas on/off..no i dziala, tylko chcialbym zeby ktos zerknal na deklaracje tych moich zmiennych w public/private, no i tak ogolnie czy nie zamotalem sie z tymi zmiennymi...

// h

#ifndef BlinkPin_h
#define BlinkPin_h

//#include "WProgram.h"
#include "Arduino.h"

class BlinkPin
{
   public:
    BlinkPin(uint8_t pin);
    void blink(uint8_t ilosc,  uint32_t zwloka_on, uint32_t zwloka_off);
    void ready();
    uint8_t pinn;
    uint8_t pin;
    uint8_t ilosc;
    uint32_t zwloka_off;
    uint32_t zwloka_on;

   private:
     uint8_t ilosc1;
     bool stan;
     bool blokuj;
     uint32_t czas;
};



#endif

 

// cpp

//#include "WProgram.h"
//#include "Arduino.h"
#include "BlinkPin.h"



 BlinkPin::BlinkPin(uint8_t pin)
    : pinn ( pin )
    , ilosc1 ( 0 )
    , stan ( false )
    , blokuj ( false )
    , czas ( 0 )
{
   pinMode(pinn, OUTPUT);
}

void BlinkPin::blink(uint8_t i,  uint32_t czas_on,  uint32_t czas_off )
{
   if (blokuj == false)  {
      blokuj = true;
      ilosc = i;
      ilosc1 = 0;
      zwloka_off = czas_off;
      zwloka_on = czas_on;
    }
}

void BlinkPin::ready()
{
   
  if ( blokuj == true) {
    
  if (ilosc1 < ilosc) {

if (millis() - czas > zwloka_on && stan == false) {
      digitalWrite(pinn, HIGH);
      stan = true;
      czas = millis();
  }
    if (millis() - czas > zwloka_off && stan == true) {
      digitalWrite(pinn, LOW);
      stan = false;
      czas = millis();
      ilosc1++;
    }
}  else {
    blokuj = false;
   }
   
  }
}

jeszcze zapytam o poczatkowe wartosci zmiennych...jezeli to maja byc zera to musze w konstruktorze to uwzgledniac? czy zmienne te zachowaja sie jak zwykle zmienne globalne i automatycznie zostana przypisane zera do nich?

6 minut temu, farmaceuta napisał:

.jezeli to maja byc zera to musze w konstruktorze to uwzgledniac?

Tak.

A przy okazji: wywaliłbym co się da do private/protected. Jeśli użytkownik nie musi mieć dostępu do tych zmiennych to znaczy, że nie muszą być publiczne. Po co np. użytkownikowi dostęp do pinn? A tak w ogóle to po co tam zmienna pin?

  • Pomogłeś! 1

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...