Utilisation des produits Dracal avec différents langages de programmation

L'ensemble des produits Dracal USB supportés par l'enregistreur QTenki, et donc tous nos capteurs USB, peuvent être utilisés dans une multitude de langages de programmation avec une approche simple qui consiste à exécuter un outil en ligne de commande et en traiter la sortie. Cette page démontre comment cela peut se faire dans différents langages et environnements.

1) Prérequis

  • L'outil en ligne de commande usbtenkiget (pour l'obtenir, voir ci-dessous)
  • Connaissances élémentaires d'utilisation de la ligne de commande

Utilisateurs sous Windows: Comment obtenir usbtenkiget.exe

L'outil en ligne de commande usbtenkiget est fourni avec QTenki. Sous Windows, installez d'abord QTenki. Après l'installation, vous trouverez usbtenkiget.exe dans le répertoire d'installation (généralement, il s'agira de "c:program filesQTenki" ou "c:program files (x86)QTenki").

Utilisateurs sous Linux: Comment compiler usbtenkiget

Sous Linux, l'outil en ligne de commande usbtenkiget doit être compilé à partir du code source. Suivez les instructions de la page Utilisation des capteurs Dracal sous Linux.

2) Utiliser usbtenkiget

Vous trouverez ci-après un bref résumé des commandes de base de notre outil en ligne de commande usbtenkiget. Pour en approfondir le contenu, consultez la Documentation de usbtenkiget. Comme pour la plupart des outils en ligne de commandes, de l'aide peut être affichée en utilisant l'option -h. Puisque les options sont généralement assez simples, elles ne seront pas expliquées en détail ici. Mais les deux plus importantes seront démontrées.

Lister les capteurs et leurs canaux

Des informations sur les capteurs USB présentement connectés peuvent être affichées via la commande -l, tel que démontré ci-dessous:

usbtenkiget -l
Found: 'USBTenki', Serial: 'B10004', Version 1.2, Channels: 11
    Channel 0: Sensirion SHT1x/7x Temperature [Temperature]
    Channel 1: Sensirion SHT1x/7x Relative Humidity [Relative Humidity]
    Channel 2: MPX4115 Absolute air pressure sensor [Pressure]
    Virtual Channel 256: Dew point [Dew point]
    Virtual Channel 257: Humidex [Humidex]
    Virtual Channel 258: Heat index [Heat index]

Dans l'exemple ci-haut, un seul capteur (Dracal PTH-01-RH) de numéro de série B10004 était présent lors de l'exécution de la commande. Ce capteur possède 3 canaux véritables (des données mesurées) et 3 canaux virtuels (données calculées à parti des données mesurés). Chaque canal, soit-il réel ou virtuel, est identifié par un numéro.

Note: Sous Linux, si aucun capteur n'est détecté, il est probable que votre compte utilisateur n'ait pas les droits requis. Consultez la section sur la configuration des droits d'accès au besoin.

Lire les valeurs d'un capteur

La valeur d'un canal peut être récupérée en passant le numéro correspondant à l'argument -i. Lorsque plus qu'un canal est requis, les identifiants sont séparés par des virgules, tel que ci-dessous. Les valeurs retournées sont elles aussi séparées par des virgules et sont affichées dans l'ordre demandé:

usbtenkiget -i 0,1,256
22.46, 39.55, 8.02

Le résultat ci-dessus est ce que votre programme devra interpréter. Si vous affichez simplement les valeurs, essayez -p ("pretty output") pour un résultat convivial avec des unités. En parlant d'unités, consultez la page d'aide (option -h) et la documentation de usbtenkiget pour savoir comment les modifier.

Utiliser plusieurs capteurs

L'exemple précédent fonctionnera sans problèmes s'il n'y a qu'un capteur d'installé. Mais si plusieurs capteurs sont présents, seules les valeurs du premier capteur trouvé seront lues.

Les valeurs d'un capteur spécifiques peuvent être obtenues en mentionnant son numéro de série en ligne de commande à l'aide de l'option -s, comme ceci:

usbtenkiget -i 0,1,256 -s B10004
22.46, 39.55, 8.02

Ceci couvre les bases de l'utilisation de l'outil usbtenkiget.

3) Exemples dans différents langages de programmation

3.1) Bash

L'exécution de usbtenkiget à partir d'un script bash se fait comme pour n'importe quelle autre commande, pourvu que l'exécutable usbtenkiget soit dans le path. De nombreuses techniques peuvent être employées pour traiter le retour de usbtenkiget. Voici un exemple utilisant cut pour séparer les champs, et ensuite bc pour comparer les valeurs.

#!/bin/bash

# Note: usbtenkiget assumed to be in path.
# You may need to use -s to read from a specific sensor if you have more than one.
VALUES=`usbtenkiget -i a`
if (( $? )); then
	echo "Sensor not found or error"
fi

# Use cut, specifying a comma delimiter, and requesting
# field 1 for temperature, field 2 for RH...
TEMPERATURE=`echo $VALUES | cut -d ',' -f 1`
RELATIVE_HUMIDITY=`echo $VALUES | cut -d ',' -f 2`
PRESSURE=`echo $VALUES | cut -d ',' -f 3`

# Display the separate values now stored in variables
echo "Temperature: $TEMPERATURE"
echo "RH: $RELATIVE_HUMIDITY"
echo "Pressure: $PRESSURE"

# Example using bc to check how temperature compares to 29.5
if (( $(echo "$TEMPERATURE > 29.5" | bc -l) )); then
	echo "Too hot!"
else
	echo "Comfortable"
fi

3.2) C (POSIX)

Dans les environnements ou elle est disponible, la fonction popen permet d'exécuter usbtenkiget en redirigeant la sortie vers un flux de type FILE. La fonction fscanf peut alors servir pour extraire les valeurs vers des variables.

#include <stdio.h>
int getTempAndHumidity(float *temp, float *hum)
{
	FILE *fptr;
	float t, h;
	int n, res;

	fptr = popen("usbtenkiget -i a", "r");
	if (!fptr) {
		perror("popen");
		return -1;
	}

	n = fscanf(fptr, "%f, %f", &t, &h);
	res = pclose(fptr);

	if (res==-1) { return -2; }
	if (n<2) { return -3; }

	if (temp) { *temp = t; }
	if (hum) { *hum = h; }

	return 0;
}

int main(void)
{
	float temperature, humidity;
	int res;

	res = getTempAndHumidity(&temperature, &humidity);
	if (res<0) {
		return res;
	}

	printf("Temperature: %.2fnHumidity: %.2fn", temperature, humidity);

	return 0;
}

3.3) C/C++ (Win32)

Avec l'API Win32, usbtenkiget peut être exécuté avec la fonction CreateProcess. La sortie de usbtenkiget est dirigée vers un tuyau (pipe) duquel le parent (le code d'exemple) lira pour recevoir les valeurs retournées par usbtenkiget, valeurs qui seront ensuite traitées et transformées en variables float.

// win32Example.cpp
//
// Based on "Creating a Child Process with Redirected Input and Output"[1] from
// Microsoft Docs, but heavily modified and simplified for this specific use case.
//
// [1] https://docs.microsoft.com/en-us/windows/desktop/procthread/creating-a-child-process-with-redirected-input-and-output
//
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <math.h>

#define BUFSIZE 4096

/* Execute usbtenkiget and return values, converted to float. Nan is used
 * for signaling illegal values (some sensors may return "err" or Nan when
 * a low level error occurs.
 *
 * param cmdline Pointer to a string for the command to execute. Eg: TEXT("usbtenkiget -i 1")
 * param values Pointer to float*. A properly sized array of floats will be allocated with malloc.
                 Must be freed using free() by the caller.
 * param n_values Pointer to an int where the number of received fields will be stored.
 * return false on error, true on success. values does not need to be freed if an error was returned.
 */
BOOL getUsbTenkiValues(const TCHAR *cmdline, float **values, int *n_values)
{
	PROCESS_INFORMATION piProcInfo;
	STARTUPINFO siStartInfo;
	BOOL bSuccess = FALSE;
	HANDLE g_hChildStd_OUT_Rd = NULL;
	HANDLE g_hChildStd_OUT_Wr = NULL;
	SECURITY_ATTRIBUTES saAttr;
	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
	saAttr.bInheritHandle = TRUE;
	saAttr.lpSecurityDescriptor = NULL;
	CHAR chBuf[BUFSIZE];
	TCHAR *cmdline_copy = NULL;

	if (!values || !n_values) {
		return false;
	}

	// Create a pipe for the child process's STDOUT.
	if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
		fprintf(stderr, "Could not create pipen");
		return false;
	}

	// Set up members of the PROCESS_INFORMATION structure.
	ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
	ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
	siStartInfo.cb = sizeof(STARTUPINFO);
	siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
	siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

	/* Create a copy of the command-line passed in argument. CreateProcessW
	 * may modify the string, so we must be sure the string passed to CreateProcess
	 * is NOT in read-only memory (such as a const variable or a literal string) */
	cmdline_copy = _tcsdup(cmdline);
	if (!cmdline_copy) {
		fprintf(stderr, "Could not allocate memory");
		CloseHandle(g_hChildStd_OUT_Wr);
		CloseHandle(g_hChildStd_OUT_Rd);
		return false;
	}

	// Create the child process.
	bSuccess = CreateProcess(NULL,
		cmdline_copy,  // command line
		NULL,          // process security attributes
		NULL,          // primary thread security attributes
		TRUE,          // handles are inherited
		0,             // creation flags
		NULL,          // use parent's environment
		NULL,          // use parent's current directory
		&siStartInfo,  // STARTUPINFO pointer
		&piProcInfo);  // receives PROCESS_INFORMATION

	// Free the command-line copy as it won't be needed anymore
	free(cmdline_copy);

	// Close the writing end of the pipe now that the child has inherited it. Otherwise
	// the read loop below will never stop.
	CloseHandle(g_hChildStd_OUT_Wr);

	if (!bSuccess) {
		CloseHandle(g_hChildStd_OUT_Rd);
		fprintf(stderr, "Could not run usbtenkigetn");
		return false;
	}

	// Wait until usbtenkiget exits
	WaitForSingleObject(piProcInfo.hProcess, INFINITE);

	// Close handles to the child process
	CloseHandle(piProcInfo.hProcess);
	CloseHandle(piProcInfo.hThread);

	// Now read the output that was buffered in the pipe. Read at most
	// BUFSIZE-1 to be sure we always get a NUL-terminated string.
	ZeroMemory(chBuf, sizeof(chBuf));
	bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE-1, NULL, NULL);

	// Close the read end of the pipe too, now that we are done.
	CloseHandle(g_hChildStd_OUT_Rd);

	if (!bSuccess) {
		fprintf(stderr, "Could not read from usbtenkigetn");
		return false;
	}

	/* Now we have comma-separated values in chBuf. Count how many fields. */
	int fields = 1;
	for (char *c = chBuf; *c; c++) {
		if (*c == ',')
			fields++;
	}

	/* Allocate memory for the array of floats, and convert fields to floats. */
	*n_values = fields;
	*values = (float*)calloc(fields, sizeof(float));
	if (!*values) {
		fprintf(stderr, "Could not allocate memory for array of valuesn");
		return false;
	}
	float *dst_value = *values;
	char *c = chBuf;
	for (char *c = chBuf; c; )
	{
		if (1 != sscanf_s(c, "%f", dst_value)) {
			*dst_value = nanf("");
		}
		dst_value++;
		c = strchr(c, ',');
		// skip , for next value, otherwise c is NULL and loop stops
		if (c)
			c++;
	}

	return true;
}

int main()
{
	BOOL bSuccess;
	float *values = NULL;
	int n_values;

	bSuccess = getUsbTenkiValues(TEXT("usbtenkiget -i 0,1,2�"), &values, &n_values);
	if (!bSuccess) {
		return -1;
	}

	if (bSuccess) {
		// We run usbtenkiget with the -i 0,1,2 argument which requests
		// exactly 3 channels. Getting any other quantity is therefore an error.
		if (n_values != 3) {
			fprintf(stderr, "usbtenkiget returned wrong number of fieldsn");
			return -1;
		}
		else {
			printf("Temperature (C): %.2fn", values[0]);
			printf("RH......... (%%): %.2fn", values[1]);
			printf("Pressure. (kPa): %.2fn", values[2]);
			printf("Temperature (F): %.2fn", values[0] * 9 / 5 + 32);
		}
	}

	if (values) {
		free(values);
	}

	return 0;
}

3.4) C#

Avec C# et le Framework .NET, la classe Process permet d'exécuter usbtenkiget. La sortie de usbtenkiget est redirigée et transformée en chaîne de caractères par ReadLine(). Un tableau des valeurs individuelles peut alors être bâti en divisant la chaîne avec Split avant de convertir le tout en Float.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DracalCSexample
{
    class Program
    {
        static void Main(string[] args)
        {
            Process usbtenki = new Process();
            usbtenki.StartInfo.FileName = "usbtenkiget";
            usbtenki.StartInfo.Arguments = "-i 0,1,2";
            usbtenki.StartInfo.UseShellExecute = false;
            usbtenki.StartInfo.RedirectStandardOutput = true;

            try
            {
                usbtenki.Start();
            }
            catch (Exception e)
            {
                Console.Error.WriteLine("could not run usbtenkiget: " + e);
                return;
            }

            usbtenki.WaitForExit();

            String output = usbtenki.StandardOutput.ReadLine();

            if (output == null)
            {
                Console.Error.WriteLine("usbtenkiget did not return data");
                return;
            }

            float[] fields = output.Split(',').Select(field => float.Parse(field)).ToArray();

            if (fields.Length != 3)
            {
                Console.Error.WriteLine("usbtenkiget returned an incorrect number of fields");
                return;
            }

            Console.WriteLine("Temperature (C): " + fields[0]);
            Console.WriteLine("RH......... (%): " + fields[1]);
            Console.WriteLine("Pressure. (kPa): " + fields[2]);
            Console.WriteLine("Temperature (F): " + (fields[0] * 9 / 5 + 32));
        }
    }
}

Un exemple en C# dans lequel usbtenkiget est gardé en vie (via log mode) plutôt que d'être redémarré pour chaque lecture est aussi disponible: usingUsbtenkiRunMode.cs

3.5) C++/CLI (.NET)

Avec C++/CLI et le Framework .NET, la classe Process permet d'exécuter usbtenkiget. La sortie de usbtenkiget est redirigée et transformée en chaîne de caractères par ReadLine(). Un tableau des valeurs individuelles peut alors être bâti en divisant la chaîne avec Split avant de convertir le tout en Double.

#include "stdafx.h"
#include 

using namespace System;
using namespace System::Diagnostics; // for Process

int main(array ^args)
{
	Process^ usbtenki = gcnew Process;

	usbtenki->StartInfo->FileName = "usbtenkiget";
	usbtenki->StartInfo->Arguments = "-i 0,1,2";
	usbtenki->StartInfo->UseShellExecute = false;
	usbtenki->StartInfo->RedirectStandardOutput = true;

	try {
		usbtenki->Start();
	}
	catch (Exception^ e) {
		Console::Error->WriteLine("could not start usbtenkiget: " + e);
		return 1;
	}

	usbtenki->WaitForExit();

	String^ output = usbtenki->StandardOutput->ReadLine();
	if (!output) {
		Console::Error->WriteLine("usbtenkiget did not return data");
		return 1;
	}

	array<String^>^ fields = output->Split(',');
	array^ values = gcnew array(fields->Length);

	int i = 0;
	for each (String^ str in fields) {
		if (!System::Double::TryParse(str, values[i])) {
			values[i] = std::numeric_limits::quiet_NaN();
		}
		i++;
	}

	Console::WriteLine("Temperature (C): " + values[0]);
	Console::WriteLine("RH..........(%): " + values[1]);
	Console::WriteLine("Pressure..(kPa): " + values[2]);
	Console::WriteLine("Temperature (F): " + (values[0] * 9 / 5 + 32));

    return 0;
}

3.6) Java

import java.io.*;

public class JavaExample
{
	public static void main(String[] args)
	{
		Process usbtenkiget;

		// Note: usbtenkiget assumed to be in the path
		// Arguments passed to -i (0,1,2) here need to be updated to fit
		// your scenario. You may also specify a serial number by adding
		// the -s argument.
		try {
			usbtenkiget = Runtime.getRuntime().exec("usbtenkiget -i 0,1,2");
		} catch(IOException e) {
			System.err.println("could not run usbtenkiget: " + e);
			return;
		}

		// Wait until the process exits
		while(true) {
			try {
				usbtenkiget.waitFor();
			} catch (InterruptedException e) {
				continue;
			}
			break;
		}

		// Check if usbtenkiget exited with an error code
		if (usbtenkiget.exitValue() != 0) {
			System.err.println("usbtenkiget error. Exit value=" + usbtenkiget.exitValue());
			return;
		}

		BufferedReader reader = new BufferedReader(new InputStreamReader(usbtenkiget.getInputStream()));

		// usbtenkiget outputs the data on the first line. Read it to a string.
		String line;
		try {
			line = reader.readLine();
		} catch (IOException e) {
			System.err.println("Error reading data: " + e);
			return;
		}

		// Now split the line in an array of values.
		String[] values = line.split(",");

		// Check that we received the expected number of fields (in this case,
		// the usbtenkiget -i 0,1,2 argument requests 3 fields).
		if (values.length != 3) {
			System.err.println("Incorrect number of fields received: " + values.length);
			return;
		}

		float temperature = Float.parseFloat(values[0]);
		float rh = Float.parseFloat(values[1]);
		float pressure = Float.parseFloat(values[2]);

		System.out.println("Temperature (C):" + temperature);
		System.out.println("RH......... (%):" + rh);
		System.out.println("Pressure..(kPa):" + pressure);
		System.out.println("Temperature (C):" + (temperature*9/5+32));
	}
}

3.7) Node.js

En node, l'exécution de usbtenkiget peut se faire à l'aide de execFile. L'exécution terminée, la sortie de usbtenkiget est disponible dans une chaîne de caractères qui peut alors être traitée.

const execFile = require('child_process').execFile;

const child = execFile('usbtenkiget', ['-i','0,1,2'], (error, stdout, stderr) => {
	if (error) {
		throw error;
	}

	// Remove everything following the first newline character, then
	// split using a comma separator, then trim each field.
	var fields = stdout.replace(/(rn|n|r)*/gm,"").split(",").map(s => s.trim());

	// Validate how many fields were read
	if (fields.length != 3) {
		throw "Wrong number of fields"
	}

	// Display individual values
	console.log("Temperature.(C): " + fields[0]);
	console.log("RH..........(%): " + fields[1]);
	console.log("Pressure..(kPa): " + fields[2]);
	console.log("Temperature.(F): " + (fields[0] * 9 / 5 + 32));

});

3.8) Python

En Python, subprocess.check_output se charge d'exécuter usbtenkiget. Un appel à la méthode split permet de séparer les champs qui seront alors transformés en nombre à virgule flottante par float().

#!/usr/bin/python
import sys,import subprocess

# Note: usbtenkiget assumed to be in the path
# Arguments passed to -i (0,1,2) here need to be updated to fit
# your scenario. You may also specify a serial number by adding
# the -s argument.

# If usbtenkiget exits with a non-zero values, the subprocess.CalledProcessError
# exception will be raised. Catch it.
try:
    p = subprocess.check_output(["usbtenkiget","-i","0,1,2"])
except subprocess.CalledProcessError:
    print "usbtenkiget error"
    sys.exit(1)

fields = p.split(",");

# This example expects the following output:
#
# 24.48, 61.56, 100.40
#
# Where fields are temperature, rh and pressure.
#
# Detect errors by checking if the exact expected number
# fields was returned.
if len(fields) < 3:
    print "Error reading sensor"
    sys.exit(2)

# Convert the fields from strings to floating point values
# This step is necessary, otherwise math on values will not
# be possible.
temperature = float(fields[0])
rh = float(fields[1])
pressure = float(fields[2])

# Display values
print "Temperature (C):", temperature
print "RH......... (%):", rh
print "Pressure..(kPa):", pressure
print "Temperature (F):",temperature*9/5+32

sys.exit(0)

3.9) VB.Net

Avec VB.NET, la classe Process permet d'exécuter usbtenkiget. La sortie de usbtenkiget est redirigée et transformée en chaîne de caractères par ReadLine(). Un tableau des valeurs individuelles peut alors être bâti en divisant la chaîne avec Split avant de convertir le tout en Double.

Module VBExample
    Sub Main()

        ' Prepare a Process object for running usbtenkiget.
        ' This assumes that usbtenkiget is in your PATH, or
        ' that usbtenkiget.exe is present in the same directory
        ' as this program.
        Dim usbtenki As New Process()
        usbtenki.StartInfo.FileName = "usbtenkiget"
        usbtenki.StartInfo.Arguments = "-i 0,1,2"
        usbtenki.StartInfo.UseShellExecute = False
        usbtenki.StartInfo.RedirectStandardOutput = True

        ' Run usbtenkiget and wait until it exits
        usbtenki.Start()
        usbtenki.WaitForExit()

        ' Read one line of what was output by usbtenkiget
        Dim output = usbtenki.StandardOutput.ReadLine()

        If output Is Nothing Then
            Console.Error.WriteLine("usbtenkiget did not return data")
            Return
        End If

        ' Split the line into fields stored in an array, trim individual fields
        ' to remove extra spaces before fields.
        Dim fields() As String
        fields = output.Split(",").Select(Function(s) s.Trim()).ToArray()

        ' Check that the expected number of fields were read.
        ' In this case, due to the use of the -i 0,1,2 usbtenkiget
        ' argument, exactly 3 are expected.
        If fields.Length <> 3 Then
            Console.Error.WriteLine("usbtenkiget returned an incorrect number of fields")
            Return
        End If

        Console.Out.WriteLine("Temperature (C): " & fields(0))
        Console.Out.WriteLine("RH..........(%): " & fields(1))
        Console.Out.WriteLine("Pressure..(kPa): " & fields(2))
        Console.Out.WriteLine("Tempearture.(F): " & fields(0) * 9 / 5 + 32)

    End Sub
End Module

3.9) Visual Basic 6

Sous Visual Basic 6, la fonction Shell() permet d'exécuter usbtenkiget, mais sans permettre d'en rediriger la sortie pour la lire directement. La stratégie employée dans cet exemple est donc de rediriger la sortie vers un fichier, pour ensuite le lire depuis Visual Basic.

Un fichier batch se charge d'exécuter usbtenkiget et d'en rediriger la sortie car la redirection vers un fichier ne semble pas fonctionner avec Shell().. (c'est à dire que "usbtenkiget > tmpfile.txt" ne fonctionne pas). Le sub createWrapper() génère automatiquement ce fichier batch s'il n'existe pas déjà.

usbtenkiget %* > tmpfile.txt

Dans le fichier batch ci dessus, les caractères %* permettent de transférer les arguments passés au fichier batch à usbtenkiget. Les arguments -i et -s passés au fichier batch parviendront donc à usbtenkiget. Le caractère > quant à lui redirige la sortie vers un fichier.

Tel que décrit dans les commentaires de la fonction getAndRefreshValues(), l'exécution du fichier batch (et de usbtenkiget) se déroule parallèlement à l'application VB. Idéalement, il faudrait attendre que l'exécution du fichier batch prenne fin avant de lire le fichier. Malheureusement, VB ne permet pas nativement de le faire. (il est possible d'y arriver avec des appels à des API externes telles que WaitForSingleObject, mais cette technique n'est pas employée ici).

Dans cet exemple, afin d'éviter de lire des donnés incomplètes ou nulles, le fichier est lu d'abord, juste avant le lancement du fichier batch. Cela a comme effet que chaque appel à getAndRefreshValues() retournera des valeurs passées. Par exemple, si la fonction est appelée aux 5 seconds, ce sera toujours des valeurs vieilles de 5 secondes qui seront retournées. Bien que la situtation de compétition demeure, les chances de complications sont faibles si les appels à getAndRefreshValues ne sont pas trop rapprochés. Un appel aux 5 seconds est probablement peu risqué, sachant que dans des circonstances normales (capteurs fonctionnels) usbtenkiget s'exécute en moins d'une seconde.

Bien entendu, c'est à vous d'améliorer l'exemple pour vos besoin, sinon d'en comprendre et accepter les limites, les risques et les compromis...

Private Declare Function getTempPath Lib "kernel32" Alias _
 "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Const MAX_PATH = 255

' Return the path of a temporary directory this process has
' permission to access. A batch file and temporary file
' will be created there.
Public Function getTempDir() As String
    Dim sRet As String, lngLen As Long
    sRet = String(MAX_PATH, 0)
    lngLen = getTempPath(MAX_PATH, sRet)
    If lngLen = 0 Then Err.Raise Err.LastDllError
    getTempDir = Left$(sRet, lngLen)
End Function

Public Function getWrapperPath()
    wrapper = getTempDir() & "runusbtenkiget.bat"
    getWrapperPath = wrapper
End Function
Public Function getTempFilePath()
    getTempFilePath = getTempDir() & "dracalsensordata.txt"
End Function
Public Function getUsbtenkigetPath()
    getUsbtenkigetPath = """c:program Files (x86)qtenkiusbtenkiget"""
End Function

' Create the batch file wrapping usbtenkiget if it does not already exist
Private Sub createWrapper()
    tmpfile = getTempFilePath()
    usbtenkigetpath = getUsbtenkigetPath()
    wrapper = getWrapperPath()

    If Dir(wrapper) <> "" Then
        Exit Sub
    Else
        Open wrapper For Output Access Write As #1
        ' Note: %* means all arguments passed to the batch file will
        ' be forwarded to usbtenkiget.
        Print #1, usbtenkigetpath & " %* < " & tmpfile
        Close #1
    End If
End Sub

' Run the batch file which will run usbtenkiget, redirecting
' the output to a temporary file. The batch file will be
' created automatically if it does not already exist.
Public Function runWrapper(arguments As String)
    Dim returnedData As String

    createWrapper
    Shell getWrapperPath() & " " & arguments, vbHide
End Function

' Try to read the the last values written to the temporary files.
Public Function readLastValues() As String
    On Error GoTo nofile
    Open getTempFilePath() For Input As #1

    On Error GoTo nodata
    Line Input #1, returnedData
    Close #1
    readLastValues = returnedData

nofile:
    Exit Function
nodata:
    ' Line raised an error, but we must still close the file!
    Close #1
End Function

Public Function getAndRefreshValues(arguments As String) As Double()
    Dim result As String
    Dim values() As String
    Dim convertedValues() As Double

    ' The batch file will run in parallel with VB6. There is a race
    ' condition. To avoid issues, the last value is read first. But
    ' this creates a lag equal to the time between updates.
    result = readLastValues()

    ' Once the last values were read, run the batch file. Hopefully
    ' it exits BEFORE next time this function gets called... (do not
    ' call it too often... 5 second intervals are probably safe.
    runWrapper (arguments)

    ' Now try to split the comma-separated fields...
    values() = Split(result, ",")
    If UBound(values) < 0 Then Err.Raise 1000

    ' ...and convert them to floating point numbers
    ReDim convertedValues(UBound(values)) As Double
    For i = 0 To UBound(values)
        convertedValues(i) = CDbl(values(i))
    Next i

    getAndRefreshValues = convertedValues()
End Function

Rem *************** Example UI code ************

Private Sub updateDisplay()
    On Error GoTo getfailed

    ' Note: You may need to change the options passed to usbtenkiget here.
    values = getAndRefreshValues("-i 0,1,2")

    On Error GoTo baddata
    For i = 0 To UBound(values)
    valuesLabel(i).Caption = Format(values(i), "0.00")
    Next i

    ' Success. Show timestamp in status label.
    messageLabel.Caption = "Last update: " & Format(Now, "YYYY-MM-dd hh:mm:ss")

    Exit Sub

    ' Display errors in status label.
getfailed:
    If Err.Number = 1000 Then
        messageLabel.Caption = "No data or no sensor connected"
    Else
        messageLabel.Caption = "Could not update values: " & Err.Description
    End If
    Exit Sub
baddata:
    messageLabel.Caption = "No data: " & Err.Description
End Sub

Private Sub Form_Load()
    ' Start the application with fresh data
    updateDisplay
End Sub

Private Sub Timer1_Timer()
    ' Timer runs every 5 seconds
    updateDisplay
End Sub

4) License et avis de non-responsabilité

Sauf indication contraire, les extraits de code sur cette page sont placés dans le domaine public et peuvent être incorporés dans tout logiciel, commercial ou non.

Les exemples sur cette page sont fournis "en l'état", dans l'espoir qu'ils puissent servir, mais sans AUCUNE GARANTIE de quelque nature que ce soit, expresse ou implicite, y compris, mais sans y être limité, les garanties implicites de commerciabilité et de la conformité à une utilisation particulière.

Les pratiques recommandées de programmation, telles que la gestion et détection d'erreurs, la validation des entrées, et les tests, incombent sont sous la responsabilité de l'utilisateur, qui assume également la totalité des risques liées à la qualité et aux performances du code.

Dracal technologies inc. n'accepte aucune responsabilité quant à l'exactitude, l'intégralité, la performance ou la fiabilité du code sur cette page. Dracal technologies inc. ne pourrait être tenue responsable à votre égard des dommages, incluant les dommages génériques, spécifiques, secondaires ou consécutifs, résultant de l'utilisation ou de l'incapacité d'utiliser le code (y compris, mais sans y être limité, la perte de données, ou le fait que des données soient rendues imprécises, ou les pertes éprouvées par vous ou par des tiers).