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


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
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
?>

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.