Program będzie korzystał z portu COM i umożliwiał dwustronną komunikację ze sterownikiem.
Pisać będę na raty, a potem ten komentarz usunę ;)
Zacząć trzeba od ściągnięcia instalatora. Poniżej link do takiego jakiego użyłem ja. Jest to instalator Online, który sam sprawdza co potrzebne i instaluje tylko potrzebne elementy.
Program informuje, że po czasie będzie wymagał darmowej rejestracji w Microsofcie. Nie miałem z tym problemu bo konto na Microsofcie miałem już od dawna. To takie samo konto jak w Google. Tez daje dostęp do przestrzeni dyskowej OneDrive i tym podobnych usług.
Kiedy uda się pomyślnie zainstalować oczom naszym ukaże się coś takiego. Obrazki będą mniejsze by strona się wczytywała, ale dostępne są po kliknięciu w powiększeniu. Z tego co pamiętam to chyba program zabiera na szybką wycieczkę albo tworzenie przykładowego programu do przeglądania zdjęć. Można się bawić.
Jak już opanujemy tworzenie nowego projektu to wyświetli się okienko nowo tworzonego programu Form1. Klikając raz na ten element wyświetlą nam się w okienku po prawej jego "Properties" - właściwości. Tam na pozycji Text możesz już nazwać swój projekt. Rozciągnij go i umieść w nim GroupBoxy bo potem ładnie wyglądają i pozwalają podpisać a nawet zarządzać naraz całą grupą. Do projektu przyda się też przeciągnąć port Com i Timery. Może się zdarzyć że na jakiś element klikniesz dwukrotnie Wtedy otworzy się karta w kodem programu. Możesz przełączyć się na kartę "Design" z powrotem jak w Chrome na razie. Tak potem znajduje się w kodzie część odpowiedzialną za obsługę np. przycisku lub dodaje taki kod.
Uruchomienie aplikacji w trybie debugowania ma same plusy. Nie kompiluje się długo i możemy co moment zmieniając jakiś parametr znów ja włączyć. Jeśli z jakiegoś powodu aplikacja spowoduje błąd i się wysypie to w normalnym trybie Windows dostaniemy suchy komunikat o błędzie. W trybie debugowania zostaniemy przeniesieni do linii kodu która powoduje problem. Dostaniemy tez podpowiedź co poszło nie tak :D. Przykład poniżej
To kawałek kodu odpowiedzialny za automatyczne połączenie ze sterownikiem jeśli zahaczony jest CheckBox1. Przed otwarciem portu musimy określić numer portu i prędkość. W tym przypadku ustawiony był numer portu który akurat nie istnieje, bo to wirtualny port USB który wyjąłem z gniazda. Nie mógł więc być przyjęty jako wartość i spowodował błąd. Takie błędy da się omijać, ale o tym później. Kiedy zaznajomisz się z treścią komunikatu wyłącz debugowanie by dokonać poprawek.
Przy pisaniu bardzo pomocne mogą być poniżej podlinkowane poradniki video, oraz ..google :)
Szukając czegokolwiek do swojej aplikacji dopisz w wyszukiwarce po odstępie magiczne "vb.net"
Odpowiedzi w postaci dokumentacji są wtedy na msdn.microsoft.com, a przykłady rozwiązań problemów zadanych przez użytkowników na stackoverflow.com.
Zanim zaczniemy trzeba też wspomnieć o tym wynalazku, bo ktoś nieuprzedzony mógłby się zdenerwować. Kiedy zacznie się coś wpisywać to narzędzie IntelliSense będzie chciało nam pomóc i podsuwa szereg podpowiedzi pod wpisywanym przez nas tekstem. Takie lepsze T9 ;) Trzeba się tylko przyzwyczaić do zatwierdzania wyboru przyciskiem TAB, a nie Enter`em. Trzeba też patrzeć co się wybiera. Jeśli mamy CheckBoxy to będą tam ponumerowane, ale będzie też taki bez numeru i jak na złość jako pierwsza podpowiedź. Trzeba wybrać ten o który nam chodzi.
Jak po komendzie postawisz kropkę, to będzie chciało podpowiadać dalej... i tak do szczęśliwego końca :D
Żeby móc skorzystać z obsługi portów albo na przykład robiąc aplikację graficzną obsługiwać grafiki musimy do naszej aplikacji zaimportować zbiory instrukcji. Importujemy je na początku by potem móc używać zawartych w nich komend.
Zmienne możemy definiować bezpośrednio w Suba`ach, ale możemy wtedy na nich działać tylko w ich obrębie. Żeby móc wynik jakiegoś procesu odczytać w innym Sub`ie można zdefiniować na początku programu zmienną globalną. Widzianą dla wszystkich i wszędzie. Wbrew pozorom, często przydaje się właśnie to by wartość pozostała tylko w Sub`ie.
Żeby dodać potrzebne nam Importy i zadeklarować zmienne klikamy w Designerze na naszą aplikację. Najlepiej na jej pasek z nazwą. Otworzy się karta z kodem programu i przy okazji kodem który wykonuje się zaraz przy uruchamianiu naszego programu.
Importy wpisujemy nad wszystkim. Na samej górze. Zmienne globalne wpisujemy do Public Class nad wszystkimi prywatnymi.
Imports System Imports System.Threading Imports System.IO.Ports Imports System.ComponentModel Public Class Form1 Dim myPort As Array 'posłuży do zebrania informacji o wszystkich dostępnych portach Dim TestArray As String() 'zmienna Test podzielona/rozdzielona na małe stringi z indeksami Dim Test As String = "" 'wiadomość odebrana ze sterownika, po testach nazwa została :D Dim tryb As Integer 'tryb w jakim pracuje sterownik ręczny/automatyczny Dim zaczekaj As Boolean 'pomocniczy Bit by pominąć jeden odczyt
Muszę w tym miejscu wrócić na chwilę do ustawień projektu. Przy starcie programu potrzebne bowiem będą zmienne których ustawienia są pamiętane i odczytywane. Trzeba je zadeklarować na osobnej karcie w ustawieniach.
Tu, wspomnę o czymś bo może ktoś nie rozumie. Ze szkoły wiemy że 2 x 2 = 4. W języku programowania znak "równa się" jest jednak inaczej interpretowany. Jest to znak przyrównania. Wartość lub wynik operacji działań po prawej stronie znaku jest przypisywany jako wartość dla zmiennej stojącej po lewej stronie znaku. Żeby więc jakiemuś parametrowi w naszym programie nadać wartość na starcie, pamiętaną w ustawieniach, to trzeba ten parametr przyrównać do pamiętanej zmiennej. I tak dla przykładu przy starcie programu:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load myPort = IO.Ports.SerialPort.GetPortNames() 'myPort przyjmuje informacje o dostępnych portach COM portComboBox.Items.AddRange(myPort) 'rozwijana lista portów przyjmuje wartości z myPort baudComboBox.Text = My.Settings.Baud_rate 'tekst przyjmuje wartość ostatnio zapamiętanego wyboru portComboBox.Text = My.Settings.Port_numer 'tekst nazwy portu przyjmuje zapamiętaną wartość CheckBox2.Text = My.Settings.Aux1 'nazwa dla dodatkowego wyjścia przyrównana do zapisanej CheckBox3.Text = My.Settings.Aux2 CheckBox1.Checked = My.Settings.Autostart 'wczytuje z pamięci czy połączyć automatycznie
Natomiast w drugą stronę. Żeby ustawienia "się pamiętały" trzeba w Desgnerze kliknąć dwukrotnie w CheckBoxa którego stan chcemy pamiętać i otworzyć obsługujący to zdarzenie kod.
Private Sub CheckBox1_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles CheckBox1.CheckedChanged My.Settings.Autostart = CheckBox1.Checked 'zmienna Autostart w My.Settings przyjmuje wartość CheckBoxa End Sub
Wróćmy na moment do Designera i, jeśli ktoś jeszcze tego nie zrobił, wybierzmy styl dla ComboBoxów i wpiszmy prędkości transmisji (Baudrate)
Przy okazji omówię instrukcję Try, Catch, End Try, wyskakujące okienko oraz uaktywnianie przycisków.
Jeśli mamy przycisk "Połącz" to klikamy go w Designerze by otworzyć kod obsługujący jego przyciśnięcie i dodajemy instrukcje. Port będzie się nazywał tak jak wybraliśmy w portComboBoxie, prędkość transmisji taka jak w baudComboBoxie. Otwieramy port. To jest funkcja więc na końcu nawiasy. Uruchamiamy Timer o którym za chwilę. Timer będzie odliczał czas do odświeżania wyświetlaczy. Uaktywniamy lub dezaktywujemy wybrane przyciski lub nawet grupy. Można w ten sposób zapobiec wpisaniu czegoś gdy jeszcze nie ma to sensu. Na przykład dopiero od momentu połaczenia aktywny jest przycisk "Rozłącz."
Private Sub Connect_button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Connect_button.Click Try SerialPort1.PortName = portComboBox.Text SerialPort1.BaudRate = baudComboBox.Text SerialPort1.Open() 'otwieramy port Timer1.Start() 'uruchamiamy Timer1 zaczekaj = True 'pomijamy jeden cykl odczytu (niekonieczne) baudComboBox.Enabled = False 'wyłączamy możliwość zmiany na otwartym porcie portComboBox.Enabled = False 'możliwość wyboru portu nieaktywna Connect_button.Enabled = False 'połączony więc przycisk "Połącz" nieaktywny Temp_button.Enabled = True 'aktywuj wysyłanie ustawionej temperatury TrackBar1.Enabled = True ' suwak do ustawiania PWM TrackBar2.Enabled = True ' suwak do ustawiania pilnowanej temperatury TrackBar3.Enabled = True ' suwak do ustawiania histerezy Disconnect_Button.Enabled = True ' przycisk "Rozłącz" Button1.Enabled = True ' wybór trybu pracy Label6.Enabled = True ' automatycznym/ręcznym (tylko człon się zmienia) GroupBox6.Enabled = True ' dodatkowe wyjścia Catch MessageBox.Show("Wybierz numer portu COM", "Ostrzeżenie") End Try End Sub
Instrukcje Try, Catch, End Try informują komputer ,że ma podjąć próbę. Jeśli się nie powiedzie z jakiegoś powodu to ma zrobić to, co po instrukcji Catch. Możemy tam nic nie wpisać, ale wtedy komputer przemilczy wszystko i nie będziemy wiedzieć dlaczego nic się nie wyświetla. Używając Try, Catch, End Try musimy użyć wszystkich trzech. Nie można pominąć Catch.
O tych poleceniach dowiedziałem się dużo później więc musiałem najpierw zadbać by program zachowywał się poprawnie bez nich. Nie używam ich też w trybie Debugowania. Dopisuję je do programu na końcu. Przydają się na nieprzewidziane zachowania. Np. przy otwieraniu plików użytkownik się rozmyśla.. W tym akurat przypadku "wyskoczy" okienko z podpowiedzią by wybrać jakiś port.
Podobnie postępujemy z przyciskiem "Rozłącz"
Private Sub Disconnect_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Disconnect_Button.Click Timer1.Stop() 'zatrzymaj Timer1 Connect_button.Enabled = True 'aktywuj i dezaktywuj przyciski Temp_button.Enabled = False PWM_button.Enabled = False Disconnect_Button.Enabled = False TrackBar1.Enabled = False TrackBar2.Enabled = False TrackBar3.Enabled = False Button1.Enabled = False baudComboBox.Enabled = True portComboBox.Enabled = True Label6.Enabled = False GroupBox6.Enabled = False SerialPort1.Close() 'zamknij port End Sub
Nie jest tak ważne żeby program sam się łączył ze sterownikiem jeśli tak zaznaczymy. Ważne jest żeby przy starcie program do portComboBoxa wpisał dostępne w danej chwili porty COM. Mając jednak CheckBox1 sprawdzam go i program może się łączyć automatycznie.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load myPort = IO.Ports.SerialPort.GetPortNames() 'najważniejszy tutaj zapis portComboBox.Items.AddRange(myPort) baudComboBox.Text = My.Settings.Baud_rate portComboBox.Text = My.Settings.Port_numer CheckBox2.Text = My.Settings.Aux1 CheckBox3.Text = My.Settings.Aux2 CheckBox1.Checked = My.Settings.Autostart 'odczytaj czy ma połączyć automatycznie If CheckBox1.Checked = False Then 'jeśli NIE zaznaczono "Połącz automatycznie" PWM_button.Enabled = False Temp_button.Enabled = False Disconnect_Button.Enabled = False TrackBar1.Enabled = False Button1.Enabled = False baudComboBox.Enabled = True portComboBox.Enabled = True Label6.Enabled = False GroupBox6.Enabled = False ElseIf CheckBox1.Checked = True Then 'jeśli zaznaczono TAK Try 'to spróbuj SerialPort1.PortName = portComboBox.Text SerialPort1.BaudRate = baudComboBox.Text SerialPort1.Open() Timer1.Start() zaczekaj = True baudComboBox.Enabled = False portComboBox.Enabled = False Connect_button.Enabled = False Temp_button.Enabled = True TrackBar1.Enabled = True Disconnect_Button.Enabled = True Button1.Enabled = True Label6.Enabled = True GroupBox6.Enabled = True Catch 'jeśli cos pójdzie źle MessageBox.Show("Wybierz numer portu COM", "Ostrzeżenie") 'pokaż ostrzeżenie End Try End If End Sub
W kodzie brakuje jeszcze obsługi portComboBox i baudComboBox. Klikamy je więc po kolei w Designerze i dodajemy instrukcję obsługi zdarzenia gdy ktoś będzie chciał nimi coś wybrać w programie.
Private Sub portComboBox_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles portComboBox.SelectedIndexChanged My.Settings.Port_numer = portComboBox.Text End Sub
Private Sub baudComboBox_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles baudComboBox.SelectedIndexChanged My.Settings.Baud_rate = baudComboBox.Text End Sub
Timery i Port trzymają się razem w lewym rogu Designera bo to komponenty. Kilkamy więc raz na Port by pokazała się jego karta Properties. W niej u góry znajdujemy żółty obrazek przedstawiający piorun. Będzie podpisany jako Events. Klikamy go i wybieramy "DataReceived" dwukrotnie klikając tak by otworzyć kod programu który obsługuje to zdarzenie
Deklarujemy Incomming jako String do którego trafiaja odebrane dane z Portu. Na końcu string przepisywany jest do zmiennej Test
Private Sub SerialPort1_DataReceived(sender As System.Object, e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived Dim Incoming As String = SerialPort1.ReadLine() 'są różne metody czytania portu, ta czyta linię Test &= Incoming End Sub
Ten program współpracuje ze sterownikiem który wysyła co sekundę wiadomość z kilkoma parametrami systemu.
Był to mój pierwszy taki projekt i tak to rozwiązałem, że wysyła je rozdzielone spacją a program je po tych spacjach dekoduje.Parametry można też zmieniać przy samym sterowniku więc potrzebne jest odświeżanie wszystkich danych. Tak więc kawałek kodu z Atmegi wygląda tak:
Tekst_pwm = Str(x) Tekst_t = Str(odczyt) Tekst_tryb = Str(tryb) Tekst_pilnuj = Str(pilnuj) Tekst_hist = Str(hist) Message = Tekst_tryb + " " + Tekst_t + " " + Tekst_pwm + " " + Tekst_pilnuj + " " + Tekst_hist + " " Print Message
Program sprawdza port co 500ms i dekoduje wiadomość. Żeby robił to z taką częstotliwością potrzebny jest Timer1. Klikamy raz by pokazała się karta jego właściwości i ustawiamy Interwał na 500
Po czym klikamy Timer1 znowu, tylko dwukrotnie żeby otworzyć kod który będzie się wykonywał co zadany interwał.
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick Dim TestArray() As String = Split(Test) 'podziel wiadomość na małe ponumerowane Try 'spróbuj If TestArray.Length > 4 Then 'jeśli wiadomość prawidłowa bo ma więcej niż 4 człony TextBox3.Clear() 'wyczyść okno PWM TextBox3.Text = TestArray(2) 'wpisz wartość trzeciej jako tekst (zaczyna się od zera) TextBox1.Clear() 'wyczyść okno Temperatura TextBox1.Text = TestArray(1) 'wpisz wartość drugiej jako tekst TrackBar1.Value = TestArray(2) 'rusz suwak do wartości PWM TrackBar2.Value = 800 + TestArray(3) 'wartość dla suwaka temp. pilnowanej TextBox2.Clear() 'wyczyść okno pilnowanej temp. TextBox2.Text = TrackBar2.Value - 800 'wpisz wartość do okna TextBox4.Text = TestArray(4) 'histereza TrackBar3.Value = TextBox4.Text If zaczekaj = False Then 'to było zabezpieczenie gdy sterownik odpowiadał echem tryb = CInt(TestArray(0)) 'przekonwertuj na wartość Else zaczekaj = False 'skasuj pominiecie odczytu End If End If Catch End Try If tryb = 0 Then 'jeśli wykryto tryb 0 to zmień text w Label6 Label6.Text = "automatycznym" PWM_button.Enabled = False TrackBar1.Enabled = False GroupBox4.Enabled = True ElseIf tryb = 1 Then Label6.Text = "ręcznym" PWM_button.Enabled = True TrackBar1.Enabled = True GroupBox4.Enabled = False End If Test = 0 'zeruj zmienna Test (powinno być Nothing ale działało :) End Sub
Teraz jeśli wyślesz taką wiadomość na port to program ją zdekoduje i umieści w okienkach.
Suwaki reprezentują też aktualne wartości i z powodu co półsekundowego odświeżania niemożliwe było by ustawienie nimi czegokolwiek. Zrobiłem więc tak, że ruszenie suwakiem wyłącza Timer1 odpowiedzialny za odświeżanie i uruchamia drugi Timer2 który to po zadanym czasie, 5 sekund, się wyłącza włączając najpierw z powrotem Timer1. Masz więc 5 sekund na wysłanie nowej zadanej sterownikowi wartości. Ustawiamy więc interwał na 5000 i klikamy podwójnie Timer2 by wyświetlić kod jego obsługi
Private Sub Timer2_Tick(sender As System.Object, e As System.EventArgs) Handles Timer2.Tick If Timer1.Enabled = False Then Timer1.Start() Timer2.Stop() End If End Sub
Tu zmieniamy jedną wartość suwakiem. Kliknij w suwak dwukrotnie by otworzyć kod obsługi zdarzenia kiedy ktoś go przesunie.
Private Sub TrackBar1_ValueChanged(sender As System.Object, e As System.EventArgs) Handles TrackBar1.ValueChanged Timer1.Stop() Timer2.Start() TextBox3.Text = TrackBar1.Value 'wartość wyświetla się w okienku End Sub
Po czym masz 5 sekund by nacisnąć "Ustaw"
Private Sub PWM_button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PWM_button.Click SerialPort1.Write(TrackBar1.Value & vbCr) 'wpisz do portu wartość z suwaka i zatwierdź "CarriageReturn"(enter) zaczekaj = True 'gdy sterownik miał włączone Echo to pomijało jeden odczyt End Sub
Zostawiłem tu takie rozwiązania które już poprawiłem specjalnie żeby pokazać ile udało mi się dowiedzieć w dwa dni. Od tego czasu trochę się nauczyłem do czego i Ciebie zachęcam :) Nie zawsze wiąże się wszystko z czytanie opasłych tomów, albo zaczynaniem od zera. Masz Internet możesz więcej. Jeśli uważasz, że w opisie czegoś jeszcze brakuje to napisz na forum a ja postaram się temat opracować i dopisać dla Wszystkich. Np. nie opisałem jeszcze sposobu tworzenia ikonki do programu i pewnych ustawień: Twórz samodzielną aplikację, oraz Zapisuj ustawienia przy zamykaniu programu - dopiszę w wolnym czasie.