Forum: Compiler & IDEs Ubuntu pyvisa: Verbindung zum USB-seriellen Gerät


von Christoph M. (mchris)


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?

von Harald K. (kirnbichler)


Lesenswert?

Und wo wird Dein String "instrument_address" ausgewertet? Wo wird der 
pyvisa bekanntgegeben?

von Christoph M. (mchris)


Lesenswert?

>Und wo wird Dein String "instrument_address" ausgewertet? Wo wird der
>pyvisa bekanntgegeben?

Zum Testen nehme ich einen Arduino Nano mit diesem Beispiel:

https://github.com/Vrekrer/Vrekrer_scpi_parser/blob/master/examples/SCPI_Dimmer/SCPI_Dimmer.ino

Im Terminal meldet er sich:
1
*IDN?
2
Vrekrer,SCPI Dimmer,#00,v0.5.0

von Christoph M. (mchris)


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()

von Christoph K. (backdraft007)


Lesenswert?


von Christoph M. (mchris)


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()

: Bearbeitet durch User
von Christoph M. (mchris)


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
}

: Bearbeitet durch User
von Manuel H. (Firma: Universität Tartu) (xenos1984)


Angehängte Dateien:

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.

von Christoph M. (mchris)


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.

von Manuel H. (Firma: Universität Tartu) (xenos1984)


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 :)

von Christoph M. (mchris)


Lesenswert?

>Ja, wäre eine Idee, ich will das Projekt bei Gelegenheit auf Github
>laden.

Bin sehr gespannt ..

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

Schade ist da, dass es nicht von STM32Duino unterstützt wird:
https://github.com/stm32duino/Arduino_Core_STM32

von Manuel H. (Firma: Universität Tartu) (xenos1984)


Lesenswert?

> Schade ist da, dass es nicht von STM32Duino unterstützt wird:
> https://github.com/stm32duino/Arduino_Core_STM32

Ja, das stimmt. Aber vielleicht lässt es sich als STM32F429ZI Generic 
Board ansprechen, oder über CMSIS:

https://docs.platformio.org/en/latest/boards/ststm32/disco_f429zi.html

von Manuel H. (Firma: Universität Tartu) (xenos1984)


Lesenswert?


von Christoph M. (mchris)


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

von Manuel H. (Firma: Universität Tartu) (xenos1984)


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 :)

von Christoph M. (mchris)


Lesenswert?

Gerade habe ich noch mal etwas genauer über SCPI gelesen.
Es gibt wohl eine Art Standard-Kommandos, die alle Geräte implemtiert 
haben sollten. *IDN? ist auf jeden Fall eines davon.

https://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments

: Bearbeitet durch User
von Manuel H. (Firma: Universität Tartu) (xenos1984)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.