Skocz do zawartości

Jak pisać biblioteki na Arduino?


macizet

Pomocna odpowiedź

20 minut temu, slon napisał:

Czy taki sposób będzie poprawny

Jeśli led faktycznie będzie obiektem to owszem.

No to teraz proszę bardzo - jako ćwiczenie napisać odpowiednią klasę. Podaję przykładowy interfejs

class BlinkingLed {
  public:
  	BlinkingLed(int pin);
  	BlinkingLed(int pin, int OnTime, int OffTime, int autostart = 0);
  	void setOnTime(int);
  	void setOffTime(int);
  	int getOnTime(void);
  	int getOffTime(void);
  	void start(void);
  	void stop(void);
  	void run(void);
};

  	

Zaproponować i zaimplementować inne metody które mogą być potrzebne.

Zaproponować inny interfejs i krótko opisać dlaczego jest lepszy.

A dla bardziej ambitnych - napisać to samo ale w C a nie w C++ 🙂

Kto chętny?

 

 

Link do komentarza
Share on other sites

@ethanak dziękuje za dalsze podjęcie tematu oraz za podanie przykładowego interfejsu. Natomiast mam pytanie : czy dla podanej klasy BlinkingLed nie będę potrzebował definiować atrybutów? 

class BlinkingLed {
  private:
    int _pin
    int _onTime;
    int _offTime;
    int _autostart;
  public:
  	BlinkingLed(int pin);
  	BlinkingLed(int pin, int OnTime, int OffTime, int autostart = 0);
  	void setOnTime(int);
  	void setOffTime(int);
  	int getOnTime(void);
  	int getOffTime(void);
  	void start(void);
  	void stop(void);
  	void run(void);
};

 

Link do komentarza
Share on other sites

Konstruktor przypisuje wartości początkowe do atrybutów

BlinkingLed(int pin, int OnTime, int OffTime, int autostart = 0);

czyli w tym wypadku do atrybutu _autostart domyślnie została by przypisana wartość zero. Tworząc nowy obiekt np:

BlinkingLed led1(13,1000,1000);

podaje wartości początkowe do pozostałych atrybutów.  Metoda start(); sprawdza warunek np:

start()
{
  if (autostart==1) 
  {
   // blink uruchomiony 
  } else 
  // czekamy
}

atrybut _autostart ma wartość 0 więc czekamy. Metoda run(); np:

run()
{
  autostar=1;
}

metoda stop();

stop()
{
 autostart=0; 
}

teraz gdybym chciał utworzyć kolejny obiekt 

BlinkingLed led2(3);

tutaj zastosowałem pierwszy konstruktor

BlinkingLed(int pin);

czyli na tą chwilę miał bym dwie diody na na pinach 3 i 13 gotowe do uruchomienia np:

led1.start(); led2.start(); 
led1.run(); led2.run();

czy to co napisałem jest poprawne zwłaszcza jeśli chodzi o zastosowanie konstruktora?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Hm... nic z tego nie rozumiem. Funkcja start nie startuje migania ledą... funkcja run to tylko sygnalizacja że start ma startować... a miganie odbywa się automagicznie za pomocą telepatii...

Biblioteka to nie tylko zestaw klas, metod i różnych innych przydatnych firdymałków - to również dokumentacja. Nawet taka najkrótsza - przy deklaracji metody jedno zdanie opisu co dana metoda robi i (jeśli nazwy parametrów nie określają jednoznacznie ich znaczenia) co znaczy który parametr. Bez tego użycie nawet najbardziej dopracowanej biblioteki będzie niemożliwe.

Poza tym pisząc bibliotekę musimy mieć na uwadze to, że ktoś ją będzie stosować i ten ktoś najprawdopodobniej nie będzie zaglądał do kodów źródłowych, a przede wszystkim będzie szukał biblioteki spełniającej jego oczekiwania. I znów nawet najpiękniejszy kod nie spowoduje tego, że biblioteka w której zabrakło jakiejś ważnej funkcji stanie się nagle używalna...

Wyobraźmy sobie jakiś nietrywialny program, który będzie korzystać z naszej biblioteki do migania ledami. Niech to będzie np. metronom.

Metronom wyposażony jest w przyciski start-stop, potencjometr regulujący tempo w zakresie 30..250 BPM, ledę sygnalizującą rytm oraz ledę pokazującą stan naładowania akumulatora.

Po wciśnięciu "start" metronom rusza, migając ledą sygnalizacyjną w określonym tempie, przy czym czas zapalenia ledy musi być równy czasowi zgaszenia. Tempo ustalane jest potencjometrem. Po wciśnięciu "stop" metronom się zatrzymuje, a leda gaśnie.

Druga leda pali się cały czas jeśli napięcie odczytane z akumulatora wynosi ponad 3.8V. Jeśli napięcie spada, leda zaczyna migać w stałym rytmie (raz na sekundę mniej więcej), z tym że mignięcia są coraz krótsze w miarę spadku napięcia do 3.2V. Od 3.2V w dół czas zapalenia ledy wynosi 1/10 okresu i już się nie zmniejsza.

Należy spróbować zaprojektować taki program używając funkcji z nieistniejącej jeszcze biblioteki - w ten sposób będziemy mieli całkiem nieźle określony interfejs (a przynajmniej jego fragment) i będziemy mogli wziąć się do tworzenia biblioteki.

Ktoś chętny?

Niekoniecznie do stworzenia biblioteki, ale właśnie programu metronomu i określenia swoich wymagań co do biblioteki migania ledami?

A może ktoś ma inną propozycję programu używającego takiej biblioteki?

 

 

  • Lubię! 1
Link do komentarza
Share on other sites

Moją intencją nie było rozpisywanie pliku BlinkingLed.cpp czy , którejkolwiek z tych metod. Natomiast sam plik nagłówkowy BlinkingLed.h to w zasadzie mógł by wyglądać tak:

#ifndef BlinkingLed_h
#define BlinkingLed_h

#include "Arduino.h"

class BlinkingLed {
  private:
    int _pin;
    int _onTime;
    int _offTime;
    int _autostart;
  public:
  	BlinkingLed(int pin);
  	BlinkingLed(int pin, int OnTime, int OffTime, int autostart = 0);
  	void setOnTime(int);
  	void setOffTime(int);
  	int getOnTime(void);
  	int getOffTime(void);
  	void start(void);
  	void stop(void);
  	void run(void);
};

#endif

oczywiście , żeby to mogło działać to trzeba by było obydwa te pliki (BlinkingLed.cpp oraz BlinkingLed.h) przenieść do folderu BlinkingLed i umieścić w  folderze arduino Libraries (zakładając , że będziemy korzystać z arduino IDE) . Jeśli ktoś jest dalej zainteresowany tym tematem to zostaje do uzupełnienia plik BlinkingLed.cpp i rozpisanie poszczególnych metod.

#include "Arduino.h"
#include "BlinkingLed.h"

BlinkingLed::BlinkingLed(int p)
{

}

 

Link do komentarza
Share on other sites

6 godzin temu, slon napisał:

zostaje do uzupełnienia plik BlinkingLed.cpp i rozpisanie poszczególnych metod.

No i tu jest pierwszy wielki błąd: przed napisaniem kodu należy dokładnie określić, co która metoda ma robić (np. że "start" ma wystartować miganie) i dopiero brać się za realizację założeń. Trudno realizować niesprecyzowane założenia, prawda?

Następny błąd to określanie z góry prywatnych atrybutów. Po co? Przecież użytkowników biblioteki nie interesują prywatne zmienne (no, chyba  że ktoś chce napisać bibliotekę pochodną i będzie klął autora w żywy kamień za użycie "private" zamiast "protected"). Te atrybuty wylezą dopiero w trakcie pisania. Pomyśl: czy pisząc program w C++ najpierw określasz wszystkie zmienne globalne, a potem się tego kurczowo trzymasz?

Jak widać, pisanie bibliotek nie jest taką prosta sprawą nawet jeśli to ma być najprostsza biblioteka do migania ledami 🙂

No ale może ktoś jeszcze spróbuje swoich sił?

 

Link do komentarza
Share on other sites

Wracając do metronomu to zainteresowały mnie dwa założenia 

Cytat

potencjometr regulujący tempo w zakresie 30..250 BPM

Cytat

 czas zapalenia ledy musi być równy czasowi zgaszenia

wpasowałem to do funkcji, którą zamieściłem na pierwszej stronie

  potencjometr=analogRead(A1);
  potencjometr=map(potencjometr,0,1023,30,250);
  BPM=60000/potencjometr/2; 
  start(7,BPM,BPM);

działa całkiem  fajnie chociaż osobiście chyba bym wolał regulację BPM przyciskami "góra" , "dół".

Link do komentarza
Share on other sites

A dla mnie najwygodniejszy by był enkoder obrotowy i wyświetlacz - przejście przyciskami od najwolniejszego do najszybszego będzie wymagało trochę klikania. Ale w sumie nie o to tu chodziło...

Po prostu sterowanie potencjometrem jest nieprecyzyjne ale najprostsze w realizacji (w sumie jedna linijka w programie), a chodziło o napisanie biblioteki a nie skupianie się nad wprowadzaniem danych do programu 🙂 Mając bibliotekę będziemy mogli napisać sobie metronom tak jak nam pasuje.

 

 

Link do komentarza
Share on other sites

@slon żeby sterować przyciskami trzeba by zrobić pętlę for która co 0.5 sekundy będzie zwiększać wartość zmiennej o 1 a potem dać tą zmienną do tego twojego BPM (Sorry, ale nie bardzo wiem gdzie), a druga pętla for przy drugim przycisku odpowiadałaby za zmniejszanie wartości zmiennej.

Link do komentarza
Share on other sites

Sterowania przyciskami nie sprawdzałem. Natomiast sprawdziłem jak wygląda sterowanie jasnością  z użyciem micros() na pinie 4. Dioda płynnie się rozjaśnia (kwestia doboru wartości).

Link do komentarza
Share on other sites

Witam. 

Ja mam problem z wykorzystaniem obiektu i metod z innej biblioteki w mojej wlasnej. Mianowicie napisalem programik wykorzystujacy biblioteke onewire, napisalem kilka funkcji do tego i w glownym pliku zrobil sie balagan. Chcialem stworzyc biblioteke i przeniesc wszystkie funkcje do tej biblioteki, ale nie wiem, jak zrobic zeby obiekt onewire stworzony w programie glownym dzialal po moja biblioteka. Szukalem w necie i nie moge znalezc. Czy jest ktos kto moglby mi w tym pomoc? 

Link do komentarza
Share on other sites

wstawiam kod biblioteki temp_sensor:

temp_sensor.h:

#ifndef temp_sensor_H
#define temp_sensor_H

#include "Arduino.h"
class OneWire;
class temp_sensor{

#define DEBUG
 
public:

  uint8_t termo[8];
  
  temp_sensor(uint8_t pin);
  void begin();
  bool search_ds();
  uint8_t save_TCS(uint8_t *rom, uint8_t cnt);
  void get_temp(uint8_t Tsensor);
  bool porownaj(uint8_t *rom, uint8_t cnt);
  void start_measT();

private:

  OneWire &DS;
  uint8_t TCS[80], data[12];
  uint8_t TC;
  uint8_t _pin;
  
  bool search_ok;
};



#endif

temp_sensor.cpp:

#include "temp_sensor.h"
#include <OneWire.h>
//#include <DallasTemperature.h>
#include <EEPROM.h>


temp_sensor::temp_sensor(uint8_t pin) {
  _pin = pin;
   begin();
 // DallasTemperature DS(&oneWire);
}

void temp_sensor::begin(){

  DS = OneWire(_pin);
  Serial.begin(9600);
  Serial.print("start");
}

bool temp_sensor::search_ds() {


  uint8_t x = DS.search(termo);
  //  for( i = 0; i < 8; i++) {
  //    Serial.write(' ');
  //    Serial.print(termo[i], HEX);
  //  }
#ifdef DEBUG
  Serial.print("cnt ds:  ");
  Serial.println(x);
#endif

  if (x == 0) {
    //Serial.println("No more addresses.");
    //Serial.println();
    //digitalWrite(LED_BUILTIN, LOW);
    DS.reset_search();
    delay(250);
  }

  if ( x == 1) {
#ifdef DEBUG
    for ( uint8_t i = 0; i < 8; i++) {
      Serial.write(' ');
      Serial.print(termo[i], HEX);
    }
#endif

    for (uint8_t i = 0; i < TC; i++) {

#ifdef DEBUG
      Serial.println();
      Serial.print("TC:   ");
      Serial.println(TC);
      Serial.print("czujnik:   ");
      Serial.println(i);
#endif

      bool flag_ok = porownaj(termo, i);
      if (!flag_ok) {
        //digitalWrite(LED_BUILTIN, HIGH);
        //break;
      }
      else {
        //digitalWrite(LED_BUILTIN, LOW);
        x = 0;
        // break;
      }
    }
    if (x == 0) digitalWrite(LED_BUILTIN, LOW);
    if (x == 1) digitalWrite(LED_BUILTIN, HIGH);

  }
#ifdef DEBUG
  for ( uint8_t i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(termo[i], HEX);
  }
  Serial.println();
#endif
  return x;
}


uint8_t temp_sensor::save_TCS(uint8_t *rom, uint8_t cnt) {

  uint8_t addr = (cnt * 8); //+10;

#ifdef DEBUG
  Serial.println("...........ZAPIS............");
  Serial.print("adres:   ");
  Serial.println(addr);
#endif
  for (uint8_t x = 0; x < 8; x++) {
    TCS[addr] = rom[x];

#ifdef DEBUG
    Serial.write(' ');
    Serial.print(rom[x], HEX);
#endif

    addr++;
  }
#ifdef DEBUG
  Serial.println();
  Serial.println(addr);

  addr = (cnt * 8);
  for (uint8_t x = 0; x < 8; x++) {
    Serial.print(TCS[addr], HEX);
    termo[x] = 0;
    addr++;
  }
#endif

  TC ++;
  search_ok = 0;
  Serial.println();
  //Serial.println("zapis");
  //Serial.print("TC:  ");
  //Serial.println(TC);

  //Serial.println(x);
  digitalWrite(LED_BUILTIN, LOW);
  //delay(1000);
  return TC;
}



void temp_sensor::get_temp(uint8_t Tsensor) {

  Serial.print("temperatura czujnik ");
  Serial.print(Tsensor);
  Serial.print(": ");
  //Serial.println("21,2 C");

  uint8_t addr[8];
  //uint8_t tmp = Tsensor * 8;

  for (uint8_t x = 0; x < 8; x++) {
    addr[x] = TCS[(Tsensor * 8) + x];
    //tmp++;
  }
  DS.reset();
  DS.select(addr);
  DS.write(0xBE);         // Read Scratchpad

  for (uint8_t i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = DS.read();
  }
  if (DS.crc8(data, 7) != addr[7]) {

    int16_t raw = (data[1] << 8) | data[0];

    float celsius = (float)raw / 16.0;
    Serial.print(celsius);
    Serial.println(" C");
  }
}



bool temp_sensor::porownaj(uint8_t *rom, uint8_t cnt) {

  uint8_t addr = (cnt * 8); //+10;

#ifdef DEBUG
  Serial.println("...........POROWNAJ............");
  Serial.print("adres:   ");
  Serial.println(addr);
  Serial.print("nr TC:   ");
  Serial.println(cnt);
#endif

  bool wynik = 1;
  //bool rowne = 0;


  //  for ( uint8_t i = 0; i < 8; i++) {
  //    Serial.write(' ');
  //    Serial.print(rom[i], HEX);
  //  }
  //  Serial.println();


  for (uint8_t x = 0; x < 8; x++) {

#ifdef DEBUG
    Serial.print(rom[x], HEX);
    Serial.print("  ");
    Serial.print(TCS[addr], HEX);
#endif

    if (!(rom[x] == TCS[addr])) {
#ifdef DEBUG
      Serial.println("   rozne");
#endif

      wynik = 0;
      //break;
    }
#ifdef DEBUG
    else {
      Serial.println("   rowne");
      //rowne = 1;
    }
#endif

    addr++;
  }

#ifdef DEBUG
  if (wynik == 0) Serial.println("znaleziono");
  else Serial.println("nie znaleziono");
#endif

  return wynik;
}


void temp_sensor::start_measT() {

  DS.reset();
  DS.write(0xCC);
  DS.write(0x44, 1);
  
}

to bym chcial miec w bibliotece.

wszystko co wyczytalem i podpatrzylem w innycj bibliotekach zrobilem. niby sie kompiluje ale nie dziala. nawet nie wysyla nic do terminala.

tu kod glowny:

 

#include <EEPROM.h>
#include"temp_sensor.h"

//#define DEBUG

#define INDEX_OF_TSENSORS 9
// Data wire is plugged into port 2 on the Arduino
#define TCcount 9
#define TCarray 10
#define button1 14
#define push    0




#define ONE_WIRE_PIN 2
//#define TEMPERATURE_PRECISION 12

temp_sensor Tsensor(ONE_WIRE_PIN);

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
//OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
//DallasTemperature temp(&oneWire);

// arrays to hold device addresses
//uint8_t termo[8], TCS[80], data[12];

// Assign address manually. The addresses below will beed to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// DeviceAddress insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// DeviceAddress outsideThermometer   = { 0x28, 0x3F, 0x1C, 0x31, 0x2, 0x0, 0x0, 0x2 };

void setup(void)
{
  // start serial port
  //Serial.begin(9600);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  pinMode(button1, INPUT_PULLUP);


  // Start up the library
  //temp.begin();
  //oneWire.reset_search();
  //delay(1000);

}

/*
   Main function, calls the temperatures in a loop.
*/

uint8_t TC;
uint8_t idx;
bool search_ok;
uint8_t tempcnt;


void loop(void)
{

  //TC = EEPROM.read(TCcount);
  TC = 0;
  idx = 0;
  search_ok = false;
  uint8_t licz = 0;


  Serial.println("start");

  while (1) {

    //Serial.println(licz);

    if (!search_ok) search_ok = Tsensor.search_ds();


    if (search_ok == 1) {
#ifdef DEBUG
      Serial.print("TC:   ");
      Serial.println(TC);
      delay(200);
#endif
      //digitalWrite(LED_BUILTIN, HIGH);
      if (digitalRead(button1) == push) {

        //Serial.println("zapis");

        idx = Tsensor.save_TCS(Tsensor.termo, idx);
        //EEPROM.write(INDEX_OF_TSENSORS, idx);
      }
    }
    //else digitalWrite(LED_BUILTIN, LOW);

    if (idx > 0 && search_ok == 0) {
      for (uint8_t x = 0; x < idx; x++) {
        Tsensor.get_temp(x);
      }
      
      Tsensor.start_measT();
      delay(1500);
    }
    //licz++;
  }
}

 

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.