Forum: PC-Programmierung PHP byte array per tcp socket versenden


von L. N. (derneumann)


Lesenswert?

Hallo!

bin grade an einem Projekt dran wo ich an einen Dimmer auf ESP8266 Basis 
(Arduino Framework) über eine PHP Website mit TCP Sockets ein Bytearray 
senden will.

Mit VB.NET klappt das schon sehr gut, allerdings ist mein PHP Wissen 
extrem eingerostet (zuletzt mit PHP4 gearbeitet).

Im Wesentlichen hab ich ein Commandbyte, z.B. 0x20 und dann z.B. 12 Bit 
PWM Werte die unter VB.NET als unsigned 16 Bit Integer abgebildet, per 
Bitshifting in zwei einzelne Bytes zerlegt und dann ins Bytearray 
gepackt und versendet werden.

Genau hier scheitert es bei PHP bei mir. Habe schon die pack() Funktion 
gefunden und konnte auch schon einzelne Bytes versenden (sind auch 
richtig angekommen). Sobald es jedoch darum ging, unsigned 16 Bit 
Integer und unsigned 8 Bit Integer auf einmal zu versenden, also mit 
einem pack() in eine Variable zu speichern um diese dann zu verschicken, 
kam nur Müll an.
Habe es mit Little und Big Endian probiert.

Bin einfach überfordert und konnte trotz stundenlanger Recherche und 
Kopfzerbrechen nicht zu einer Lösung finden.

Für manche Dinge bin ich wohl einfach zu blöd bzw. verstehe sie erst, 
wenn ich die Lösung vor den Augen habe (sprich: wenn es mir jemand 
vorgekaut hat).
Bitte um Nachsicht diesbezüglich, habe mir echt schon die Doku u.a. von 
pack, unpack, ... reingezogen und etliche Threads auf stackoverflow 
umgegraben, verstehe es trotzdem nicht.

Danke für eure Hinweise!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Lucas N. schrieb:
> Im Wesentlichen hab ich ein Commandbyte, z.B. 0x20 und dann z.B. 12 Bit
> PWM Werte die unter VB.NET als unsigned 16 Bit Integer abgebildet, per
> Bitshifting in zwei einzelne Bytes zerlegt und dann ins Bytearray
> gepackt und versendet werden.

Das kannst Du genauso auch in PHP machen. Am besten zeigst Du mal den 
funktionierenden VB-Code.

von L. N. (derneumann)


Lesenswert?

Danke dass du dir Zeit nimmst!
1
Private Sub SendColors()
2
 Dim ByteArray(17) As Byte
3
 ByteArray(0) = &H20
4
 ByteArray(1) = CByte(BitConverter.GetBytes(ch1.Value)(0))
5
 ByteArray(2) = CByte(BitConverter.GetBytes(ch1.Value)(1))
6
 ByteArray(3) = CByte(BitConverter.GetBytes(ch2.Value)(0))
7
 ByteArray(4) = CByte(BitConverter.GetBytes(ch2.Value)(1))
8
 ByteArray(5) = CByte(BitConverter.GetBytes(ch3.Value)(0))
9
 ByteArray(6) = CByte(BitConverter.GetBytes(ch3.Value)(1))
10
 ByteArray(7) = CByte(BitConverter.GetBytes(ch4.Value)(0))
11
 ByteArray(8) = CByte(BitConverter.GetBytes(ch4.Value)(1))
12
 ByteArray(9) = CByte(BitConverter.GetBytes(ch5.Value)(0))
13
 ByteArray(10) = CByte(BitConverter.GetBytes(ch5.Value)(1))
14
 ByteArray(11) = CByte(BitConverter.GetBytes(ch6.Value)(0))
15
 ByteArray(12) = CByte(BitConverter.GetBytes(ch6.Value)(1))
16
 ByteArray(13) = CByte(BitConverter.GetBytes(ch7.Value)(0))
17
 ByteArray(14) = CByte(BitConverter.GetBytes(ch7.Value)(1))
18
 ByteArray(15) = CByte(BitConverter.GetBytes(ch8.Value)(0))
19
 ByteArray(16) = CByte(BitConverter.GetBytes(ch8.Value)(1))
20
 ExecQuery(ByteArray)
21
End Sub
22
23
Private Sub ExecQuery(data As Byte())
24
  Dim tcpClient As New System.Net.Sockets.TcpClient()
25
  tcpClient.Connect(txtIP.Text, 5160)
26
27
  Dim networkStream As NetworkStream = tcpClient.GetStream()
28
29
  If networkStream.CanWrite And networkStream.CanRead Then
30
    ' Do a simple write.
31
    Dim numberOfBytesRead As Integer = 0
32
33
    Dim ResponseBytes(18) As Byte
34
    
35
    Dim ReceiveBuffer(0) As Byte
36
37
    networkStream.Write(data, 0, data.Length)
38
39
    Dim TimeOut As DateTime
40
    TimeOut = DateTime.Now
41
42
    While (networkStream.DataAvailable Or DateTime.Now.Subtract(TimeOut).Seconds <= 2)
43
      networkStream.Read(ReceiveBuffer, 0, 1)
44
      ResponseBytes(numberOfBytesRead) = ReceiveBuffer(0)
45
      numberOfBytesRead += 1
46
      TimeOut = DateTime.Now
47
      If numberOfBytesRead > 0 Then
48
        If ResponseBytes(0) = &HF0 Or ResponseBytes(0) = &H95 And numberOfBytesRead = 9 Then
49
          Exit While
50
        End If
51
      End If
52
    End While
53
    ' Do stuff
54
55
  Else
56
    If Not networkStream.CanRead Then
57
      Console.WriteLine("cannot not write data to this stream")
58
      tcpClient.Close()
59
    Else
60
      If Not networkStream.CanWrite Then
61
        Console.WriteLine("cannot read data from this stream")
62
        tcpClient.Close()
63
      End If
64
    End If
65
  End If
66
  tcpClient.Close()
67
68
End Sub

Edit:
der Microcontrollerteil am ESP8266 schaut in C so aus:
1
if (client.available() > 16)
2
{
3
  client.readBytes(tcpServerInputBuffer, 17);
4
  client.flush();
5
  
6
  ....
7
  
8
  if (tcpServerInputBuffer[0] == TCP_CHANNELS_SET)
9
  {
10
    poweredOn = true;
11
    pwm_channels[0] = tcpServerInputBuffer[1] | uint16_t(tcpServerInputBuffer[2]) << 8;
12
    pwm_channels[1] = tcpServerInputBuffer[3] | uint16_t(tcpServerInputBuffer[4]) << 8;
13
    pwm_channels[2] = tcpServerInputBuffer[5] | uint16_t(tcpServerInputBuffer[6]) << 8;
14
    pwm_channels[3] = tcpServerInputBuffer[7] | uint16_t(tcpServerInputBuffer[8]) << 8;
15
16
    pwm_channels[4] = tcpServerInputBuffer[9] | uint16_t(tcpServerInputBuffer[10]) << 8;
17
    pwm_channels[5] = tcpServerInputBuffer[11] | uint16_t(tcpServerInputBuffer[12]) << 8;
18
    pwm_channels[6] = tcpServerInputBuffer[13] | uint16_t(tcpServerInputBuffer[14]) << 8;
19
    pwm_channels[7] = tcpServerInputBuffer[15] | uint16_t(tcpServerInputBuffer[16]) << 8;
20
    client.write(0xF0);
21
  }
22
  
23
  ...
24
}

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Das Schreiben müsste folgendermaßen funktionieren:
1
$host = "127.0.0.1";
2
$port = 5160;
3
4
$packet = pack("CSSSSSSSS", 0x20, $ch1, $ch2, $ch3, $ch4, $ch5, $ch6, $ch7, $ch8);
5
6
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
7
socket_connect($socket, $host, $port);
8
socket_write($socket, $packet, 17);

Danach muss man dann die Antwort lesen. In dem ESP-Beispiel-Code wird 
nur ein Byte (nämlich 0xF0) zurückgegeben, im VB-Code sehe ich 
irgendwelche möglichen Werte mit mit Datenlänge 9.

Du kannst ja mal selbst schauen, lesen der Antwort geht mit:
1
$maxlen = 9; // oder ein anderer Wert
2
$result = socket_read ($socket, $maxlen);
Notfalls machst Du auch eine Schleife über Read jedes einzelnen 
Zeichens:
1
$result = socket_read ($socket, 1);

Googlen nach "socket_read php" hilft weiter.

: Wiederhergestellt durch Moderator
von L. N. (derneumann)


Lesenswert?

Frank M. schrieb:

> $packet = pack("CSSSSSSSS", 0x20, $ch1, $ch2, $ch3, $ch4, $ch5, $ch6,
> $ch7, $ch8);

"CSSSS..." war die ausschlaggebende Information, vielen Dank! Könnte mir 
auf den Kopf greifen, das ist ja eh ähnlich wie printf in C.

> Danach muss man dann die Antwort lesen. In dem ESP-Beispiel-Code wird
> nur ein Byte (nämlich 0xF0) zurückgegeben, im VB-Code sehe ich
> irgendwelche möglichen Werte mit mit Datenlänge 9.

Die Länge der Antwort hängt vom Kommando ab. Manche Kommandos bekommen 
nur 0xF0 zurück (einfaches ACK), manche bekommen eine komplette Antwort 
inkl. Daten retour.

> Du kannst ja mal selbst schauen, lesen der Antwort geht mit:$maxlen = 9;
> // oder ein anderer Wert
> $result = socket_read ($socket, $maxlen);
> Notfalls machst Du auch eine Schleife über Read jedes einzelnen
> Zeichens:$result = socket_read ($socket, 1);
>
> Googlen nach "socket_read php" hilft weiter.

Danke, hast mir jedenfalls schon sehr weitergeholfen. Da hätte ich 
wieder mal echt selbst draufkommen können.

Mein Code hat, bis auf das von dir gelieferte "CSSSSSSSS" so ausgesehen, 
habe das mit fsockopen statt mit socket_create und socket_connect 
gemacht. Wo liegt denn da der Unterschied? Habe irgendwo gelesen, dass 
fsockopen viel langsamer sein soll, kann ich aber irgendwie nicht ganz 
glauben...
1
<?php
2
 $output = pack("CSSSSSSSS", 0x20, $r, $g, $b, 0, $r, $g, $b, 0);
3
 $socket = fsockopen("192.168.25.158", 5160);
4
 fwrite($socket, $output, 17);
5
 fclose($fp);
6
?>

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.