Maszynaduino jest biblioteką dla Arduino (Uno, Mega, etc) ułatwiającą oprogramowanie pulpitów budowanych na potrzeby symulatora MaSzyna (aka EU07).
Biblioteka posiada obiektowy interfejs i ułatwia korzystanie z analogowych multiplekserów umożliwiających obsługę większej ilości kontrolek, przełączników i mierników, niż fizycznie posiadają mikrokontrolery.
- obiektowy interfejs
- szeregowa konsola do diagnostyki
- wsparcie dla analogowych multiplekserów (typu 74HC4067)
- obsługa kontrolek i przełączników
- prostota użycia i czytelność
- kompatybilność z MaSzyną wysyłającą preambułę w komunikacji UART (cztery bajty 0xEF na początkach ramek wejść i wyjść)
Użycie biblioteki należy rozpocząć od załadowania plików nagłówkowych:
#include "maszynaduino.h"
W dalszej części (przed setup()
i loop()
) trzeba zadeklarować kontrolki, przełączniki i sam pulpit.
Przykładowo przełącznik baterii podłączony do portu 2 można zadeklarować następująco:
#define PIN_BATERIA 2
Switch *bateria = new PinSwitch(PIN_BATERIA, 0, 2)
Parametry konstruktora PinSwitch
są następujące:
- nr pinu
- nr bajtu ramki wyjścia (tu: 0, liczony bez preambuły)
- nr bitu do gaszenia / zapalania (tu: 2)
Do pinu 3 podłączymy kontrolkę lampki czuwaka:
#define PIN_LAMPKA_CZUWAK 3
Indicator *lampkaCzuwaka = new PinIndicator(PIN_LAMPKA_CZUWAK, 4, 6);
Parametry konstuktora PinIndicator
są następujące:
- nr pinu
- nr bajtu ramki wejścia (tu: 4, liczony bez preambuły)
- nr bitu określającego stan kontrolki (tu: 6)
W następnym kroku należy utworzyć pulpit określając szeregowy port do komunikacji:
Console *sm42 = new Console(&Serial);
W funkcji setup()
należy dodać do pulpitu utworzone przełącznik i kontrolkę, oraz zainicjować pulpit.
void setup() {
sm42->addSwitch(bateria); // dodanie przełącznika baterii
sm42->addIndicator(lampkaCzuwaka); // dodanie kontrolki czuwaka
sm42->setup(); // zainicjowanie pulpitu
}
W funkcji loop()
należy dodać aktualizację pulpitu oraz transmisję danych:
void loop() {
sm42->update(); // aktualizacja pulpitu
sm42->transmit(); // komunikacja Pulpit <-> PC
}
Aby przykład zadziałał w MaSzynie, w pliku eu07_input-uart.ini
, musi pojawić się adekwatny wpis:
2 toggle batteryenable batterydisable
#include "maszynaduino.h"
#define PIN_BATERIA 2
#define PIN_LAMPKA_CZUWAK 3
Switch *bateria = new PinSwitch(PIN_BATERIA, 0, 2);
Indicator *lampkaCzuwaka = new PinIndicator(PIN_LAMPKA_CZUWAK, 4, 6);
Console *sm42 = new Console(&Serial);
void setup() {
sm42->addSwitch(bateria);
sm42->addIndicator(lampkaCzuwaka);
sm42->setup();
}
void loop() {
sm42->update();
sm42->transmit();
}
Do przełączników nie trzeba podłączać rezystorów ściągających, ponieważ Maszynaduino domyślnie konfiguruje wejścia w trybie INPUT_PULLUP
(z rezystorami podciągającymi wbudowanymi w Arduino). W razie potrzeby dowolny przełącznik można skonfigurować bez użycia rezystora podciągającego - biblioteka zadba o prawidłową interpretację stanu wejścia.
W tej chwili Console
konfiguruje port szeregowy na 57600 bodów (bez możliwości zmiany), i taką prędkość należy ustawić w eu07.ini
w MaSzynie.
Uwaga. Do wydania wersji 1.0 API biblioteki może się zmieniać bez zachowania kompatybilności.
Klasa | Opis |
---|---|
PinSwitch |
przełącznik podłączony bezpośrednio do pinu mikrokontrolera |
MuxSwitch |
przełącznik podłączony przez multiplekser |
PinIndicator |
kontrolka podłączona bezpośrednio do pinu mikrokontrolera |
Console |
obiekt opisu i obsługi pulpitu (może być zdefiniowanych wiele pulpitów) |
Mux |
obsługa pojedynczego multipleksera |
ConsoleDebug |
konsola debugowania podłączana do portu szeregowego |
Struktura | Opis |
---|---|
InputFrame |
struktura opisująca ramkę wejśc z symulatora (PC) |
OutputFrame |
struktura opisująca ramkę wyjść do symulatora (PC) |
struct InputFrame {
uint8_t preamble[4]; // 0-3
uint16_t tacho; // 4-5
uint8_t indicator0; // 6
uint8_t indicator1; // 7
uint8_t indicator2; // 8
uint8_t indicator3; // 9
uint8_t indicator4; // 10
uint16_t break_pressure; // 11-12
uint16_t pipe_pressure; // 13-14
uint16_t tank_pressure; // 15-16
uint16_t hv_voltage; // 17-18
uint16_t hv_current1; // 19-20
uint16_t hv_current2; // 21-22
uint16_t hv_current3; // 23-24
uint16_t year_month; // 25-26
uint16_t day_hour_minute; // 27-28
uint16_t second_milisecond; // 29-30
uint32_t odometer; // 31-34
uint16_t lv_voltage; // 35-36
uint8_t radio_channel; // 37
uint8_t unused[14]; // 38-51
};
struct OutputFrame {
uint8_t preamble[4];
uint8_t switch0;
uint8_t switch1;
uint8_t switch2;
uint8_t switch3;
uint8_t switch4;
uint8_t switch5;
uint8_t master_controller;
uint8_t second_controller;
uint16_t train_brake;
uint16_t independent_brake;
uint8_t unused[4];
};
InputFrame* Console::getInputs(); // odczyt stanu wejść (ostatnio odebrane)
OutputFrame* Console::getOutputs(); // odczyt stanu wyjść (do wysłania)
void addSwitch(Switch *); // dodanie przełącznika
void addIndicator(Indicator *indicator); // dodanie kontrolki
void setup(); // inicjalizacja
void update(); // aktualizacja przełączników, kontrolek, mierników, itp
void transmit(); // odebranie i wysłanie danych z/do PC
Konstruktor:
Console(HardwareSerial *serial);
Console(HardwareSerial *serial, Switch **switches, int switchesCount);
Console(HardwareSerial *serial, Switch **switches, int switchesCount, Indicator **indicators, int indicatorsCount);
Przełączniki i kontrolki można podać jako tablice bez konieczności dodawania ich pojedynczo w setup()
.
Ekwiwalentem dla przykładu z dokumentacji będzie:
Indicator *kontrolki[] = {lampkaCzuwaka};
Switch *hebelki[] = {bateria};
Console *sm42 = new Console(
&Serial,
hebelki, ARRAY_LENGTH(hebelki),
kontrolki, ARRAY_LENGTH(kontrolki)
);
void setup() {
sm42->setup();
}
ARRAY_LENGTH jest typowym makrem definiowanym przez bibliotekę.
Uwaga. Interfejs obsługi multiplekserów może się zmienić przed wydaniem wersji 1.0
Aby zwiększyć liczbę I/O należy zastosować multiplekser (jeden lub więcej).
Biblioteka testowana jest o popularny układ 74HC4067
, 16-to kanałowy analogowy multiplekser.
W celu opisania multipleksera wpiętego w układ należy utworzyć instancję klasy Mux
z opisem użytych pinów, a przy tworzeniu instancji przełączników lub kontrolek użyć klas
dedykowanych dla multipleksera.
Zakładając, że mamy jeden multiplekser wpięty do pinów:
- 2: pin "enable" multipleksera
- 3: pin "data" multipleksera
- 4, 5, 6, 7: piny sterujące "S0-S3" multipleksera, w kodzie programu należy zdefiniować multiplekser następująco:
// Mux(int pinEnable, int pinData, int pinS0, int pinS1, int pinS2, int pinS3);
Mux* mux1 = new Mux(2, 3, 4, 5, 6, 7);
a przełącznik baterii z przykładu należałoy zdefiniować następująco:
// MuxSwitch(Mux *mux, int channel, int nrBajtuRamki, int nrBitu);
Switch *bateria = new MuxSwitch(mux1, MUX_CH1, 0, 2);
Ponieważ multiplekser zajął pin 3, to lampkę czuwaka (z przykładu) należałoby podłączyć do innego pinu (np. 8). To wszystkie zmiany, które należy wprowadzić w celu przeniesienia przełącznika na multiplekser.
Maszynaduino posiada prostą konsolę szeregową do ułatwienia debugowania. Konsolę można podłączyć pod dowolny port szeregowy wpierany przez Arduino.
// Pulpit będzie komunikował się z PC podstawowym portem (`Serial`)
Console *sm42 = new Console(&Serial);
// Konsola debugowania będzie używała portu `Serial1`
ConsoleDebug *debug = new ConsoleDebug(sm42, &Serial1);
void setup() {
sm42->setup();
debug->setup();
}
void loop() {
sm42->update();
sm42->transmit();
// wysłanie debug output na konsolę debugowania
debug->send();
}
Aby wykorzystać dodatkowe porty szeregowe do debugowania, konieczna będzie płytka obsługująca większą liczbę portów szeregowych (typu Arduino Mega, Arduino Due) oraz adapter USB.
Konsolę debugowania można też uruchomić także na urządzeniach z jednym portem szeregowym (typu Arduino Uno).
W tej sytuacji nie powino się transmitować danych z/do PC (za pomocą Console::transmit()
), bo zaburzy to dzialanie konsoli.
Transmisję danych warto wtedy uruchamiać warunkowo. Przykład:
#define DEBUG
#ifdef DEBUG
ConsoleDebug *debug = new ConsoleDebug(sm42, &Serial);
#endif
void setup() {
// ...
#ifdef DEBUG
debug->setup();
#endif
// ...
}
void loop() {
// ...
#ifdef DEBUG
debug->send();
#else
sm42->update();
sm42->transmit();
#endif
// ...
}
Ponieważ konsola debugowania wysyła polecenia terminala do "czyszczenia" ekranu, zalecane jest korzystanie z Putty lub Screena zamiast Serial Monitora wbudowanego w Arduino IDE. Przykładowo:
screen /dev/ttyACM0 57600
Transmisja konsoli debugowania wymaga ustawienia prędkości na 57600 bodów. W przyszłości będzie możliwa zmiana.
- przełączniki (bezpośrednio do pinów)
- przełączniki (przez multiplekser)
- kontrolki (bezpośrednio do pinów)
- kontrolki (przez multiplekser)
- przełączniki wielopozycyjne
- wskaźniki / mierniki (direct i mux)
- ułatwienie obsługi wielu multiplekserów (do 4 szt, common data i select)
- optymalizacje, użycie przerwań, ewentualna przebudowa i stabilizacja API
- gotowe rozwiązania dla mierników, manometrów (np. servo)
Pytania i sugestie proszę wpisywać za pomocą https://github.com/marcinn/maszynaduino/issues
Czy można korzystać z biblioteki za darmo?
Tak.
Czy mogę budować swoje produkty w oparciu o nią?
Tak, ale z zachowaniem zasad licencji GPL v3.0 (w skrócie - powstały produkt także musi być tak samo licencjonowany i dystrybuowany).
Biblioteka nie posiada bardzo potrzebnej funkcji.
Należy opisać problem, sugestię lub zapotrzebowanie za pomocą https://github.com/marcinn/maszynaduino/issues.