Diskussion:Nova 35: Unterschied zwischen den Versionen

Aus FabLab Region Nürnberg
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
 
(15 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 8: Zeile 8:
* [https://stefan.schuermans.info/rdcam/ RDCAM Reverse Engineering]
* [https://stefan.schuermans.info/rdcam/ RDCAM Reverse Engineering]
* [http://www.thunderlaser.co.nz/beifen/technical-support/downloads/category/rdc6442g-and-rdwork-software.html RDworks + Manuals]
* [http://www.thunderlaser.co.nz/beifen/technical-support/downloads/category/rdc6442g-and-rdwork-software.html RDworks + Manuals]
* [https://github.com/t-oster/VisiCut/issues/404 VisiCut issue]


* [https://edutechwiki.unige.ch/en/Ruida Protocol analysis]
== Protokollanalyse ==
== Protokollanalyse ==
=== UDP ===
* The device listens on a fixed UDP port (DEST_PORT). IPaddress is configurable 172.16.18.11, netmask is 255.255.255.0 fixed, Gateway is configurable 172.16.20.134.
* An RD file is transfered as payload, same commands and syntax as with USB-Serial or USB-MassStorage.
* The payload is split in chunks with a well known maximum size (MTU). (The last packet is usually shorter)
* There is no header, and no arbitration phase, but successful transmission of the first chunk indicates device ready.
* Each chunk starts with a two byte checksum, followed by payload data. Length of the payload is implicit by the UDP datagram size. (Would not work with TCP)
* Each chunk is acknowledged with a single byte response packet:
** 0xc6 if all is well, The next chunk should be sent. A delay of 4 seconds was tested successfully.
** 0x46 if error. TODO: Checksum error and/or busy? A running laser job does not cause a busy condition.
* The first chunk should be retried when 0x46 was received. For subsequent chunks transmission should be aborted.
* No pause is needed after the last chunk. The payload contains a terminator token.
Command line tool usage:
    wget https://github.com/jnweiger/ruida-laser/blob/master/udpsendruida.py
    python3 udpsendruida.py 192.168.1.100 thunder.rd
=== earlier analysis ===


# Capture mit Wireshark
# Capture mit Wireshark
Zeile 21: Zeile 42:


=== rdcam.py ===
=== rdcam.py ===
<code>
This python code snippet is probably useful with wireshark. Please add details.
pat magic
 
</code>
<pre>
def send_command(payload):
    data=scramble_bytes(payload)
    checksum=sum(data)
    b1=checksum&0xff
    b0=(checksum>>8)&0xff
    return bytes([b0,b1])+data
 
def encode_number(n,l=5):
    res=[]
    while n>0:
        res.append(n&0x7f)
        n>>=7
    while len(res)<l:
        res.append(0)
    res.reverse()
    return bytes(res)
 
def decode_number(x):
    fak=1
    res=0
    for b in reversed(x):
        res+=fak*b
        fak*=0x80
    return res
 
def format_capture(c):
    for p in c:
        direction=p["_source"]['layers']['udp']['udp.port']=='50200'
        data=unscramble_packet(p,checksum=direction)
        line='-> ' if direction else '<- '
        line+=' '.join([bytes(x).hex() for x in split_messages(data)])
        print(line)
 
def split_messages(d):
    m=[]
    res=[m]
    for x in d:
        if x&0x80:
            m=[]
            res.append(m)
        m.append(x)
    return res
   
 
 
def unscramble_packet(p,checksum=False):
    string=p["_source"]["layers"]["data"]["data.data_raw"]
    if checksum:
        return list(bytes.fromhex(string[:4]))+unscramble_string(string[4:])
    else:
        return unscramble_string(string)
 
def unscramble_string(s):
    return [unscramble(b) for b in bytes.fromhex(s)]
 
def unscramble(b):
    res_b=b-1
    if res_b<0: res_b+=0x100
    res_b^=0x88
    fb=res_b&0x80
    lb=res_b&1
    res_b=res_b-fb-lb
    res_b|=lb<<7
    res_b|=fb>>7
    return res_b
 
def scramble(b):
    fb=b&0x80
    lb=b&1
    res_b=b-fb-lb
    res_b|=lb<<7
    res_b|=fb>>7
    res_b^=0x88
    res_b+=1
    if res_b>0xff:res_b-=0x100
    return res_b
 
def scramble_bytes(bs):
    return bytes([scramble(b) for b in bs])
def unscramble_bytes(bs):
    return bytes([unscramble(b) for b in bs])
</pre>


=== Data format ===
=== Data format ===


Byte = 1 Bit Message Start Indicator + 7 Bit Payload
* Byte = 1 Bit Message Start Indicator + 7 Bit Payload
* Only one message (checksum + command) can be sent per UDP package
* Max UDP package size 1472 bytes including checksum; fragmented by simple cutting (even inside a command)


==== Checksum ====
==== Checksum ====
Zeile 33: Zeile 138:
2 Bytes - sum of scrambled message bytes; MSB first.
2 Bytes - sum of scrambled message bytes; MSB first.


Checksum has to be send before message.  
Checksum has to be send before message.
 
'''Network only''' USB does not have checksum.


==== Values ====
==== Values ====


{| class="wikitable"
{| class="wikitable sortable"
|-
|-
! Value !! Lenght !! Description
! Value !! Lenght !! Description
|-
|-
|id="VAL-ABSCOORD"| ABSCOORD || 5 Bytes || absolute position relative to job origin in µm
|id="VAL-ABSCOORD"| ABSCOORD || 5 Bytes || absolute position relative to job origin in µm
|-
|id="VAL-RELCOORD"| RELCOORD || 2 Bytes || relative position in µm; signed (2s complement)
|-
|-
|id="VAL-SPEED"| SPEED || 5 Bytes || speed in µm/s
|id="VAL-SPEED"| SPEED || 5 Bytes || speed in µm/s
Zeile 52: Zeile 161:
==== Commands ====
==== Commands ====


{| class="wikitable"
{| class="wikitable sortable"
|-
|-
! Byte squence !! Description !! how sure we are
! Byte squence !! Description !! how sure we are
|-
|-
| C6 01 [[#VAL-POWER|POWER]] || 1st laser source min power || 99%
| C6 01 [[#VAL-POWER|<POWER>]] || 1st laser source min power || 99%
|-
|-
| C6 21 [[#VAL-POWER|POWER]] || 2nd laser source min power || 99%
| C6 21 [[#VAL-POWER|<POWER>]] || 2nd laser source min power || 99%
|-
|-
| C6 02 [[#VAL-POWER|POWER]] || 1st laser source max power || 99%
| C6 02 [[#VAL-POWER|<POWER>]] || 1st laser source max power || 99%
|-
|-
| C6 22 [[#VAL-POWER|POWER]] || 2nd laser source max power || 99%
| C6 22 [[#VAL-POWER|<POWER>]] || 2nd laser source max power || 99%
|-
|-
| C9 02 [[#VAL-SPEED|SPEED]] || movement and/or (not sure) cutting speed || 80%
| C9 02 [[#VAL-SPEED|<SPEED>]] || movement and/or (not sure) cutting speed || 80%
|-
|-
| D9 00 02 [[#VAL-ABSCOORD|ABSCOORD]] || move X || 99%
| D9 00 02 [[#VAL-ABSCOORD|<ABSCOORD>]] || move X || 99%
|-
|-
| D9 00 03 [[#VAL-ABSCOORD|ABSCOORD]] || move Y || 50%
| D9 00 03 [[#VAL-ABSCOORD|<ABSCOORD>]] || move Y || 50%
|-
|-
| D9 00 04 [[#VAL-ABSCOORD|ABSCOORD]] || move Z || 50%
| D9 00 04 [[#VAL-ABSCOORD|<ABSCOORD>]] || move Z || 50%
|-
|-
| D9 00 05 [[#VAL-ABSCOORD|ABSCOORD]] || move U || 50%
| D9 00 05 [[#VAL-ABSCOORD|<ABSCOORD>]] || move U || 50%
|-
|-
| CC || ACK from machine || 99%
| CC || ACK from machine || 99%
|-
|-
| CD || FAIL from machine || 99%
| CD || ERR from machine || 99%
|-
| DA 00 XX XX || get XX XX from machine || 99%
|-
| DA 00 04 05 || saved job count || 99%
|-
| DA 01 XX XX <VALUE> || response to DA 00 XX XX || 99%
|-
| A8 [[#VAL-ABSCOORD|<ABSCOORD>]] [[#VAL-ABSCOORD|<ABSCOORD>]] || Straight cut to absolute X Y; turn laser on with configured speed and power || 99%
|-
| A9 [[#VAL-RELCOORD|<RELCOORD>]] [[#VAL-RELCOORD|<RELCOORD>]] || Straight cut to relative X Y; turn laser on with configured speed and power || 99%
|-
|-
| DA.... || get data from machine || 20%
| E7 50 [[#VAL-ABSCOORD|<ABSCOORD>]] [[#VAL-ABSCOORD|<ABSCOORD>]] || Bounding box top left? || 30%
|-
|-
| A8 [[#VAL-ABSCOORD|ABSCOORD]] [[#VAL-ABSCOORD|ABSCOORD]] || Straight cut to absolute X Y; turn laser on with configured speed and power ||  
| E7 51 [[#VAL-ABSCOORD|<ABSCOORD>]] [[#VAL-ABSCOORD|<ABSCOORD>]] || Bounding box bottom right? || 30%
|-
|-
| E7 50 [[#VAL-ABSCOORD|ABSCOORD]] [[#VAL-ABSCOORD|ABSCOORD]] || ||  
| E8 02 E7 01 [[#VAL-CSTRING|<CSTRING>]] || Set filename for following transfer (transfer needs to be done really quickly after this!) || 90%
|-
|-
| E7 51 [[#VAL-ABSCOORD|ABSCOORD]] [[#VAL-ABSCOORD|ABSCOORD]] ||  ||  
| E8 01 XX XX || Read filename number XX XX ||
|-
|-
| E8 02 E7 01 [[#VAL-CSTRING|CSTRING]] || Set filename for following transfer || 90%
| 88 [[#VAL-ABSCOORD|<ABSCOORD>]] [[#VAL-ABSCOORD|<ABSCOORD>]] || straight move to absolute X Y as fast as possible; with laser off || 99%
|-
|-
| || ||  
| 89 [[#VAL-RELCOORD|<RELCOORD>]] [[#VAL-RELCOORD|<RELCOORD>]] || straight move to relative X Y as fast as possible; with laser off || 80%
|}
|}

Aktuelle Version vom 10. November 2023, 19:30 Uhr

Hardware

RDC6442G 4-axis Lasercontrol
Breakout Board

Protokollanalyse

UDP

  • The device listens on a fixed UDP port (DEST_PORT). IPaddress is configurable 172.16.18.11, netmask is 255.255.255.0 fixed, Gateway is configurable 172.16.20.134.
  • An RD file is transfered as payload, same commands and syntax as with USB-Serial or USB-MassStorage.
  • The payload is split in chunks with a well known maximum size (MTU). (The last packet is usually shorter)
  • There is no header, and no arbitration phase, but successful transmission of the first chunk indicates device ready.
  • Each chunk starts with a two byte checksum, followed by payload data. Length of the payload is implicit by the UDP datagram size. (Would not work with TCP)
  • Each chunk is acknowledged with a single byte response packet:
    • 0xc6 if all is well, The next chunk should be sent. A delay of 4 seconds was tested successfully.
    • 0x46 if error. TODO: Checksum error and/or busy? A running laser job does not cause a busy condition.
  • The first chunk should be retried when 0x46 was received. For subsequent chunks transmission should be aborted.
  • No pause is needed after the last chunk. The payload contains a terminator token.

Command line tool usage:

   wget https://github.com/jnweiger/ruida-laser/blob/master/udpsendruida.py
   python3 udpsendruida.py 192.168.1.100 thunder.rd


earlier analysis

  1. Capture mit Wireshark
  2. Datei > Paketdissektion exportieren > Als JSON
    • Packet summary line
      • Include column headings
    • Packet details:
      • As displayed
    • Packet Bytes
  3. some more pat foo

rdcam.py

This python code snippet is probably useful with wireshark. Please add details.

def send_command(payload):
    data=scramble_bytes(payload)
    checksum=sum(data)
    b1=checksum&0xff
    b0=(checksum>>8)&0xff
    return bytes([b0,b1])+data

def encode_number(n,l=5):
    res=[]
    while n>0:
        res.append(n&0x7f)
        n>>=7
    while len(res)<l:
        res.append(0)
    res.reverse()
    return bytes(res)

def decode_number(x):
    fak=1
    res=0
    for b in reversed(x):
        res+=fak*b
        fak*=0x80
    return res

def format_capture(c):
    for p in c:
        direction=p["_source"]['layers']['udp']['udp.port']=='50200'
        data=unscramble_packet(p,checksum=direction)
        line='-> ' if direction else '<- '
        line+=' '.join([bytes(x).hex() for x in split_messages(data)])
        print(line)

def split_messages(d):
    m=[]
    res=[m]
    for x in d:
        if x&0x80:
            m=[]
            res.append(m)
        m.append(x)
    return res
    


def unscramble_packet(p,checksum=False):
    string=p["_source"]["layers"]["data"]["data.data_raw"]
    if checksum:
        return list(bytes.fromhex(string[:4]))+unscramble_string(string[4:])
    else:
        return unscramble_string(string)

def unscramble_string(s):
    return [unscramble(b) for b in bytes.fromhex(s)]

def unscramble(b):
    res_b=b-1
    if res_b<0: res_b+=0x100
    res_b^=0x88
    fb=res_b&0x80
    lb=res_b&1
    res_b=res_b-fb-lb
    res_b|=lb<<7
    res_b|=fb>>7
    return res_b

def scramble(b):
    fb=b&0x80
    lb=b&1
    res_b=b-fb-lb
    res_b|=lb<<7
    res_b|=fb>>7
    res_b^=0x88
    res_b+=1
    if res_b>0xff:res_b-=0x100
    return res_b

def scramble_bytes(bs):
    return bytes([scramble(b) for b in bs])
def unscramble_bytes(bs):
    return bytes([unscramble(b) for b in bs])

Data format

  • Byte = 1 Bit Message Start Indicator + 7 Bit Payload
  • Only one message (checksum + command) can be sent per UDP package
  • Max UDP package size 1472 bytes including checksum; fragmented by simple cutting (even inside a command)

Checksum

2 Bytes - sum of scrambled message bytes; MSB first.

Checksum has to be send before message.

Network only USB does not have checksum.

Values

Value Lenght Description
ABSCOORD 5 Bytes absolute position relative to job origin in µm
RELCOORD 2 Bytes relative position in µm; signed (2s complement)
SPEED 5 Bytes speed in µm/s
POWER 2 Bytes power in 0,006103516% (100/2^14)
CSTRING variable zero terminated

Commands

Byte squence Description how sure we are
C6 01 <POWER> 1st laser source min power 99%
C6 21 <POWER> 2nd laser source min power 99%
C6 02 <POWER> 1st laser source max power 99%
C6 22 <POWER> 2nd laser source max power 99%
C9 02 <SPEED> movement and/or (not sure) cutting speed 80%
D9 00 02 <ABSCOORD> move X 99%
D9 00 03 <ABSCOORD> move Y 50%
D9 00 04 <ABSCOORD> move Z 50%
D9 00 05 <ABSCOORD> move U 50%
CC ACK from machine 99%
CD ERR from machine 99%
DA 00 XX XX get XX XX from machine 99%
DA 00 04 05 saved job count 99%
DA 01 XX XX <VALUE> response to DA 00 XX XX 99%
A8 <ABSCOORD> <ABSCOORD> Straight cut to absolute X Y; turn laser on with configured speed and power 99%
A9 <RELCOORD> <RELCOORD> Straight cut to relative X Y; turn laser on with configured speed and power 99%
E7 50 <ABSCOORD> <ABSCOORD> Bounding box top left? 30%
E7 51 <ABSCOORD> <ABSCOORD> Bounding box bottom right? 30%
E8 02 E7 01 <CSTRING> Set filename for following transfer (transfer needs to be done really quickly after this!) 90%
E8 01 XX XX Read filename number XX XX
88 <ABSCOORD> <ABSCOORD> straight move to absolute X Y as fast as possible; with laser off 99%
89 <RELCOORD> <RELCOORD> straight move to relative X Y as fast as possible; with laser off 80%