# hex Steuerbefehle an serielle Schnittstelle senden



## jower (28. April 2005)

Hallo,
ich würde gerne über die API Funktion WriteFile einen hexadezimalen Steuerbefehl über die serielle Schnittstelle an ein Gerät senden. Es handelt sich dabei um einen Statusrequest.Den hab ich in der Form "02 00 01 00 31 15 12" vorliegen. Mein Problem besteht nun darin, dass ich nicht weiss, wie ich das verschicken soll. Ich habe versucht, dass in einen 7 Felder großen Array zu schreiben (nach dem Motto: 
	
	
	



```
char data_status_req[7]; data_status_req[0] = 0x02;...
```
 ), funktioniert aber nicht.  Kann mir da vielleicht jemand den entscheidenden Tipp geben?
 Danke


----------



## Tobias K. (28. April 2005)

moin


Ein bscihen wenig INformationen die du da gibst, besonders in Hinsicht zu den Fehlern.

Aber hier ein Beispiel wie ich das mal gemacht hab:

```
#include <windows.h>
#include <iostream>

using namespace std;

int i=0;

int main()
{
	DCB           dcb;
	HANDLE hCom = CreateFile ("COM1", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
	dcb.DCBlength = sizeof(DCB);
	GetCommState (hCom, &dcb); 
	dcb.BaudRate  = 2400;
	dcb.ByteSize  = 8;   
	dcb.Parity    = NOPARITY;    
	dcb.StopBits  = ONESTOPBIT;  
	SetCommState (hCom, &dcb);


	DWORD iBytesWritten;
	unsigned char ucMsg[5];

	for(int i=0; i < 8; i++)
	{
		ucMsg[0] = (char)13;
		ucMsg[1] = (char)1;
		ucMsg[2] = 'S';
		ucMsg[3] = (char)49 + i;
		ucMsg[4] = ucMsg[0] ^ ucMsg[1] ^ ucMsg[2] ^ ucMsg[3];

		WriteFile (hCom, ucMsg, 5, &iBytesWritten, NULL);
		//WriteFile (hCom, ucMsg, 5, &iBytesWritten, NULL);
		
		Sleep(350);
		//cin.get();
	}

	CloseHandle (hCom);

	return 0;
}
```


mfg
umbrasaxum


----------



## jower (28. April 2005)

Danke erstmal, hätte das gerne auch ein wenig genauer beschreiben wollen, aber ich weiss nicht so ganz, wie sich dem Problem nähern kann. Das einzige, was ich feststellen konnte, ist, dass ich von dem Gerät keine Antwort erhalte.
Wenn ich das richtig verstehe, sendest Du die Befehle dezimal. Mein versuch war es das hexadezimal zu machen. Sieht doch eigentlich ähnlich aus, oder? Nur bei mir funktioniert´s nicht. 

```
char data_status_req[7];
data_status_req[0] = 0x02;
data_status_req[1] = 0x00;
data_status_req[2] = 0x01; 
data_status_req[3] = 0x00;
data_status_req[4] = 0x31; 
data_status_req[5] = 0x15;
data_status_req[6] = 0x12;
std::cout << "Status Request Daten: " << std::showbase << std::hex << data_status_req << Bytes: " << std::showbase << std::dec << (DWORD) strlen(data_status_req) << std::endl;
```
Die Ausgabe gibt dann das hier aus: 
Status Request Daten:    Bytes: 1
kannst Du da was mit anfangen , bzw. was mach ich falsch?

das Ganze schicke ich dann so 

```
DWORD dwBytesWRITE = 0;
dwBytesWRITE = comport.Send(data_status_req);
```


```
unsigned long ComPort::Send(const char *text) 
{ 
   if(hCom!=INVALID_HANDLE_VALUE) 
   { 
      unsigned long sent; 
      DWORD l = (DWORD) strlen(text);
      if (WriteFile(hCom, text, (DWORD) strlen(text), &sent, NULL))   
	  return(sent);
      else
	  return(0);
   } 
   else 
      return(0); 
}
```
Hast Du eine Ahnung wo das Problem liegen könnte.


----------



## Tobias K. (28. April 2005)

moin


Das Problem könnte sein das data_status_req keinen "Abschluss" bekommt.
Füge mal noch einen data_status_req[7] = '\0'; hinzu.

Und vielleicht musst du dann strlen(text) noch minus 1 rechnen.

Ich sende übrigens keine ints sondern chars.


mfg
umbrasaxum


----------



## jower (28. April 2005)

hab ihc gemacht, nur funktioniert´s nicht.

```
char data_status_req[8];
	data_status_req[0] = (char) 2;
	data_status_req[1] = (char) 0;
	data_status_req[2] = (char) 1; 
	data_status_req[3] = (char) 0;
	data_status_req[4] = (char) 49; 
	data_status_req[5] = (char) 21;
	data_status_req[6] = (char) 18;
	data_status_req[7] = '\0';
	std::cout << "Status Request Daten: "  << data_status_req << " Bytes: " << std::showbase << std::dec << (DWORD) (strlen(data_status_req)-1) << std::endl;
```

Das gibt mir als Ausgabe: 
Status Request Daten:    Bytes: 0

Aber warum hat er 0 Byte? Sollten es nicht 7 sein? irgendwie scheinen die Daten nicht richtig zu sein, oder?
Und den Smile versteh ich auch nicht, scheint mich wohl zu mögen


----------



## jower (28. April 2005)

ich glaube ich habe den Fehler ziemlich stark eingegrenzt. 
strlen scheint nicht ganz zu funktionieren, warum weiß ich auch nicht. 
wenn ich eine Methode Send schreibe, der die Länge der zu sendenden Daten zu übergeben ist, funktioniert´s. Ist nur leider nicht so elegant. 
Wenn also jemand eine Idee haben könnte, wie ich etwas Senden könnte, ohne zu wissen, wie lang das Ganze ist, wäre ich sehr dankbar. 
Gruß jower


----------



## jokey2 (28. April 2005)

Was meinst du eigentlich mit 'hexadezimal senden'. Willst Du nur die Werte 2, 0, 1, 0, 31, 15 und 12 (also 7 Bytes) nacheinander verschicken, oder willst Du die Zeichenkette "02 00 01 00 31 15 12" (also 20 Bytes) verschicken?


----------



## jower (28. April 2005)

in meine die 7 Byte.


----------



## Tobias K. (28. April 2005)

moin


Was hast du eigentlich als externes Gerät?
Hast du mal ein Datenblatt dazu?


mfg
umbrasaxum


----------



## jower (28. April 2005)

Als externes Gerät hab ich einen LMS 200 von SICK.
zu finden ist das Dateenblatt unter team.caltech.edu/members/ SICK/LMS%20Communication%20Manual.pdf


----------



## Tobias K. (28. April 2005)

moin


Und woher weisst du wie und in welcher Form du Daten senden und Empfangen kannst?


mfg
umbrasaxum


----------



## jower (28. April 2005)

Hier ist das Datenblatt zu finden. Da steht alles drin.
Link hat nicht funktioniert. 
such mal unter Google nach "LMS%20Communication%20Manual" dann findest Du das Quick Datenblatt.


----------



## Tobias K. (28. April 2005)

moin


Versuch doch mal folgendes:

```
#include <windows.h>
#include <iostream>

using namespace std;

int i=0;

int main()
{
	DCB           dcb;
	HANDLE hCom = CreateFile ("COM1", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
	dcb.DCBlength = sizeof(DCB);
	GetCommState (hCom, &dcb); 
	dcb.BaudRate  = 9600;
	dcb.ByteSize  = 8;   
	dcb.Parity    = NOPARITY;    
	dcb.StopBits  = ONESTOPBIT;  
	SetCommState (hCom, &dcb);


	DWORD iBytesWritten;
	unsigned char ucMsg[5];

	ucMsg[0] = (char) 0x02;
	ucMsg[1] = (char) 0x00;
	ucMsg[2] = (char) 0x01;
	ucMsg[3] = (char) 0x00;
	ucMsg[4] = (char) 0x31;
	ucMsg[5] = (char) 0x15;
	ucMsg[6] = (char) 0x12;

	WriteFile (hCom, ucMsg, 7, &iBytesWritten, NULL);
		
	cin.get();
	
        CloseHandle (hCom);

	return 0;
}
```

Vielleicht musst du die Parity und StopBit noch anpassen.


mfg
umbrasaxum


----------



## jower (28. April 2005)

Das funktioniert auch, wichtig schien hier wohl das cast auf char zu sein. Wie schon bei der Lösung von Dir weiter oben. 
Ich bekomme mittlerweile eine Antwort. Die sieht nur ziemlich kryptisch aus. 
Sind Steuerzeichen, wie DC2 (0x12), etc. eigentlich irgendwie darstellbar, vielleicht z.B. als 0x12?


----------



## Tobias K. (28. April 2005)

moin


Eigentlich schon, z.B. mit cout<< ios_base::hex << atoi(var);
Vielleicht musst du statt ios_base::hex auch ios::hex nehmen.


mfg
umbrasaxum


----------



## Tobias K. (28. April 2005)

moin


Dem Manual nach solltest du als Antowrt eine Zeichen kette halten deren erste drei Zeichen 0x06 0x02 0x81 erhalten.


mfg
umbrasaxum


----------



## Byteblaster (28. April 2005)

strlen() kann auch nicht funktionieren, weil es nach nach dem ersten Byte mit dem wert 0 abbricht, da ein string in c/c++ mit 0 terminiert wird. Es arbeitet also wahrscheinlich völlig korrekt, dein 2. byte ist 0 und somit liefert strlen() nur die Länge 1 zurück. Nach deinem Quellcode weiter oben sendet er wahrscheinlich nur ein Byte (eventuell mal mit den Anzahl der geschriebenen Bytes vergleichen.) Wenn du also binäre Daten versenden willst, musst du die Länge mit übergeben. Ein beenden des Buffers mit '\0' ist überflüssig. Das heist deine Funktionskopf müsste in etwa so aussehen.


```
unsigned long ComPort::Send(const char *buffer,const DWORD size)
```

und weiter unten dann:


```
if (WriteFile(hCom, text, size, &sent, NULL))
```

Da dein externes Gerät ja wahrscheinlich immer bestimmte Kommandosequenzen benötigt z.B zur Initialisierung, sind die dir ja bekannt und somit auch die Länge.

Wenn du unterschiedliche Länge der Sequenzen hast, erweitere die Sequenzen und schreibe in das erste Byte die Länge die du dann auslesen kannst und hinten dran die eigentliche Sequenz (ab 2. Byte) und sende dann den Buffer ab den 2. Byte an des externe Gerät. (vergleiche mit: Strings in Pascal) 

oder pack sie in eine Struktur:


```
struct befehl
{
  DWORD size;
  char* buffer;
};
```


Ich hoffe es hilft weiter.

Gruß Byteblaster


----------



## jower (28. April 2005)

Danke für den Vorschlag mit den struct. Scheint das sinnvollste zu sein, da ich mit teilweise bis zu 720 Werten zu rechnen habe und mir jetzt auch nicht im Klaren darüber bin inwieweit sich jeder Meßwert über ein Byte darstellt. 



> cout<< ios_base::hex << atoi(var);
> Vielleicht musst du statt ios_base::hex auch ios::hex nehmen.


funktioniert nur insoweit, dass bspw. DC2 (0x12) als 20480 ausgegeben wird. weiss auch nicht wieso


----------



## Byteblaster (28. April 2005)

probier doch mal:


```
itoa(buffer[i],text,16);
```

Um bei meiner Struktur zu bleiben ;-), i entspricht hier dem entsprechenden Byte innerhalb des Buffers.

Beispiel

```
cout << "0x" << itoa(buffer[i],string,16) << " ";
```

Gruß Byteblaster


----------



## jower (29. April 2005)

Danke, das war´s. Mit 

```
cout << " 0x" << itoa(data_status_req[6],data_status_req,16) << " ";
```
bekomm ich als Ausgabe für DC2 auch 0x12.
Dann werde ich mich mal an die Arbeit machen und versuchen, Daten vom Gerät auch zu verstehen. Dafür werde ich dann eine neue Klasse aufsetzen. 
Gruß Jower


----------

