Shop OBEX P1 Docs P2 Docs Learn Events
UDP + ARP driver for Ethernet PHY accessory board — Parallax Forums

UDP + ARP driver for Ethernet PHY accessory board

As I've mentioned in the last live forum I have written an UDP and ARP protocol driver for the Ethernet port of my CNC and flight simulator motion control board (which has the same circuit as the Ethernet accessory board).

UDP is ideal for transmitting repeating smaller data chunks for real time control, for example

  • LED lighting
  • relays, solenoids...
  • motion control
  • sensor input
    UDP supports no segmentation or error correction. But retransmitting lost packets doesn't make much sense, anyway, because old data is replaced by new data in the next cycle. Of course for file transfer or HTML related applications you'd still need an IP stack.

If anyone is interested I could put it on OBEX.

BTW, I also support ARP. At startup, the driver sends a gratuitous ARP message to advertise the IP adress. And it sends normal ARP responses if the PC asks for it. At the moment I only support a hardcoded IP address. Does anybody know an easy way to automatically or manually assign an IP address? DHCP is very complicated, AFAIK, and only works if a router is present in the network which is not always the case.

My idea was to use an UDP broadcast to "ping" all devices connected that have no IP address assigned, yet. If they respond to the ping the PC application can then assign an IP address to each with reverse-ARP.

Comments

  • RaymanRayman Posts: 14,744

    I'm interested. Could be very useful for control systems...
    Would be great to be in OBEX.
    Does it just need one cog?

    For me, I'd just manually assign IPs to everything on the local lan anyway.

    I see you did the accessory board in Eagle. Did you post the files for that? That would be very useful. But, the schematic might be enough.

  • It needs 3 cogs at the moment, two for the RMII driver (RX+TX) and one for the UDP protocol. The RX cog needs to listen all the time for input, so that's really required. The TX and UDP cog can be possibly eliminated. You could call the UDP task from time to time from your main loop and the TX function could be merged with some other PASM driver. I think the code is too big to be called as inline PASM from Spin without disturbing the Interpreter or compiler register usage.

    Yes, the PCB was done in Eagle. I can upload it tomorrow.

  • ke4pjwke4pjw Posts: 1,169

    I wrote a DHCP client that seems to be reliable for the W6100 driver. I don't think it retries if it fails, so that could be better. It should give you an idea of what the exchange is and the data structure of the UDP payloads are. It's actually kind of ingenious the way DHCP was designed.

    pub dhcpclient() | i
    ' Clear IP Saved in config
      IPADDRESS[0] := 0
      IPADDRESS[1] := 0
      IPADDRESS[2] := 0
      IPADDRESS[3] := 0
    
      waitms(3000)
    
      debug("Configuring Protocol Mode UDP on Socket 0", $0D, $0A)
      W6100.Open(68,W6100.PM_UDP4,W6100.IDM_BSR_Socket0_Register)
    
      ' Set Destination IP
      WRITEBUFFER[0] := 255
      WRITEBUFFER[1] := 255
      WRITEBUFFER[2] := 255
      WRITEBUFFER[3] := 255
      W6100.writereg(W6100.REG_Sn_DIPR,W6100.IDM_BSR_Socket0_Register,4,@WRITEBUFFER)
    
      ' Set Destination port
      WRITEBUFFER[0] := 00
      WRITEBUFFER[1] := 67
      W6100.writereg(W6100.REG_Sn_DPORTR,W6100.IDM_BSR_Socket0_Register,2,@WRITEBUFFER)
    
    
      ' Set XID
      RequestID[0] := prng.xrandomize($00,$FF)
      RequestID[1] := prng.xrandomize($00,$FF)
      RequestID[2] := prng.xrandomize($00,$FF)
      RequestID[3] := prng.xrandomize($00,$FF)
      debug("Set XID to: ", uhex_(RequestID[0]),":", uhex_(RequestID[1]),":", uhex_(RequestID[2]),":", uhex_(RequestID[3]))
    
      WRITEBUFFER[0]  := $01 'Op $01 This is a discover packet
      WRITEBUFFER[1]  := $01 'HType $01 Ethernet xBase-T
      WRITEBUFFER[2]  := $06 'HLen $06 because we have 6 Octets in the hardware address.
      WRITEBUFFER[3]  := $00 'Hops setting Hops counter to 0. Is incremented by DHCP relays
      WRITEBUFFER[4]  := RequestID[0] 'XID Unique ID
      WRITEBUFFER[5]  := RequestID[1] 'XID Unique ID
      WRITEBUFFER[6]  := RequestID[2] 'XID Unique ID
      WRITEBUFFER[7]  := RequestID[3] 'XID Unique ID
      WRITEBUFFER[8]  := $00 'Secs Elapsed Seconds (Not used)
      WRITEBUFFER[9]  := $00 'Secs Elapsed Seconds (Not used)
      WRITEBUFFER[10] := $80 'Flags $80 sets this to broadcast from the DHCP server, becasue we are not on the network yet.
      WRITEBUFFER[11] := $00 'Flags Reserved
      WRITEBUFFER[12] := $00 'CIAddr We have no IP yet, so set to $00
      WRITEBUFFER[13] := $00 'CIAddr We have no IP yet, so set to $00
      WRITEBUFFER[14] := $00 'CIAddr We have no IP yet, so set to $00
      WRITEBUFFER[15] := $00 'CIAddr We have no IP yet, so set to $00
      WRITEBUFFER[16] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[17] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[18] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[19] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[20] := $00 'SIAddr Client Request, so set to $00
      WRITEBUFFER[21] := $00 'SIAddr Client Request, so set to $00
      WRITEBUFFER[22] := $00 'SIAddr Client Request, so set to $00
      WRITEBUFFER[23] := $00 'SIAddr Client Request, so set to $00
      WRITEBUFFER[24] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[25] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[26] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[27] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[28] := $00 'CHAddr MAC address
      WRITEBUFFER[29] := $00 'CHAddr MAC address
      WRITEBUFFER[30] := $00 'CHAddr MAC address
      WRITEBUFFER[31] := $00 'CHAddr MAC address
      WRITEBUFFER[32] := $00 'CHAddr MAC address
      WRITEBUFFER[33] := $00 'CHAddr MAC address
      BYTEFILL(@WRITEBUFFER + 34, $00, 10) 'CHAddr fill with 0's
      BYTEFILL(@WRITEBUFFER + 44, $00, 64) 'bootp.server fill with 0's
      BYTEFILL(@WRITEBUFFER + 108, $00, 128) 'File fill with 0's
      WRITEBUFFER[236] := $63 'Magic Cookie
      WRITEBUFFER[237] := $82 'Magic Cookie
      WRITEBUFFER[238] := $53 'Magic Cookie
      WRITEBUFFER[239] := $63 'Magic Cookie
      WRITEBUFFER[240] :=  53 'Option 53
      WRITEBUFFER[241] := $01 'Option 53 Length
      WRITEBUFFER[242] := $01 'Option 53 Discovery Value
      WRITEBUFFER[243] :=  55 'Option 55
      WRITEBUFFER[244] := $03 'Option 55 Length
      WRITEBUFFER[245] := $01 'Option 55 Request Subnet Mask
      WRITEBUFFER[246] := $03 'Option 3 Request Router
      WRITEBUFFER[247] := $06 'Option 6 Request DNS Server
      WRITEBUFFER[248] := $FF 'End this Smile
    
      W6100.SendData(W6100.IDM_BSR_Socket0_Register,@WRITEBUFFER,249)
    
      repeat until READBUFFER[12] == RequestID[0] && READBUFFER[13] == RequestID[1] && READBUFFER[14] == RequestID[2] && READBUFFER[15] == RequestID[3]
         repeat until W6100.ReceiveData(W6100.IDM_BSR_Socket0_Register,1) == 0
         psize := W6100.ReceiveProcess(W6100.IDM_BSR_Socket0_Register,@READBUFFER)
    
        debug("UDP Header Information: ", uhex_(READBUFFER[0]),uhex_(READBUFFER[1]) )
    
        debug("UDP Source IP: ",udec_(READBUFFER[2]),".",udec_(READBUFFER[3]),".",udec_(READBUFFER[4]),".",udec_(READBUFFER[5]) )
    
        debug("UDP Port Number: ",udec_(READBUFFER[6]), udec_(READBUFFER[7]) )
    
        debug("Received XID is: ",uhex_(READBUFFER[12]),":",uhex_(READBUFFER[13]),":",uhex_(READBUFFER[14]),":",uhex_(READBUFFER[15]))
    
      debug("DHCP Assigned IP Address: ",udec_(READBUFFER[24]),".", udec_(READBUFFER[25]),".",udec_(READBUFFER[26]),".",udec_(READBUFFER[27])  )
    
      IPADDRESS[0] := READBUFFER[24]
      IPADDRESS[1] := READBUFFER[25]
      IPADDRESS[2] := READBUFFER[26]
      IPADDRESS[3] := READBUFFER[27]
    
      debug("Request IP Address: ", udec_(IPADDRESS[0]),".", udec_(IPADDRESS[1]),".",udec_(IPADDRESS[2]),".",udec_(IPADDRESS[3]))
    
      debug("Magic Cookie: ", uhex_(READBUFFER[244]), uhex_(READBUFFER[245]),uhex_(READBUFFER[246]), uhex_(READBUFFER[247]) )
    
      i:=248
      repeat until i >= psize || READBUFFER[i] == $FF
        case READBUFFER[i]
          53  :
                 DHCPMESSAGETYPE := READBUFFER[i+2]
                 debug("DHCP Message Type: ", udec_(READBUFFER[i+2]))
    
                 i := i + 3
          54  :
                   debug("DHCP Server: ", udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
                   DHCPSERVER[0] := READBUFFER[i+2]
                   DHCPSERVER[1] := READBUFFER[i+3]
                   DHCPSERVER[2] := READBUFFER[i+4]
                   DHCPSERVER[3] := READBUFFER[i+5]
                   i := i + 6
    
          1 :
                   debug("DHCP Subnet Mask: ", udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
                   SUBNETMASK[0] := READBUFFER[i+2]
                   SUBNETMASK[1] := READBUFFER[i+3]
                   SUBNETMASK[2] := READBUFFER[i+4]
                   SUBNETMASK[3] := READBUFFER[i+5]
                   i := i + 6
          3 :
                   debug("DHCP Gateway: " , udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
                   GATEWAY[0] := READBUFFER[i+2]
                   GATEWAY[1] := READBUFFER[i+3]
                   GATEWAY[2] := READBUFFER[i+4]
                   GATEWAY[3] := READBUFFER[i+5]
                   i := i + 6
          6 :
                   debug("DNS Server: " , udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
                   DNS[0] := READBUFFER[i+2]
                   DNS[1] := READBUFFER[i+3]
                   DNS[2] := READBUFFER[i+4]
                   DNS[3] := READBUFFER[i+5]
                   i := i + 6
    
          $ff :
                   debug("Detected end of DHCP Options.")
    
          OTHER :
                   debug("DHCP Option Unknown: ", udec_(READBUFFER[i]), " Incrementing ", udec_(READBUFFER[i+1] + 2))
                   i++
                   i := i + READBUFFER[i]
                   i++
    
    
      W6100.readreg(W6100.REG_SIPR0,W6100.IDM_BSR_Common_Register,4,@READBUFFER)
      debug("Current IP Address: ",udec_(READBUFFER[0]),".",udec_(READBUFFER[1]),".",udec_(READBUFFER[2]),".",udec_(READBUFFER[3]))
    
      W6100.readreg(W6100.REG_GAR0,W6100.IDM_BSR_Common_Register,4,@READBUFFER)
      debug("Current Subnet Mask: ",udec_(READBUFFER[0]),".",udec_(READBUFFER[1]),".",udec_(READBUFFER[2]),".",udec_(READBUFFER[3]))
    
      WRITEBUFFER[0]  := $01 'Op $01 This is a request packet
      WRITEBUFFER[1]  := $01 'HType $01 Ethernet xBase-T
      WRITEBUFFER[2]  := $06 'HLen $06 because we have 6 Octets in the hardware address.
      WRITEBUFFER[3]  := $00 'Hops setting Hops counter to 0. Is incremented by DHCP relays
      WRITEBUFFER[4]  := RequestID[0] 'XID Unique ID
      WRITEBUFFER[5]  := RequestID[1] 'XID Unique ID
      WRITEBUFFER[6]  := RequestID[2] 'XID Unique ID
      WRITEBUFFER[7]  := RequestID[3] 'XID Unique ID
      WRITEBUFFER[8]  := $00 'Secs Elapsed Seconds (Not used)
      WRITEBUFFER[9]  := $00 'Secs Elapsed Seconds (Not used)
      WRITEBUFFER[10] := $80 'Flags $80 sets this to broadcast from the DHCP server, becasue we are not on the network yet.
      WRITEBUFFER[11] := $00 'Flags Reserved
      WRITEBUFFER[12] := IPADDRESS[0] 'CIAddr
      WRITEBUFFER[13] := IPADDRESS[1] 'CIAddr
      WRITEBUFFER[14] := IPADDRESS[2] 'CIAddr
      WRITEBUFFER[15] := IPADDRESS[3] 'CIAddr
      WRITEBUFFER[16] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[17] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[18] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[19] := $00 'YIAddr Client Request, so set to $00
      WRITEBUFFER[20] := DHCPSERVER[0] 'SIAddr DHCP Server IP
      WRITEBUFFER[21] := DHCPSERVER[1] 'SIAddr DHCP Server IP
      WRITEBUFFER[22] := DHCPSERVER[2] 'SIAddr DHCP Server IP
      WRITEBUFFER[23] := DHCPSERVER[3] 'SIAddr DHCP Server IP
      WRITEBUFFER[24] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[25] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[26] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[27] := $00 'GIAddr Client Request, so set to $00
      WRITEBUFFER[28] := $00 'CHAddr MAC address
      WRITEBUFFER[29] := $00 'CHAddr MAC address
      WRITEBUFFER[30] := $00 'CHAddr MAC address
      WRITEBUFFER[31] := $00 'CHAddr MAC address
      WRITEBUFFER[32] := $00 'CHAddr MAC address
      WRITEBUFFER[33] := $00 'CHAddr MAC address
      BYTEFILL(@WRITEBUFFER + 34, $00, 10) 'CHAddr fill with 0's
      BYTEFILL(@WRITEBUFFER + 44, $00, 64) 'bootp.server fill with 0's
      BYTEFILL(@WRITEBUFFER + 108, $00, 128) 'File fill with 0's
      WRITEBUFFER[236] := $63 'Magic Cookie
      WRITEBUFFER[237] := $82 'Magic Cookie
      WRITEBUFFER[238] := $53 'Magic Cookie
      WRITEBUFFER[239] := $63 'Magic Cookie
      WRITEBUFFER[240] :=  53 'Option 53
      WRITEBUFFER[241] := $01 'Option 53 Length
      WRITEBUFFER[242] := $03 'Option 53 Request Value
      WRITEBUFFER[243] :=  50 'Option 50 ' Requesting this IP
      WRITEBUFFER[244] := $04 'Option 50 Length
      WRITEBUFFER[245] := IPADDRESS[0] 'Option 50 requested IP 1st Octect
      WRITEBUFFER[246] := IPADDRESS[1] 'Option 50 requested IP 2nd Octect
      WRITEBUFFER[247] := IPADDRESS[2] 'Option 50 requested IP 3rd Octect
      WRITEBUFFER[248] := IPADDRESS[3] 'Option 50 requested IP 4th Octect
      WRITEBUFFER[249] := $03 'Option 54 DHCP Server
      WRITEBUFFER[250] := $04 'Option 54 Length
      WRITEBUFFER[251] := DHCPSERVER[0] 'SIAddr DHCP Server IP
      WRITEBUFFER[252] := DHCPSERVER[1] 'SIAddr DHCP Server IP
      WRITEBUFFER[253] := DHCPSERVER[2] 'SIAddr DHCP Server IP
      WRITEBUFFER[254] := DHCPSERVER[3] 'SIAddr DHCP Server IP
      WRITEBUFFER[255] := $FF 'End this Smile
    
      W6100.SendData(W6100.IDM_BSR_Socket0_Register,@WRITEBUFFER,256)
    
      'clearing readbuffer
      BYTEFILL(@READBUFFER,$00,1024)
    
      W6100.readreg(W6100.REG_Sn_IR,W6100.IDM_BSR_Socket0_Register,1,@READBUFFER)
      debug("Checking Sn_IR Socket Status: ", uhex_(READBUFFER[0]))
    
      repeat until READBUFFER[12] == RequestID[0] && READBUFFER[13] == RequestID[1] && READBUFFER[14] == RequestID[2] && READBUFFER[15] == RequestID[3]
         repeat until W6100.ReceiveData(W6100.IDM_BSR_Socket0_Register,1) == 0
         psize := W6100.ReceiveProcess(W6100.IDM_BSR_Socket0_Register,@READBUFFER)
    
        debug("Received XID is: ", uhex_(READBUFFER[12]),":",uhex_(READBUFFER[13]),":",uhex_(READBUFFER[14]),":",uhex_(READBUFFER[15]))
    
      i:=248
      repeat until i >= psize || READBUFFER[i] == $FF
        case READBUFFER[i]
          53  :  debug("DHCP Message Type: ",udec_(READBUFFER[i+2]) )
                 DHCPMESSAGETYPE := READBUFFER[i+2]
    
                 i := i + 3
          54  :  debug("DHCP Server: ", udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
    
                   DHCPSERVER[0] := READBUFFER[i+2]
                   DHCPSERVER[1] := READBUFFER[i+3]
                   DHCPSERVER[2] := READBUFFER[i+4]
                   DHCPSERVER[3] := READBUFFER[i+5]
                   i := i + 6
    
          1 : debug("DHCP Subnet Mask: ", udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
    
                   SUBNETMASK[0] := READBUFFER[i+2]
                   SUBNETMASK[1] := READBUFFER[i+3]
                   SUBNETMASK[2] := READBUFFER[i+4]
                   SUBNETMASK[3] := READBUFFER[i+5]
                   i := i + 6
          3 : debug("DHCP Gateway: ", udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
    
                   GATEWAY[0] := READBUFFER[i+2]
                   GATEWAY[1] := READBUFFER[i+3]
                   GATEWAY[2] := READBUFFER[i+4]
                   GATEWAY[3] := READBUFFER[i+5]
                   i := i + 6
          6 :
                   debug("DNS Server: " , udec_(READBUFFER[i+2]),".",udec_(READBUFFER[i+3]),".",udec_(READBUFFER[i+4]),".",udec_(READBUFFER[i+5]))
                   DNS[0] := READBUFFER[i+2]
                   DNS[1] := READBUFFER[i+3]
                   DNS[2] := READBUFFER[i+4]
                   DNS[3] := READBUFFER[i+5]
                   i := i + 6
    
          $ff : debug("Detected end of DHCP Options.")
    
          OTHER : debug("DHCP Option Unknown: ", udec_(READBUFFER[i]), " Incrementing ", udec_(READBUFFER[i+1] + 2))
                   i++
                   i := i + READBUFFER[i]
                   i++
    
    
      if DHCPMESSAGETYPE <> 5
        debug("DHCP Messagetype: ", udec_(DHCPMESSAGETYPE))
    
        debug("DHCP Failure")
        abort
    
Sign In or Register to comment.