SMC1000i Schrittmotorsteuerung

  • VB.NET

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    SMC1000i Schrittmotorsteuerung

    Ich habe mir mal die SMC1000i Steuerung zugelegt.
    Jetzt versuche ich gerade mit VB auf sie zuzugreifen.
    Per Terminal (HTerm) hat alles gut geklappt!
    Mit VB kann ich zwar die Version abfragen ("@V" & vbcr), aber
    egal welchen Befehl ich schicke, ich bekomme immer die
    Versionsmeldungsquittierung zurück geliefert. Sonst passiert nix.

    Zur Info: Es ist so, dass das Interface immer mit <READY> (ACK = 6) , <ERROR> (BEL = 7) oder <Busy> (NAK =21) antworten sollte.
    Es sendet selbst kein Zeilenende (kein CR oder LF).
    Ich hatte schon probiert auf <READY> zu warten, weil man ja nur dann wieder Befehle schicken könnte.
    Ja, irgendwann kommt <READY> aber der restliche Text ist eben die Quittierung.
    Also muss ich ja annehmen, dass das Senden nicht richtig klappt.
    Darum habe ich auch noch das Encoding auf ascii eingestellt.

    Was macht das Terminalprogramm denn alles anders als VB?

    Gab es da nicht auch mal eine Möglichkeit die Daten zurückzuschleifen, bevor sie den PC verlassen,
    so dass ich sehen kann was tatsächlich gesendet wurde?

    Ich werde weiter probieren, aber hat jemand noch eine Idee?
    @Lightsource Kann es sein, dass Du immer die Versionsabfrage sendest?
    Logge Deine Befehle und die Antworten des Geräts.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich hatte es innerhalb des Programms geloggt, da war nichts Komisches
    (von der PC-Sendeseite aus gesehen) außer halt die Versionsmeldungen
    nach jedem Befehl, so als würde der noch im Puffer hängen.
    Inzwischen habe ich mal einfach die Rückmeldung ignoriert, und siehe da, die
    anderen Befehle werden angenommen.

    Aber natürlich brauche ich für verschiedene Sachen die Rückmeldungen.
    Wie ist es mit dem Puffer, wird der automatisch beim Lesen geleert,
    oder muss ich das durch das Programm explizit erledigen?
    Ich habe zwar schon versucht den Puffer zu löschen, aber da bin ich mir nicht so sicher.
    Es ist ja so, dass das Interface keine richtigen Zeilenabschlüsse sendet, sondern nur die
    oben erwähnten Zeichen.
    Ich hatte auch versucht NewLine-Zeichen vorzugeben, aber das bringt wohl auch nichts, weil
    ich ja 3 verschiedene Zeilenabschlüsse haben könnte (und dann noch der Sendezeilenabschluss).

    Es gibt keinen Handshake. Muss ich dann aber vielleicht dem PC irgendwelche Signale
    vorgaukeln?
    @Lightsource Poste mal Deinen Code.
    Wenn ich mir die Beschreibung von @exc-jdbi ansehe, sollte das ganze ein Kinderspiel sein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ja, das ist auch die offizielle Beschreibung, die beim Gerät dabei ist.

    Mit dem Code bringe ich den Schrittmotor jedenfalls schon mal zum Laufen...

    Die Rückmeldungen müsste ich dann wohl in DataReceived abfangen?

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Text
    4. Public Class Schrittmotoren
    5. Dim offen As Boolean = False
    6. Dim schlafzeit As Integer = 50
    7. Dim speed As Integer = 20
    8. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    9. If SP1.IsOpen Then
    10. SP1.WriteLine("@R" & vbCr) ' Reset
    11. SP1.Close()
    12. End If
    13. End
    14. End Sub
    15. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    16. Dim ascii As New ASCIIEncoding()
    17. offen = Not offen
    18. If offen Then
    19. SP1.Open()
    20. SP1.Encoding = ascii
    21. SP1.ReceivedBytesThreshold = 1
    22. Button1.Text = "Close"
    23. 'SP1.NewLine = 6
    24. ssend("@V", schlafzeit) ' Version
    25. ssend("@R", schlafzeit) ' Reset
    26. ssend("#S50", schlafzeit) ' StartSpeed
    27. For k As Integer = 1 To 9
    28. ssend("#E" & k.ToString & "," & speed.ToString, schlafzeit) ' EndSpeeds
    29. Next
    30. ssend("#R200", schlafzeit) ' Rampe?
    31. ssend("L1,X" & "-100", 5000) ' Absolutfahrt auf -100
    32. ssend("@R", schlafzeit)
    33. ssend("L1,X" & "100", 5000) ' Absolutfahrt auf 100
    34. Else
    35. SP1.Close()
    36. Button1.Text = "Open"
    37. End If
    38. End Sub
    39. Sub ssend(ByVal SText As String, ByVal schlafzeit As Integer)
    40. 'Debug.Print(SText)
    41. For n As Integer = 0 To SText.Length - 1
    42. 'Debug.Print(SText.Substring(n, 1))
    43. SP1.Write(SText.Substring(n, 1))
    44. Next
    45. SP1.Write(vbCr)
    46. 'SP1.WriteLine(SText)
    47. Threading.Thread.Sleep(schlafzeit)
    48. End Sub
    49. Private Sub TrackBar1_Scroll(sender As System.Object, e As System.EventArgs) Handles TrackBar1.Scroll
    50. Dim wert As Integer
    51. wert = TrackBar1.Value
    52. ssend("@B", schlafzeit)
    53. ssend("L1,X" & wert.ToString, schlafzeit)
    54. TB1.Text = wert.ToString
    55. End Sub
    56. Private Sub SP1_DataReceived(sender As Object, e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SP1.DataReceived
    57. 'Dim ttext As String=""
    58. Dim tchar As Integer
    59. 'Dim tcharS As String
    60. Do ' Warten auf <READY>
    61. tchar = SP1.ReadChar
    62. 'tcharS = SP1.ReadLine
    63. 'ttext = ttext & Chr(tchar)
    64. 'Debug.Print(Asc(tchar).ToString & " " & tchar & " " & tchar.ToString & "-")
    65. 'Debug.Print(tcharS)
    66. Loop Until tchar = 6 'SP1.BytesToRead = 0
    67. 'SP1.DiscardInBuffer()
    68. 'SP1.DiscardOutBuffer()
    69. 'Debug.Print(ttext & vbCrLf)
    70. End Sub
    71. 'Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    72. ' TrackBar1.Value = Val(TB1.Text)
    73. ' Application.DoEvents()
    74. ' Dim wert As Integer = TrackBar1.Value
    75. ' ssend("@B" & wert.ToString, schlafzeit)
    76. ' ssend("L1,X" & wert.ToString, schlafzeit)
    77. ' TB1.Text = wert.ToString
    78. 'End Sub
    79. End Class

    Lightsource schrieb:

    Die Rückmeldungen müsste ich dann wohl in DataReceived abfangen?
    Im Handbuch stehen 50 ms bis zur Antwort.
    Da so ein Schrittmotor ein gemütlich Ding ist, würde ich keine asynchrone, sondern eine synchrone Kommunikation programmieren.
    Also:
    Du erstellst eine Funktion SendCommandWait4Answer(command As String, expected As String, ByRef answer as String) As Boolean.
    Diese
    formatiert den Befehl (CR dranhängen),
    sendet den Befehl
    wartet 50 ms
    liest die Antwort
    holt aus der Antwort die gewünschte Information => answer
    liefert Erfolg als Rückgabewert (Vergleich expected - answer) bzw. Timeout.
    Feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich verstehe was du meinst.

    Da das Interface aber keine "Endline"-Zeichen sendet, muss ich dann wohl den Empfangspuffer so lange
    einzeln auslesen, bis ich mal wieder ein <READY> bzw. <ERROR>-Zeichen bekomme.
    Wird der Puffer denn dann durch das Auslesen geleert, oder muss ich ihn extra löschen?
    @Lightsource Was Du rausholst, wird innen gelöscht.
    Probierma ein <Ready> als SerialPort.NewLine-Zeichen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vielen Dank.
    Im Moment klappt alles so, wie ich es auch erwarte.
    Auch das Auslesen der Endschalter geht. Das mache ich gerade mit einem Timer.
    Da dies Masterbefehle sind kann man sie jederzeit schicken.

    Das

    VB.NET-Quellcode

    1. Threading.Thread.Sleep(schlafzeit)

    da bin ich mir nicht sicher, ob das so sinnvoll ist. Oder sollte ich da auch einen Timer nehmen?

    Falls du noch Ideen zu Verbesserung hast?

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Text
    4. Public Class Schrittmotoren
    5. Dim offen As Boolean = False
    6. Dim schlafzeit As Integer = 50
    7. Dim speed As Integer = 200
    8. Dim ramp As Integer = 100
    9. Dim sspeed As Integer = 50
    10. Dim haltestrom As Integer = 10
    11. Dim motorstrom As Integer = 10
    12. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    13. If SP1.IsOpen Then
    14. SP1.WriteLine("@R" & vbCr) ' Reset
    15. SP1.Close()
    16. End If
    17. End
    18. End Sub
    19. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    20. Dim ascii As New ASCIIEncoding()
    21. offen = Not offen
    22. If offen Then
    23. SP1.Open()
    24. SP1.Encoding = ascii
    25. SP1.ReceivedBytesThreshold = 1
    26. Button1.Text = "Close"
    27. SP1.NewLine = Chr(CInt("6"))
    28. ssend("@V", schlafzeit) ' Version
    29. ssend("@R", schlafzeit) ' Reset
    30. ssend("#S" & sspeed.ToString, schlafzeit) ' StartSpeed
    31. ssend("c,X" & motorstrom.ToString & "," & haltestrom.ToString, schlafzeit)
    32. For k As Integer = 1 To 9
    33. ssend("#E" & k.ToString & "," & speed.ToString, schlafzeit) ' EndSpeeds
    34. Next
    35. ssend("#R" & ramp.ToString, schlafzeit) ' Rampe?
    36. Timer1.Start() ' Endschalterchecker
    37. ssend("L1,X" & "-200", 2000) ' Testfahrt auf -200
    38. ssend("@R", schlafzeit)
    39. ssend("L1,X" & "200", 2000) ' Testfahrt auf 200
    40. Else
    41. SP1.Close()
    42. Button1.Text = "Open"
    43. End If
    44. End Sub
    45. Sub ssend(ByVal SText As String, ByVal schlafzeit As Integer)
    46. Dim tchar As Integer
    47. Dim ttext As String = ""
    48. Try
    49. 'Debug.Print(SText)
    50. For n As Integer = 0 To SText.Length - 1
    51. SP1.Write(SText.Substring(n, 1))
    52. Next
    53. SP1.Write(vbCr)
    54. ListBox1.Items.Add(" -> " & SText & "CR")
    55. ListBox1.SelectedIndex = (ListBox1.Items.Count - 1)
    56. Threading.Thread.Sleep(schlafzeit)
    57. Dim raus As Boolean = False
    58. Do ' Warten auf <READY> etc.
    59. tchar = SP1.ReadChar
    60. Select Case tchar
    61. Case 6
    62. ttext = ttext & "<READY>"
    63. raus = True
    64. Case 7
    65. ttext = ttext & "<ERROR>"
    66. raus = True
    67. Case 21
    68. ttext = ttext & "<BUSY>"
    69. raus = True
    70. Case Else
    71. ttext = ttext & Chr(tchar)
    72. End Select
    73. Loop Until raus = True
    74. ListBox1.Items.Add(ttext)
    75. ListBox1.SelectedIndex = (ListBox1.Items.Count - 1)
    76. Application.DoEvents()
    77. Catch ex As Exception
    78. End Try
    79. End Sub
    80. Private Sub TrackBar1_Scroll(sender As System.Object, e As System.EventArgs) Handles TrackBar1.Scroll
    81. Dim wert As Integer
    82. wert = TrackBar1.Value
    83. ssend("@B", schlafzeit)
    84. ssend("L1,X" & wert.ToString, schlafzeit)
    85. TB1.Text = wert.ToString
    86. End Sub
    87. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    88. Dim Eingabe As Integer = 0
    89. Eingabe = CInt(Val(TB1.Text))
    90. If Eingabe > 180 Then Eingabe = 180
    91. If Eingabe < -180 Then Eingabe = -180
    92. TrackBar1.Value = Eingabe
    93. Application.DoEvents()
    94. Dim wert As Integer = TrackBar1.Value
    95. ssend("L1,X" & wert.ToString, schlafzeit)
    96. TB1.Text = wert.ToString
    97. End Sub
    98. Private Sub ND_Sleep_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_Sleep.ValueChanged
    99. schlafzeit = CInt(ND_Sleep.Value)
    100. End Sub
    101. Private Sub ND_Speed_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_Speed.ValueChanged
    102. speed = CInt(ND_Speed.Value)
    103. For k As Integer = 1 To 9
    104. ssend("#E" & k.ToString & "," & speed.ToString, schlafzeit) ' EndSpeeds
    105. Next
    106. End Sub
    107. Private Sub ND_SSpeed_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_SSpeed.ValueChanged
    108. sspeed = CInt(ND_SSpeed.Value)
    109. ssend("#S" & sspeed.ToString, schlafzeit) ' StartSpeed
    110. End Sub
    111. Private Sub ND_Ramp_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_Ramp.ValueChanged
    112. ramp = CInt(ND_Ramp.Value)
    113. ssend("#R" & ramp.ToString, schlafzeit) ' Rampe?
    114. End Sub
    115. Private Sub ND_Hold_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_Hold.ValueChanged
    116. haltestrom = CInt(ND_Hold.Value)
    117. ssend("c,X" & motorstrom.ToString & "," & haltestrom.ToString, schlafzeit)
    118. End Sub
    119. Private Sub ND_Drive_ValueChanged(sender As System.Object, e As System.EventArgs) Handles ND_Drive.ValueChanged
    120. motorstrom = CInt(ND_Drive.Value)
    121. ssend("c,X" & motorstrom.ToString & "," & haltestrom.ToString, schlafzeit)
    122. End Sub
    123. Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
    124. Dim Eingabe As Integer = 0
    125. Eingabe = CInt(Val("-180"))
    126. If Eingabe > 180 Then Eingabe = 180
    127. If Eingabe < -180 Then Eingabe = -180
    128. TrackBar1.Value = Eingabe
    129. Application.DoEvents()
    130. Dim wert As Integer = TrackBar1.Value
    131. ssend("L1,X" & wert.ToString, schlafzeit)
    132. TB1.Text = wert.ToString
    133. End Sub
    134. Private Sub Button5_Click(sender As System.Object, e As System.EventArgs) Handles Button5.Click
    135. Dim Eingabe As Integer = 0
    136. Eingabe = CInt(Val("+180"))
    137. If Eingabe > 180 Then Eingabe = 180
    138. If Eingabe < -180 Then Eingabe = -180
    139. TrackBar1.Value = Eingabe
    140. Application.DoEvents()
    141. Dim wert As Integer = TrackBar1.Value
    142. ssend("L1,X" & wert.ToString, schlafzeit)
    143. TB1.Text = wert.ToString
    144. End Sub
    145. Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
    146. ' NotAus und Endschalter erkennen
    147. Dim stext As String = "@I"
    148. Dim ttext As String
    149. For i As Integer = 1 To 4
    150. stext = stext & i.ToString
    151. For n As Integer = 0 To SText.Length - 1
    152. SP1.Write(SText.Substring(n, 1))
    153. Next
    154. SP1.Write(vbCr)
    155. stext = "@I"
    156. ttext = SP1.ReadLine
    157. If ttext.Length >= 4 Then
    158. Select Case ttext.Substring(0, 2)
    159. Case "@I"
    160. Select Case ttext.Substring(2, 1)
    161. Case "1"
    162. If ttext.Substring(4, 1) = "0" Then
    163. BX.BackColor = Color.White
    164. Else
    165. BX.BackColor = Color.Black
    166. End If
    167. Case "2"
    168. If ttext.Substring(4, 1) = "0" Then
    169. BY.BackColor = Color.White
    170. Else
    171. BY.BackColor = Color.Black
    172. End If
    173. Case "3"
    174. If ttext.Substring(4, 1) = "0" Then
    175. BZ.BackColor = Color.White
    176. Else
    177. BZ.BackColor = Color.Black
    178. End If
    179. Case "4"
    180. If ttext.Substring(4, 1) = "0" Then
    181. BNotAus.BackColor = Color.White
    182. Else
    183. BNotAus.BackColor = Color.Black
    184. End If
    185. End Select
    186. End Select
    187. End If
    188. Next
    189. End Sub
    190. End Class

    VB.NET-Quellcode

    1. ' Dim ascii As New ASCIIEncoding() <----- unnötig
    2. offen = Not offen
    3. If offen Then
    4. SP1.Open()
    5. 'SP1.Encoding = ascii <-------------unnötig
    6. SP1.ReceivedBytesThreshold = 1
    7. Button1.Text = "Close"
    8. Button1.Text = "Close"
    9. 'SP1.NewLine = Chr(CInt("6"))
    10. SP1.NewLine = Chr(6)

    ASCII ist das Standard-Encoding der Seriellen-Schnttstelle.

    CInt(Val("+180")) ist doppelt gemoppelt. Cint reicht oder für die .net Fanatiker Convert.ToInt32("180")

    Du brauchst die Zeichen nicht einzeln senden

    Statt

    VB.NET-Quellcode

    1. ...
    2. For n As Integer = 0 To SText.Length - 1
    3. SP1.Write(SText.Substring(n, 1))
    4. Next
    5. SP1.Write(vbCr)
    6. ...

    reicht

    VB.NET-Quellcode

    1. SP1.Write(SText & vbCr)
    @Lightsource Wenn Du die Befehle mit String.Format() formatierst, kannst Du sie sogar im Quelltext ordentlich lesen.
    Sleep(x) einzusetzen, um der Hardware etwas Zeit zu geben, ist völlig in Ordnung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Im Prinzip laufen meine Schrittmotoren wie gewünscht.

    Die Frage, die ich gerade habe, betrifft die Schnittstelle.
    Ich kann über ReadLine Rückmeldungen auslesen.
    Ich kann aber auch über DataReceived feststellen, ob etwas gesendet wurde.

    Kann ich beide "gleichzeitig" vewenden?
    Wenn ich z.B. einfach eine Motor-Position abfragen will, verwende ich ReadLine.
    Um einen Status abzufragen müsste ich dann mit einem Timer arbeiten.

    Wenn ich aber so schnell wie möglich einen Endschalter abfragen will, würde ich gerne DataReceived verwenden.
    Beim Auslesen wird doch aber der Buffer geleert, oder?

    Wie kann ich beide Probleme sinnvoll lösen?
    @Lightsource Dann arbeite ohne das DataReceived-Event. Beides zusammen funktioniert nicht so richtig.
    Schick der Steuerung den Befehl, warte kurz und lies die Antwort aus.
    Wenn die Steuerung Daten ohne Befehl schickt, musst Du am Inhalt erkennen, was da gesendet wurde, wenn Du per Timer abfragst.
    Oder Du hast ein Regime, bei dem Du zwischen Timer und DataReceived-Event umschaltest.
    Auf jeden Fall musst Du immer den Buffer leer lesen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Lightsource Wieso?
    Sendet das Teil ohne Anforderung?
    Wie ist der Unterschied der Antworten zu explizit abgefragten Werten?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Es sendet nur nach Anforderung, aber priorisiert nach Normal- bzw. Masterbefehl.

    Der Grund warum ich direkt die momentanen Werte wollte:
    Erstens hätte ich gerne, dass auf meiner Form die aktuellen Positionen in Labels, bzw. vielleicht auch mal in einer bildhaften Darstellung gezeigt werden.
    Zweitens will ich natürlich so schnell wie möglich auf ein ungewolltes Anstoßen an die Endschalter reagieren.
    Drittens wollte ich Zeiten stoppen, um die Bewegungen zu optimieren.

    Um die Endschalter abzufragen, gibt es z.B. einen so genannten Masterbefehl. Diesen kann man jeder Zeit, auch während die Motoren noch
    unterwegs sind, zur Steuerung absetzen und dann auch gleich das Ergebnis erhalten.

    Die Positionen kann man ebenfalls mit einem Masterbefehl abfragen. Das muss man für jede Achse einzeln machen.
    Das klappt zwar, hinkt aber sichtbar etwas hinterher.

    Da ich immer eine "Sendepause" von 50ms brauche, kann ich den TimerTick nicht kürzer als 100ms bringen. Mal sehen ob mir das reicht.
    @Lightsource Ich würde das ohne DataReceived-Event mit einem Timer machen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!