![[Rozmiar: 10329 bajtów]](naglowek.gif)
Program będzie korzystał z portu COM i umożliwiał dwustronną komunikację ze sterownikiem.
![[Rozmiar: 36897 bajtów]](termocontrol.jpg)
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.
![[Rozmiar: 7784 bajtów]](instalator.gif)
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ć.
![[Rozmiar: 37989 bajtów]](visual1resize.jpg)
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.
![[Rozmiar: 37989 bajtów]](visual2resize.jpg)
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
![[Rozmiar: 13661 bajtów]](err_resize.jpg)
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.
![[Rozmiar: 13631 bajtów]](sense_resize.jpg)
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.
![[Rozmiar: 8796 bajtów]](form1resize.jpg)
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.
![[Rozmiar: 23382 bajtów]](settings_resize.jpg)
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)
![[Rozmiar: 23382 bajtów]](items_resize.jpg)
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.
![[Rozmiar: 13208 bajtów]](ostrzezenie_resize.jpg)
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.