Android Question How to implement communication to external device?

Victor Pavlov

Member
Licensed User
Longtime User
What is the right way to implement Host-Slave communication via LAN with Wait For and Sleep?
My current work requires 2 way communication with fiscal device.

Here is same sample code for the task.

B4X:
Private Sub FiscalPrint()
    Dim Ret As Int
  
    SendCommand(CreateCommand(cmdGetStatus, ""))
    Ret = GetAnswer()
  
    Select Case Ret
        Case 0:    'Do nothing, the printer is in correct state

        Case 1:    'Transaction started, waiting payment
            SendCommand(CreateCommand(cmdPayment, "100"))
            Ret = GetAnswer()
            SendCommand(CreateCommand(cmdFinish, ""))
            Ret = GetAnswer()

        Case 2:    'Transaction started, payment complete, print receipt
            SendCommand(CreateCommand(cmdFinish, ""))
            Ret = GetAnswer()
    End Select
     
    SendCommand(CreateCommand(cmdStart, ""))
    Ret = GetAnswer()
  
    SendCommand(CreateCommand(cmdItemSale, "Beer;10;2.50"))
    Ret = GetAnswer()

    SendCommand(CreateCommand(cmdItemSale, "Chips;5;1.50"))
    Ret = GetAnswer()
  
    SendCommand(CreateCommand(cmdPayment, "100"))
    Ret = GetAnswer()

    SendCommand(CreateCommand(cmdFinish, ""))
    Ret = GetAnswer()

End Sub
 

Victor Pavlov

Member
Licensed User
Longtime User
B4X:
'Отговор от устройството
Sub PrinterTX_NewData (Buffer() As Byte)
    Dim s As String
       
    s = BytesToString(Buffer, 0, Buffer.Length, PrinterCodePage)
   
    If s.Length > 5 Then Log("In <- " & s)
       
    GetAnswer(s)
End Sub

'Анализиране на получения отговор
Sub GetAnswer(RawAnswer As String) As String
    Dim s As String
    Dim Conv As ByteConverter
    Dim i As Int
    Dim j As Int
   
    s = RawAnswer
   
    If (s.Length = 1) And (s.SubString2(0, 1) = Chr(SYN)) Then
        Log("SYN")
        Return ""            'Printer sends WAIT command, wait for next block
    End If
   
    For j = 0 To 7            'Reset Status Bytes of the printer
        StatusBytes(j) = 0
    Next

    Dim MyBytes(s.Length) As Byte
    MyBytes = Conv.StringToBytes(s, PrinterCodePage)
   
    If s.Length >= 25 Then 
        If True Then            'Here will be package CRC checks, now always TRUE
                                'Тук трябва да има проверка за валидност на пакета!
            For i = 0 To (s.Length - 9)    'At least 8 status bytes
               
                If MyBytes(i) = Splitter Then        'Data spliter found, next are the status bytes   
                    ECRLastCommand = HexReverse(s.SubString2(6, 10))    'BCD to Long conversion
                    ECRAnswer = s.SubString2(10, i-1)
                    ECRParams = Regex.Split(Sep, ECRAnswer)
                    ECRError = ECRParams(0)
               
                    For j = 0 To 7        'Copy Status Bytes
                        StatusBytes(j) = MyBytes(i + j + 1)
                    Next
                    Log("Status Bytes: " & HexSplit(Conv.HexFromBytes(StatusBytes)))    'Debug informaton
                                   
                    Exit                'Status bytes found, exiting loop
                End If
            Next
           
            If ECRError <> "0" Then ErrorHandling
           
            Log("Answer: " & ECRAnswer)
                   
            ExecuteTask            'Execute next task after getting the answer
           
            Return ECRAnswer
        Else
            'Answer not valid, check sum failed! Here should be "Resend the command"
            Return ""
        End If
    Else
        'Answer not valid, too short! Here should be "Resend the command"
        Return ""
    End If
End Sub

Protocol description
Low level protocol

A) Protocol type - Master (Host) / Slave

The fiscal printer performs the commands sent by the Host and returns messages, which depend on the result. The fiscal printer cannot instigate asynchronous communications itself. Only responses to commands from the Host are sent to the Host. These messages are either wrapped or single byte control codes. The fiscal printer maintains the communication via the RS232 serial connection at baud rates of 1200, 2400, 4800, 9600, 19200, 38400, 57600 and 115200 b/s, 8N1.


B) Sequence of the messages

Host sends a wrapped message, containing a command for the fiscal printer. ECR executes the requested operation and response with a wrapped message. Host has to wait for a response from the fiscal printer before to send another message. The protocol uses non-wrapped messages with a length one byte for processing of the necessary pauses and error conditions.


C) Non-wrapped messages – time-out


When the transmitting of messages from the Host is normal, Slave answers not later than 60 ms either with a wrapped message or with a 1 byte code. Host must have 500 ms of time-out for receiving a message from Slave. If there is no message during this period of time the Host will transmit the message again with the same sequence number and the same command. After several unsuccessful attempts Host must indicate that there is either no connection to the fiscal printer or there is a hardware fault.

Non-wrapped messages consist of one byte and they are:

A) NAK 15H

This code is sent by Slave when an error in the control sum or the form of the received message is found. When Host receives a NAK it must again send a message with the same sequence number.

B) SYN 16H

This code is sent by Slave upon receiving a command which needs longer processing time. SYN is sent every 60 ms until the wrapped message is not ready for transmitting.


D) Wrapped messages

a) Host to fiscal printer (Send)

<01><LEN><SEQ><CMD><DATA><05><BCC><03>

b) Fiscal printer to Host (Receive)

<01><LEN><SEQ><CMD><DATA><04><STATUS><05><BCC><03>

Where:

<01> Preamble. - 1 byte long. Value: 01H.

<LEN> Number of bytes from <01> preamble (excluded) to <05> (included) plus the fixed offset of 20H.

Length: 4 bytes. Each digit from the two bytes is sent after 30H is added to it. For example the sum 1AE3H is presented as 31H, 3AH, 3EH, 33H.

<SEQ> Sequence number of the frame.

Length : 1 byte. Value: 20H – FFH. The fiscal printer saves the same <SEQ> in the return message. If the ECR gets a message with the same <SEQ> as the last message received it will not perform any operation, but will repeat the last sent message.

<CMD> The code of the command.

Length: 4 byte. The fiscal printer saves the same <CMD> in the return message. If the fiscal printer receives a non-existing code it returns a wrapped message with zero length in the data field and sets the respective status bit. Each digit from the two bytes is sent after 30H is added to it. For example the sum 1AE3H is presented as 31H, 3AH, 3EH, 33H.

<DATA> Data.

Length: 0-213 bytes for Host to fiscal printer, 0-218 bytes for Fiscal printer to Host. Value: 20H – FFH. The format and length of the field for storing data depends on the command. If the command has no data the length of this field is zero. If there is a syntax error the respective status bit is established in the data and a wrapped message is returned with zero field length.

<04> Separator (only for fiscal printer-to-Host massages),

Length: 1 byte. Value: 04H.

<STATUS> The field with the current status of the fiscal device.

Length: 8 bytes. Value: 80H-FFH.

<05> Postamble

Length: 1 byte. Value:05H.

<BCC> Control sum (0000H-FFFFH),

Length: 4 bytes. Value of each byte: 30H-3FH. The sum includes between <01> preamble (excluded) to <05>. Each digit from the two bytes is sent after 30H is added to it. For example the sum 1AE3H is presented as 31H, 3AH, 3EH, 33H.

<03> Terminator, Length: 1 byte. Value: 03H.


Message composition, syntax and meanings


a) The data field depends on the command.

b) The parameters sent to the fiscal printer may be separated with a [\t] and/or may have a fixed length.

c) The separator( [\t] ) between the parameters shows that it is mandatory.

d) Some of the parameters are mandatory and others are optional. Optional parameters can be left empty, but after them must have separator ( [\t] ).

The symbols with ASCII codes under 32 (20H) have special meanings and their use is explained whenever necessary. If such a symbol has to be sent for some reason (for example in an ESCAPE-command to the display) it must be preceded by 16 (10H) with an added offset 40H.

Here is some communication through COM port (PC)
Write COM1: 01 52 3C 2A 20 20 31 20 53 74 65 72 6C 69 6E 67 .R<* 1 Sterling
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 31 2E 1.
39 39 05 30 39 32 37 03 99.0927.
Read COM1: 01 2B 3C 2A 04 80 80 A0 80 86 98 05 30 33 3D 38 .+<*.ЂЂ Ђ†˜.03=8
Port flushed COM1 (31.08.2017 16:58:31)

Write COM1: 01 52 3D 2A 20 20 31 20 53 74 61 72 6F 62 72 6E .R=* 1 Starobrn
6F 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 o
20 20 20 20 20 20 20 20 20 20 20 20 20 20 31 2E 1.
35 39 05 30 39 37 36 03 59.0976.
Read COM1: 01 2B 3D 2A 04 80 80 A0 80 86 98 05 30 33 3D 39 .+=*.ЂЂ Ђ†˜.03=9
Port flushed COM1 (31.08.2017 16:58:31)

Write COM1: 01 25 3E 2A 20 05 30 30 3B 32 03 .%>* .00;2.
Read COM1: 01 2B 3E 2A 04 80 80 A0 80 86 98 05 30 33 3D 3A .+>*.ЂЂ Ђ†˜.03=:
Port flushed COM1 (31.08.2017 16:58:31)

Write COM1: 01 24 3F 27 05 30 30 38 3F 03 .$?'.008?.
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 .
Read COM1: 16 01 2F 3F 27 30 30 30 37 04 80 80 80 80 86 98 ../?'0007.ЂЂЂЂ†˜
05 30 34 38 33 03 .0483.
Port flushed COM1 (31.08.2017 16:58:32)

Write COM1: 01 24 40 3E 05 30 30 3A 37 03 .$@>.00:7.
Read COM1: 01 3C 40 3E 33 31 2D 30 38 2D 31 37 20 31 36 3A .<@>31-08-17 16:
35 38 3A 32 38 04 80 80 80 80 86 98 05 30 37 34 58:28.ЂЂЂЂ†˜.074
31 03 1.
Port flushed COM1 (31.08.2017 16:58:32)

Write COM1: 01 24 41 71 05 30 30 3D 3B 03 .$Aq.00=;.
Read COM1: 01 32 41 71 30 30 30 30 31 37 30 04 80 80 80 80 .2Aq0000170.ЂЂЂЂ
86 98 05 30 35 36 33 03 †˜.0563.
Port flushed COM1 (31.08.2017 16:58:32)

Write COM1: 01 25 42 4A 57 05 30 31 30 3D 03 .%BJW.010=.
Read COM1: 01 31 42 4A 80 80 80 80 86 98 04 80 80 80 80 86 .1BJЂЂЂЂ†˜.ЂЂЂЂ†
98 05 30 37 30 32 03 ˜.0702.
Port closed COM1 (31.08.2017 16:58:32)
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. As a general rule it is a mistake to treat raw bytes as strings. You can lose data this way.
2. Your code assumes that messages will not be split. This assumption is incorrect.
3. You can see how AsyncStreamsText works: https://www.b4x.com/android/forum/t...n-working-with-streams-of-text.27002/#content
It collects the data until it finds an end of line character. You can do something similar.

You shouldn't call GetAnswer from FiscalPrint.

You should instead raise an event with CallSubDelayed2(Me, "MessageArrived", DataHere) from GetAnswer (it shouldn't return a value).

FiscalPrint should look like:
B4X:
SendCommand(...)
Wait For MessageArrived(Data As String)
'''
SendCommand(...)
Wait For MessageArrived(Data As String)
 
Upvote 0
Top