Ubuntu pyvisa: Verbindung zum USB-seriellen Gerät

#7846767
Lesenswert?

Ich versuche, ein SCPI-Gerät über die serielle Schnittstelle zu steuern:

1
import pyvisa
2

3
# Define the serial resource address (update this with your actual device port)
4
instrument_address = 'ASRL/dev/ttyUSB0::INSTR'
5

6
# Create a PyVISA resource manager
7
rm = pyvisa.ResourceManager()
8
rm.list_resources()
9

10
print(rm)

"rm.list_resources()" zeigt aber kein Gerät an. Woran könnte das liegen?

#7846880
Lesenswert?

OK, ich bin ein Stück weiter. Mit dem Logik-Analysator zeigt sich, dass pyvisa das *IDN? Kommando absendet. Nutzt man das Terminal-Programm in der Arduino-IDE, ist das Terminierungszeichen "0x0A" und der Arduino antwortet mit dem korrekten Identifikaitonstring.

Nimmt man hingegen das pyvisa script unten, sind die Terminierungszeichen "0x0d, 0x0a" was den Arduino zu verwirren scheint. Wie kann man die Terminierung in pyvisa umstellen?

1
#!/usr/bin/env python3
2

3
import pyvisa
4

5
# Define the serial resource address (update this with your actual device port)
6
instrument_address = 'ASRL/dev/ttyUSB0::INSTR'
7

8
# Create a PyVISA resource manager
9
rm = pyvisa.ResourceManager()
10

11
# Open the serial instrument with correct parameters
12
instrument = rm.open_resource(
13
    instrument_address,
14
    baud_rate=115200,  # Match your instrument's baud rate
15
    data_bits=8,
16
    parity=pyvisa.constants.Parity.none,
17
    stop_bits=pyvisa.constants.StopBits.one,
18
    timeout=5000  # Timeout in milliseconds
19
)
20

21
rm.list_resources()
22

23
# Send the *IDN? command and print the response
24
try:
25
    idn = instrument.query('*IDN?')
26
    print(f'Instrument IDN: {idn.strip()}')
27
finally:
28
    instrument.close()
#7846894
Lesenswert?

Christoph K. (backdraft007) 17.03.2025 11:53

Danke. Mittlerweile läuft es. Allerdings war es ein Doppelfehler, die bekanntlich besonders schwer zu finden sind: Einerseits das Problem mit den Endzeichen und zweitens braucht der Arduino-Nano etwas Zeit um zu starten, wenn man die serielle Schnittstelle neu öffnet. Deshalb muss unbedingt eine Wartezeit vor der Anfrage "*IDN?" eingebaut werden.

Here you go:

1
#!/usr/bin/env python3
2

3
# test USB SCPI Commands with an Arduino as Instrument
4
#
5
# Arduino test side:
6
# https://github.com/Vrekrer/Vrekrer_scpi_parser/blob/master/examples/SCPI_Dimmer/SCPI_Dimmer.ino
7
#
8
# 250317 mchris
9

10

11
import pyvisa
12
import time
13

14
# Define the serial resource address (update this with your actual device port)
15
instrument_address = 'ASRL/dev/ttyUSB0::INSTR'
16

17
# Create a PyVISA resource manager
18
rm = pyvisa.ResourceManager()
19

20
# Open the serial instrument with correct parameters
21
instrument = rm.open_resource(
22
    instrument_address,
23
    baud_rate=115200,  # Match your instrument's baud rate
24
    data_bits=8,
25
    parity=pyvisa.constants.Parity.none,
26
    stop_bits=pyvisa.constants.StopBits.one,
27
    timeout=5000  # Timeout in milliseconds
28
)
29

30
# Set read and write termination to carriage return (CR)
31
instrument.read_termination = '\n'  # '\n' represents line feed
32
instrument.write_termination = '\n'
33

34
print("wait till Arduino has started ..")
35
time.sleep(2)
36
print("list resources: ")
37
print(rm.list_resources())
38

39

40
print("get instrument identification:")
41
# Send the *IDN? command and print the response
42
try:
43
    idn = instrument.query('*IDN?')
44
    print(f'Instrument IDN: {idn.strip()}')
45
finally:
46
    instrument.close()
#7847018
Lesenswert?

Könnte man einen Arduino als einfaches SCPI Messgerät nutzen?

Wenn man statt *IDN? ein read ADC im obigen Pythonscript implementiert, kann man zumindest die ADC-Werte lesen.

1
print("get values:")
2

3
try:
4
    for n in range(1,10):
5
        value = instrument.query('ADC:READ 0')
6
        print(f'adc value: {value.strip()}')    
7
finally:
8
    instrument.close()

Was spricht dafür oder dagegen das SCIP-Format zu verwenden? IN der Implementierung sieht es einigermaßen umständlich aus:

1
/*
2

3
  ADC Control Commands
4

5
    Read ADC:
6
    ADC:READ <channel>
7

8
        Example: ADC:READ 0 to read from analog channel 0
9

10
    Set ADC Resolution:
11
    ADC:RES <bits>
12

13
        Example: ADC:RES 10 to set 10-bit resolution
14

15
    Configure ADC Reference:
16
    ADC:REF <reference>
17

18
        Example: ADC:REF INT for internal reference
19

20
  GPIO Control Commands
21

22
    Set Pin Mode:
23
    GPIO:MODE <pin>,<mode>
24

25
        Example: GPIO:MODE 13,OUT to set pin 13 as output
26

27
    Digital Write:
28
    GPIO:WRITE <pin>,<state>
29

30
        Example: GPIO:WRITE 13,1 to set pin 13 HIGH
31

32
    Digital Read:
33
    GPIO:READ <pin>
34

35
*/
36

37
void setup() {
38
  Serial.begin(115200); // Initialize Serial communication
39
  delay(1000);
40
  //Serial.println("scpi ready");
41
}
42

43
void loop() {
44
  //delay(1000);
45
  //Serial.println("hello");
46
  if (Serial.available()) {
47

48
    String command = Serial.readStringUntil('\n'); // Read incoming command
49
    //String command = Serial.readString(); // Read incoming command
50
    //Serial.println(command);
51
    command.trim(); // Remove whitespace and newline characters
52

53
    if (command.startsWith("ADC:READ")) {
54
      int channel = command.substring(9).toInt();
55
      int adcValue = analogRead(channel);
56
      Serial.println(adcValue);
57
    }
58
    else if (command.startsWith("ADC:RES")) {
59
      int bits = command.substring(8).toInt();
60
#if defined(ESP32)
61
      analogReadResolution(bits);
62
#endif
63
    }
64
    else if (command.startsWith("ADC:REF")) {
65
      if (command.substring(8).equals("INT")) {
66
        analogReference(INTERNAL);
67
      } else if (command.substring(8).equals("EXT")) {
68
        analogReference(EXTERNAL);
69
      } else {
70
        Serial.println("ERROR: Invalid ADC reference");
71
      }
72
    }
73
    else if (command.startsWith("GPIO:MODE")) {
74
      int commaIndex = command.indexOf(',');
75
      int pin = command.substring(10, commaIndex).toInt();
76
      String mode = command.substring(commaIndex + 1);
77

78
      if (mode.equals("OUT")) {
79
        pinMode(pin, OUTPUT);
80
      } else if (mode.equals("IN")) {
81
        pinMode(pin, INPUT);
82
      } else {
83
        Serial.println("ERROR: Invalid GPIO mode");
84
      }
85
    }
86
    else if (command.startsWith("GPIO:WRITE")) {
87
      int commaIndex = command.indexOf(',');
88
      int pin = command.substring(11, commaIndex).toInt();
89
      int state = command.substring(commaIndex + 1).toInt();
90

91
      if (state == 0 || state == 1) {
92
        digitalWrite(pin, state);
93
      } else {
94
        Serial.println("ERROR: Invalid GPIO state");
95
      }
96
    }
97
    else if (command.startsWith("GPIO:READ")) {
98
      int pin = command.substring(10).toInt();
99
      int pinState = digitalRead(pin);
100
      Serial.println(pinState);
101
    }
102
    else if (command.startsWith("*IDN?")) {
103
      Serial.println(F("Arduino Peripherial Control"));
104
    }
105
    else {
106
      Serial.println(F("ERROR: Unknown command"));
107
    }
108
  }
109
}
(Firma: Universität Tartu) #7847063
Lesenswert?

Im Prinzip geht das durchaus. Ich bastele gerade an einem solchen Projekt und habe hier ein paar Wavgat UNO (Arduino Klone) rumliegen, die einen LGT8F328P haben - der kommt sogar mit einem DAC. Im Moment benutze ich den Vrekrer SCPI Parser, will das aber noch umstellen und dann weitere Befehle ergänzen.

Angehängte Dateien:
#7847080
Lesenswert?

Wavgat UNO (Arduino Klone) rumliegen, die einen LGT8F328P haben

Dein Code ist gut lesbar geschrieben. Gefällt mir gut.
Ich wusste gar nicht, dass es Arduino328-artige mit DAC gibt. Vermutlich ein chinesischer Clone. Den Hersteller konnte ich nicht finden.

Im Moment benutze ich den Vrekrer SCPI Parser, will das aber noch umstellen und dann weitere Befehle ergänzen.

Wie möchtest du umstellen? Vielleicht lässt sich das Projekt so machen, dass man die Codebasis teilen kann.
Bezüglich der Peripherie sind auch die STM32L432KC nicht schlecht. Die haben 2x12 Bit DAC und sind vermutlich analog-technisch besser.

(Firma: Universität Tartu) #7847116
Lesenswert?

Dein Code ist gut lesbar geschrieben. Gefällt mir gut.

Danke!

Ich wusste gar nicht, dass es Arduino328-artige mit DAC gibt. Vermutlich ein chinesischer Clone. Den Hersteller konnte ich nicht finden.

Ja, ist chinesisch. Hier gibt es ein paar Infos und die passende Arduino-Bibliothek:

http://github.com/dbuezas/lgt8fx

Beim großen China-Markt gibt es die Boards schon um die 2,50€.

Wie möchtest du umstellen? Vielleicht lässt sich das Projekt so machen, dass man die Codebasis teilen kann.

Ja, wäre eine Idee, ich will das Projekt bei Gelegenheit auf Github laden. Ich würde gerne eine eigene möglichst kompakte (wenig Flash, wenig RAM) C++ SCPI-Bibliothek schreiben (bin auch schon dabei), die sich möglichst am Standard orientiert (z.B. lange und kurze Form von Kommandos, keine Unterscheidung von Groß- und Kleinschreibung).

Bezüglich der Peripherie sind auch die STM32L432KC nicht schlecht. Die haben 2x12 Bit DAC und sind vermutlich analog-technisch besser.

Ich habe mir ein STM32F429I Discovery Board genau für diesen Zweck besorgt :)

#7847285
Lesenswert?

Manuel H. (Firma: Universität Tartu)

Ja, das stimmt. Aber vielleicht lässt es sich als STM32F429ZI Generic Board ansprechen,

Das sollte gehen. Wenn es das Generic-Board gibt, kann man auf jeden Fall mit dem Arduino-Framework compilieren und laufen lassen. Es fehlen dann nur die Definitionen für die Peripherie das Discovery-Boards (z.B. die ON-Board LED ist dann nicht als LED_BUILTIN vorhanden, Display usw. )
Um den vollen Comfort zu erhalten, müssten im wesentlichen für das Discovery-Board nur die Dateien platform.txt und boards.txt angepasst werden.

Eine genauere Anleitung gibt es hier:
https://github.com/stm32duino/Arduino_Core_STM32/wiki/Add-a-new-variant-(board)

Etwas unschön ist beim STM32F429ZI Generic Board im Arduino-Framework ist, dass der Treiber für das Display fehlt. Deshalb wäre die Discovery-Board Auswahl nützlich. Es könnte sein, dass sich noch keiner erbarmt hat den Treiber einzupflegen und das Display ist ja eine der Hauptatraktionen des Discovery Boards.

https://blog.embeddedexpert.io/?p=2077

https://github.com/xenos1984/OpenSCPI

Vielen Dank. Sieht interessant aus.
Beim ersten drüber schauen fällt mir das Python-Beispiel auf der PC-Seite auf: Bei den Beispielen ist es immer gut, wenn es sehr einfache und dann die etwas anwenderfreunlichen, komplizierten Beispiele gibt. Das einfache Beispiel sollte aus nicht mehr als 10 Zeilen bestehen, bei dem die grundlegende Funktion sichtbar wird. Also z.B. lesen eines Wertes vom ADC und dann print. Viele Ersteller von Github-Repostitories neigen dazu, möglichst die gesamte Funktionalität mit allen API-Funktionen in ein Beispiel zu stecken. Das führt dann dazu, dass man das wesentliche Prinzip der Bibliothek nicht auf einen Blick erkennen kann.

Hier mal am Beispiel eines ADC1115 ADC:
== einfach ==
https://github.com/RobTillaart/ADS1X15/blob/master/examples/ADS_minimum/ADS_minimum.ino

== kompliziert ==
https://github.com/RobTillaart/ADS1X15/blob/master/examples/ADS_high_speed_differential/ADS_high_speed_differential.ino

(Firma: Universität Tartu) #7847304
Lesenswert?

Christoph M. schrieb:

Das sollte gehen. Wenn es das Generic-Board gibt, kann man auf jeden Fall mit dem Arduino-Framework compilieren und laufen lassen. Es fehlen dann nur die Definitionen für die Peripherie das Discovery-Boards (z.B. die ON-Board LED ist dann nicht als LED_BUILTIN vorhanden, Display usw. ) Um den vollen Comfort zu erhalten, müssten im wesentlichen für das Discovery-Board nur die Dateien platform.txt und boards.txt angepasst werden.

Ja, das stimmt, wobei sich damit zumindest für's erste wohl leben lässt.

Eine genauere Anleitung gibt es hier: https://github.com/stm32duino/Arduino_Core_STM32/wiki/Add-a-new-variant-(board)

Leider ist der Teil "Define a specific board" ganz unten noch Baustelle :D Aber das Board einzupflegen wäre sicher nicht schlecht.

Etwas unschön ist beim STM32F429ZI Generic Board im Arduino-Framework ist, dass der Treiber für das Display fehlt. Deshalb wäre die Discovery-Board Auswahl nützlich. Es könnte sein, dass sich noch keiner erbarmt hat den Treiber einzupflegen und das Display ist ja eine der Hauptatraktionen des Discovery Boards.

https://blog.embeddedexpert.io/?p=2077

Ja, das ist schon hübsch. Ich hatte mir auch überlegt, das Display als Live-Anzeige für die gemessenen bzw. ausgegebenen Daten zu benutzen.

Beim ersten drüber schauen fällt mir das Python-Beispiel auf der PC-Seite auf: ...

Ja, da hast du natürlich vollkommen Recht. Den Skript hatte ich erst einmal nur für mich selbst geschrieben, um zu testen, ob die gemessenen Daten überhaupt Sinn ergeben. Die "richtigen" Anwenderbeispiele, komplett mit Kommentaren, sollen noch folgen :)

(Firma: Universität Tartu) #7847416
Lesenswert?

Ja, richtig. Darüber, was diese Standard-Kommandos tun (sollen), schweigt sich der 800 Seiten lange SCPI Standard aber aus - und verweist auf IEEE 488.2 / IEC 60488-2, wo diese Kommandos definiert sind. Generell basiert SCPI auf IEEE 488.2 und viele Grundlagen sind stattdessen dort definiert, darunter Dinge wie Zeichensatz, Befehlsformat, Datenformate, Einheiten für Messgrößen...

Das ist auch ein Grund, warum ich statt Vrekrer lieber eine eigene SCPI-Bibliothek implementieren möchte, die bereits alle notwendigen Standard-Kommandos behandelt, und außerdem einen Parser für die IEEE 488.2 Datenformate und Einheiten enthält, den man direkt einbinden kann, statt im Sketch mit Zeichenketten zu arbeiten.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren