Here you can find a step-by-step tutorial about the Bascom-AVR code to drive an AVR and a RTL8019as ethernetcontroller.

 

Step 1. The first steps.....

You can cut and paste all green parts of the next text and place it into the Bascom-AVR IDE. Going through the code, I will ask you to compile the code and let you know what you should see on a connected Hyperterminal or LED's on the board.

The AVR Ethernet I/O board from www.drftech.com

The Easy Ethernet board from www.edtp.com


The Atmega32 microcontroller that is used on the two above boards have some fusebits. Care should be taken while programming these fusebits.

First, start with a small description what you are planning to do with date and/or version-number.

'-----------------------------------------------------------
' Embedded UDP&WebServer - Atmega32 and RTL8019AS
'-----------------------------------------------------------
' Version 1.0 - april 1 2005

Make a small note what you did with the fusebits of the microcontroller
For this program the following fusebits should be set:

' Fusebits:
' A987:1111 = Crystal/Boden enabled
' H: 1 = JTAG disabled
' G: 0 = Preserve EEprom when chip erase

'
We are going to use a external crystal.

At the Bascom-AVR options put Hardware stack, Soft stack and Framesize all at 64 Bytes.



Study the schematics and put here an inventory, helps to keep things simple

' Hardware:
'
' PA0 - SD0 - Databus
' PA1 - SD1
' PA2 - SD2
' PA3 - SD3
' PA4 - SD4
' PA5 - SD5
' PA6 - SD6
' PA7 - SD7

' PB0 - SA0 - Addressbus
' PB1 - SA1
' PB2 - SA2
' PB3 - SA3
' PB4 - SA4
' PB5 - MOSI
' PB6 - MISO
' PB7 - SCK

' PC0 - I2c SLC
' PC1 - I2c SDA
' PC2 - PC2
' PC3 - PC3
' PC4 - PC4
' PC5 - PC5
' PC6 - PC6
' PC7 - PC7

' PD0 - RXD
' PD1 - TXD
' PD2 - INT0
' PD3 - LE - Latch Enable
' PD4 - RSTDRV
' PD5 - EEDO
' PD6 - IORB
' PD7 - IOWB

$crystal = 7372800
$regfile = "M32def.dat"
$baud = 57600

const debug = 1

#if debug = 1
print "
Hello world"
#endif

end

$crystal is used to give the Mhz or MegaCycles you are using. In this case a crystal of 7.372.800 is used.
$regfile is used to have Bascom-AVR use the right registers for the right microcontroller. In this case we are using a Atmega32.
$baud is used to give the right transmission speed for the serial port we are going to use on the board.

The const debug is used for testpurposes. During programming you can test certain parts by using a #if #endif combination. 

Compile this program. If you don't get any errors put this small program in your Atmega32 using an ISPAVR-programmer. Connect a serial cable from board to PC, start Hyperterminal, 57600 baud, 8 bit, no parity, no handshake, and after resetting the board you should see a message.

On Hyperterminal I see Hello world. On the AVR Ethernetboard I see the powerled and all led's by the relays on. Furthermore, the green and the yellow led near the RTL8019as are also on.

Step 2: Preparing the ports for input and output

Place the next piece of code before your end statement.

' Part that can be used to attach the right ports to the hardware
'
Databus Alias Porta
_databus Alias Pina
Rtldata Alias Porta
Databus_ddr Alias Ddra

To make your code a bit more readable, you can use aliases. Here I give Porta the name Databus and throughout the program I can refer to this alias.

Addrbus Alias Portb
Addrbus_ddr Alias Ddrb

The same for the addressbus. On AVR's each port has a datadirection register. With this datadirectionregister you can make a port ready for input or for output. You can do that for seperate pins but you can do that also for a complete port.

Resetport Alias Portd
Ior_pin Alias Portd.6
Iow_pin Alias Portd.7
Resetport_ddr Alias Ddrd

Portd is a misc. port. Give portd.6 and portd.7 an alias.

Inputport1 Alias Pinc
Inputport2 Alias Portc

Two aliases for portC

Latch Alias Portd.3
Resetport_ddr = &HF8 ' leave RXD and TXD

Give the pin Portd.3 the alias Latch. And the Resetport-datadirection register all output, but leave RXD and TXD untouched.

Step 3: Creating a read and write subroutine for the RTL8019as

Now it is getting serious. We have to add two subroutines. One to read and one to write to the RTL8019as. First we have to declare both subroutines, and we have to do that at the top of the program. Two variables are used and we have to DIM them before we can use them.

You have to add some lines to get, the $crystal, $regfile and $baud are existing, you have to add the three dims and declares to get the following:

$crystal = 7372800
$regfile = "M32def.dat"
$baud = 57600

Dim Regaddr as byte
Dim Regdata as byte
Dim Byte_read as byte

Declare Sub Write_rtl8019as(byval Regaddr as Byte, Byval Regdata as Byte)
Declare Sub Read_rtl8019as(byval Regaddr as byte)

We will be adding a lot of variables and subroutines, so will return a lot here!!

Place two subroutines after the END of your program.

' Routine to read from NIC Control register
'
Sub Read_rtl8019as(regaddr)
Databus_ddr = &H00
Databus = &HFF
Addrbus = Regaddr
Reset Ior_pin
nop
Byte_read = Pina
nop
Set Ior_pin
nop
End Sub

In the read-routine we put the databus in inputmode, and put the address we want to read on the addressbus. We toggle the IOR_PIN and read the value of the databus.

' Routine to write to NIC Control register
'
Sub Write_rtl8019as(regaddr , Regdata)
Addrbus = Regaddr
Databus = Regdata
Databus_ddr  = &HFF
nop
Reset Iow_pin
nop
Set Iow_pin
nop
Databus_ddr = &H00
Databus = &HFF
End Sub

In the write-routine we are putting Regdata on the databus, Regaddr on the addressbus and after that toggle the IOW_PIN. Leaving the subroutine with the Databus as input with pull-ups on.

Compile this and see if it is done without any errors. You could send this to the Atmega32 to see that nothing changes. The flash of the Atmega32 can be programmed over 10.000 times, so don't worry.

Hit the reset button. On Hyperterminal I see Hello world. On the AVR Ethernet board I see the power led and all led's by the relays on. Furthermore, the green and the yellow led near the RTL8019as are also on.

Step 4: Initializing the RTL8019as

Now we have two routines to read and write to the RTL8019as Ethernet controller we can start to initialize it. We are doing this by adding a subroutine called Init_rtl8019as

Go to the top of the program and add a declare of this new subroutine, Sub Init_rtl8019as

Declare Sub Write_rtl8019as(byval Regaddr as Byte, Byval Regdata as Byte)
Declare Sub Read_rtl8019as(byval Regaddr as byte)
Declare Sub Init_rtl8019as

Place under the read and write routines the new routine.

' Routine to initialize the RTL8019AS ethernetchip
'
Sub Init_rtl8019as
Databus_ddr = &H00
Databus = &HFF
Addrbus_ddr = &HFF
Addrbus = &H00
Resetport_ddr = &HF8
Reset Eeprom.5
Set Iow_pin
Set Ior_pin
Set Resetport.4
Waitms 2
Reset Resetport.4
Call Read_rtl8019as(rstport)
Call Write_rtl8019as(rstport , Byte_read)
Waitms 10
'check for good soft reset
Call Read_rtl8019as(isr)
If Byte_read.7 = 0 Then
      Print Msg_initfail
End If
Call Write_rtl8019as(cr , &H21)
Waitms 2
Call Write_rtl8019as(dcr , Dcrval)
Call Write_rtl8019as(rbcr0 , &H00)
Call Write_rtl8019as(rbcr1 , &H00)
Call Write_rtl8019as(rcr , &H04)
Call Write_rtl8019as(tpsr , Txtstart)
Call Write_rtl8019as(tcr , &H02)
Call Write_rtl8019as(pstart , Rxstart)
Call Write_rtl8019as(bnry , Rxstart)
Call Write_rtl8019as(pstop , Rxstop)
Call Write_rtl8019as(cr , &H61)
Waitms 2
Call Write_rtl8019as(curr , Rxstart)
For Hulp1 = 1 To 6
     Call Write_rtl8019as(hulp1 , Mymac(hulp1))
Next Hulp1
Call Write_rtl8019as(cr , &H21)
Call Write_rtl8019as(dcr , Dcrval)
Call Write_rtl8019as(cr , &H22)
Call Write_rtl8019as(isr , &HFF)
Call Write_rtl8019as(imr , Imrval)
Call Write_rtl8019as(tcr , Tcrval)
End Sub

Compile this and see a screen full of errors. That's where I got my gray hair from.

We are referring to a lot of variables that are neither in the M32def.dat or in our own Dim'ed variables. So we have to add some DIM's or in this case variables with a static value, so we use Constants.

Const Rstport = &H18
Const Isr = &H07
Const Msg_initfail = "Init failed"
Const Cr =&H00
Const Dcrval = &H58
Const Dcr = &H0E
Const Rbcr0 = &H0A
Const Rbcr1 = &H0B
Const Rcr = &H0C
Const Tpsr = &H04
Const Txtstart = &H40
Const Tcr = &h0D
Const Pstart = &h01
Const Rxstart = &H46
Const Bnry = &H03
Const Pstop = &H02
Const Rxstop = &H60
Const Curr = &H07

We have to add a variable Hulp1 which is going to be used throughout the program. It is not static so we put it at the DIM'ed variables.

Dim Regaddr as byte
Dim Regdata as byte
Dim Byte_read as byte
Dim Hulp1 as byte

And while we are at it, add our first array. The Mymac(6). It holds the MAC-address of this Ethernet board.

Dim Regaddr as byte
Dim Regdata as byte
Dim Byte_read as byte
Dim Hulp1 as byte
Dim Mymac(6) as byte

The MAC-address of the board is also static, but in Bascom-AVR arrays may not be used with constants, so we do it a bit different. Assigning values to this array. To be honest, for our initialization at the moment we don't need a MAC-address, but later on we can't do without it.

Mymac(1) = 00
Mymac(2) = 10
Mymac(3) = 20
Mymac(4) = 30
Mymac(5) = 40
Mymac(6) = 50

We are going on with a few constants

Const Imr = &H0F
Const Imrval = &H11
Const Tcrval = &H00
 

That's all. Now we have initialized the RTL8019as. An explanation of  all constants used will follow after the next section. But is the RTL8019as alive? On the next section we will check it.

In Bascom-AVR, I can hit Control-W to see the result of the compilation.
This is what I get


1584 bytes used till now. The rest of the options we will see later on.

Step 5: It's alive......

A few lines more and we will know if our RTL8019as is alive. Connect a network cable to the Ethernet board. If you have a HUB or switch, connect a straight cable from PC to HUB or switch, and a straight cable from HUB or switch to Ethernet board. If you don't have a HUB or switch, you can use a crosscable between PC and ethernet board. If you want to know how a crosscable looks like, check www.sbprojects.net and go to the knowledge base.

We have to add an interrupt routine to our program. The RTL8019as is giving an interrupt when a Ethernet packet has arrived. First we have to activate the interrupt. Place in the main part of the program, just before the END the next lines

Call init_rtl8019as

' Sit and wait for an Interrupt from the RTL8019as

Enable Interrupts
Enable Int0
On Int0 Rtl8019as_interrupt
'start the NIC
Call Write_rtl8019as(cr, &H22)
Do
Loop
End

Here interrupts are enabled and Int0 to be specific. When an Interrupt occurs there will be a jump to subroutine Rtl8019as_interrupt. So we have to insert at the subroutine part, (that is after the END statement) a subroutine called Rtl8019as_interrupt. The program will, when not interrupted by the RTL8019as, stay in the DO LOOP part of the program.

'Routine to handle an interrupt from the RTL8019as

Rtl8019as_interrupt:
    Print "Interrupt from RTL8019as"
    'reset the interrupt bits
    Call Write_rtl8019as(isr , &HFF)
    'start the NIC again
    Call Write_rtl8019as(cr, &H22)
Return

It is an interrupt and the above code is the ISR, Interrupt Service Routine. We have to end that routine with a Return.

So, now, from a PC start a ping with a -t option. This means a continuous ping on your network.

On a PC, Start, Run, CMD, and then ping 192.168.0.109 -t will give you this screen.

And this is what you should see on your Hyperterminal screen. On every ping you will receive an interrupt from the RTL801as.

And what do I see on the AVR Ethernet board? The yellow LED is blinking on all pings that passes the network. If you have more traffic on your network you will see that, the yellow LED is blinking more. The red LED's for the relays are on, powerled is on. Near the RTL8019as the green LED is on and red LED is off.

Step 6: What have we done so far?

The code size is about 1800 bytes until now. We have written data into the registers of the Realtek RTL8019as. Registers that are used during initialization, packet transmission and reception. The RTL8019as has a Ring Buffer. It's a classic circular, head and tail buffer with four pointers:
  • Pstart
  • Pstop
  • Curr
  • Bnry

 

  • Pstart (Page Start) is the beginning address of the Ring Buffer. Pstop (Page Stop) the address of the end. Curr (Current Page Pointer) points to the next available buffer area for the next incoming frame. BNRY (Boundary Pointer) points to the next frame to be unloaded from the Ring Buffer.

    Here some more detail. There are several register pages in the RTL8019as.
    &H followed by a number will tell you at what position of the page
    After that is it Read, Write or Read and Write
    After that on which pages it can be found.

    If you want more detail about the registers check the datasheet of the RTL8019as which can be found at https://realtek.info/pdf/8019as.pdf

  • Pstart
    Page Start Register (&h01; Type = W in Page0, Type = R in Page2)
    The page Start Register sets the start page address of the receive buffer ring.
  • Pstop
    Page Stop Register (&h02; Type = W in Page 0, Type = R in Page 2)
    The Page Stop register sets the stop page address of the receive buffer ring. In 8 bit mode the Pstop register should not exceed to &h60, in 16 bit mode the Pstop register should not exceed to &h80
  • Bnry
    Boundary Register (&h03; Type = R/W in Page 0)
    This register is used to prevent overwirte of the receive buffer ring. It is typically used as a pointer indicating the last receive buffer page the host has read.


  • TPSR
    Transmit Page Start Register (&h04; Type = W in Page 0)
    This register sets the start page address of the packet to be transmitted.
  • RBCDR0, 1
    Remote Byte Count Registers (&h0A and &h0B; Type = W in Page 0)
    These two registers set the data byte counts of remote DMA
  • PAR0-5
    Physical Address Registers (&h01 - &h06; Type = R/W in Page 1)
    These rgisters contain the Ethernet node address and are used to compare the destination address of incoming packets for acceptation or rejection. In our program we use the array MyMac() for it.
  • Curr:
    Current Page Register (&h07; Type = R/W in Page 1)
    This register points to the page address of the first receive buffer page to be used for a packet reception.
  • IMR
    Interrupt Mask Register (&h0F; Type = W in Page 0, Type = R in Page2)
    All bits correspond to the bits in the ISR register. Power Up = all 0's. Setting individual bits will enable the corresponding interrupts.
  • DCR
    Data Configuration Register (&h0E; Type = W in Page 0, Type = R in Page 2)
  • TCR
    Transmit Configuration Register (&h0D; Type = W in Page 0, Type = R in Page 2)
  • RCR
    Receive Configuration Register (&h0C; Type = W in Page 0, Type = R in Page 2)
  • CR
    Command Register (&h00; Type = R/W)
    This register is used to select register pages, enable or disable DMA operation and issue commands.
  • ISR
    Interrupt Status Register (&h07; Type = R/W in Page 0)
    This register reflects the NIC status. The host reads it to determine the cause of an interrupt. Individual bits are cleared by writing a "1" into the corresponding bit. It must be cleared after power up.
  • And here are the bits
    7 = RST
    This bit is set when NIC enters reset state and is cleared when a start command is issued to the CR. It is also set when receibe buffer overflowd and is cleared when one or more packets have been read from the buffer.
    6 = RDC
    Set when remote DMA operation has been completed.
    5 = CNT
    Set when MSB of one of more of the network tally counters has been set
    4 = OVW
    This bit is set when the receibe buffer has been exhausted
    3 = TXE
    Transmit error bit is set when a packet transmission is aborted due to excessive collisions
    2 = RXE
    This bit is set when a packet received with one or more of the following errors:
    - CRC error
    - Frame alignment error
    - Missed packet
    1 = PTX
    This bit indicates packet transmitted with no errors.
    0 = PRX
    This bit indicates packet received with no errors.



    First we will make a small ulility to see all pages of the RTL8019as. But after that we are going to use the Interrupt Status Register bits to get a packet and to check for a overrun. 

Step 7: A small utility: Showregs

We have to add another subroutine to show the contents of the RTL8019as registers.

' Routine show the contents of the RTL8019as registers
'
Sub Showregs
Local Cntr1 As Byte
Local Cntr2 As Byte
Call Write_rtl8019as(cr , &H21)
Print
Print "Realtek 8019AS Register dump"
Print
Print "REG Page0 Page1 Page2 Page3"
Print
For Cntr1 = 0 To 15
Cntr2 = Cntr1
Print Hex(cntr2);
Print " ";
Call Write_rtl8019as(cr , &H21)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(cr , &H61)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(cr , &HA1)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read);
Print " ";
Call Write_rtl8019as(cr , &HE1)
Cntr2 = Cntr1
Call Read_rtl8019as(cntr2)
Print Hex(byte_read)
Next Cntr1
End Sub

With the write_rtl8019as-instruction with &h21, &h61, &hA1 and &hE1, we instruct the ethernetcontroller to go to the different pages of registers. At each page we collect 16 values.
Notice the use of LOCAL, if you are sure you wouldn't need variables at other places in your program, you can use locals. The space they take in the internal RAM of the microcontroller is given back when leaving the subroutine.

At the top of the program, at the place we have declared the other subroutines, place

Declare Sub Showregs

And in our main piece of program we have to add a call to showregs, and a stop to halt the program.

Call Init_rtl8019as
Call Showregs
Stop

Enable Interrupts
Enable Int0
On Int0 Rtl8019as_interrupt
'start the NIC
Call Write_rtl8019as(cr , &H22)
Do
Loop
End

So that piece will look like the above part.

If you compile it and send it to the Atmega32,
you will have the next screen on your hyperterminal

Picture below: Notice at the red parts, on page 1 the MAC-address we gave. MyMac()
The 50 70 at page 0 is the ID for a Realtek RTL8019as

If you got your code all mixed up, here you can download the source to the point we have arrived now.

Step1_till_step7.txt

Step 8: Get a packet from the RTL8019as

Now we got the interrupt from the RTL8019as when a packet arrives, we have to grab the packet for further analyzes

Replace the Rtl8019as_interrupt-routine with the follow code:

'Routine to handle an interrupt from the RTL8019as

Rtl8019as_interrupt:
    Disable Int0
    Print "Interrupt from RTL8019as"
    Call Read_rtl8019as(isr)
    If Byte_read.0 = 1 Then
         Call Getpacket
    End if    
    'reset the interrupt bits
    Call Write_rtl8019as(isr , &HFF)
    'start the NIC again
    Call Write_rtl8019as(cr, &H22)
    Enable Int0
Return

Sub Getpacket
    Print "GetPacket"
End Sub

And at the declare part of this program, add the line

Declare Sub Getpacket

Remove the 'Call showregs' and 'Stop' commands.

During the interrupt-handling, we are disabling the interrupt on Int0. We don't want the information to be grabbed garbled. After that we check bit 0 if the Interrupt Service Register if a good packet is in the RTL8019as. If it is, we call a subroutine, at the moment doing nothing, and after resetting the Interrupt bits and starting the RTL8019as again we continue with the enabling of Int0. 

After compiling and sending to the AVR Ethernet board I see this on the Hyperterminal screen

Step 9: Full sized interrupt-routine...

But that is not all to get a packet from the RTL8019as. First of all, we are going to reserve an array called packet(1500) to receive the contents of the RTL8019as ethernet packet. What if the memory of the RTL8019as has been overrun? We will add some more statements in the Rtl8019as_interrupt routine. It will look like this:

Rtl8019as_interrupt:
Print "Interrupt from RTL8019as"
Disable Int0
'read the interrupt status register
Call Read_rtl8019as(isr)
'if the receive buffer has been overrun
If Byte_read.4 = 1 Then
    Call Overrun
End If
'if the receive buffer holds a good packet
If Byte_read.0 = 1 Then
   
Call Getpacket
End If
'make sure the receive buffer ring is empty. If BNRY = CURR, the buffer is empty
Call Read_rtl8019as(bnry)
Data_l = Byte_read
Call Write_rtl8019as(cr , &H62)
Call Read_rtl8019as(curr)
Data_h = Byte_read
Call Write_rtl8019as(cr , &H22)
'buffer is not empty, get next packet
If Data_l <> Data_h Then
   
Call Getpacket
End If
'reset the interrupts bits
Call Write_rtl8019as(isr , &HFF)
Call Write_rtl8019as(cr , &H22)
Enable Int0
Return

The Interrupt Service Register is examined for a overrun (bit 4) or a good packet (bit 0). Furthermore a check is done if the receive buffer is empty.

Some extra's have to be added. First a dummy overrun subroutine

At the Declare part

Declare Sub Overrun

And after the END

Sub Overrun
    Print "Overrun"
End Sub

Furthermore some new variables at the DIM-part of the program. Packet is an array of 1500 bytes. This variable will be the most important variable of this program. We are going to trick it a bit, in step 12 more information......

Dim Data_l as Byte
Dim Data_h as Byte
Dim Packet(1500) as Byte

When I compile the program I use 9% of the flash, 3020 bytes to be precise.

Step 10: Two dummies....

At the moment we have got two dummy subroutines. Getpacket and Overrun. In the Getpacket subroutine I have to convert a low-byte and a high-byte to a Word. In Bascom-AVR that can easily be done using overlays.

Add this at the DIM-part of the program:

Dim Result16 as Word at &HB5
Dim Result16h as Byte at &HB6 overlay
Dim Result16l as Byte at &HB5 overlay

What we do here is we dimension a WORD at the specific memory place &HB5. After that we overlay this WORD with two BYTES, high-part on &HB6 and low-part at &HB5. By filling the two Bytes we get a full WORD, and if we want to split a WORD into two Bytes we can fill just Result16 and have the result in Resul16h and Result16l. I love Bascom-AVR for these little tricks.

We have to add some more variables for the Getpacket subroutine

Dim I as Integer
Dim Rxlen as Word
Dim Hulp2 as Word

Dim Pageheader(4) as Byte

Const Rdmaport = &H10
Const Imr = &H0F
Const Imrval = &H11
Const Tcrval = &H00
Const Rdc = &H40

Oke, here is the complete Getpacket-routine. Replace the dummy Getpacket subroutine with this one.

' Routine to execute send packet command to retrieve the packet

' Sub Getpacket
Call Write_rtl8019as(cr , &H1A)
For I = 0 To 4
     Call Read_rtl8019as(rdmaport)
     Pageheader(i + 1) = Byte_read
Next I
'watch it. overlay variables
Result16h = T_enetpacketlenh
Result16l = T_enetpacketlenl
Rxlen = Result16
Hulp2 = Rxlen + 1
For I = 1 To Hulp2
     Call Read_rtl8019as(rdmaport)
     'dump any bytes that will overrun the receive buffer
     If I < 1500 Then
          Packet(i + 1) = Byte_read
     End If
Next I
Hulp1 = Byte_read And Rdc
If Hulp1 <> 64 Then
     Call Read_rtl8019as(isr)
End If
Call Write_rtl8019as(isr , &HFF)
End Sub

Send packet command is written to the RTL8019as, Length of the packet is retrieved. Packet-array is filled with the packet from the RTL8019as.

And while we are busy here is the Overrun subroutine.
Place this at the DIM-part

Dim Resend as Byte
Dim Hulp3 as Word
Dim Hulp4 as Byte

And place this under the END statement

Sub Overrun
Call Read_rtl8019as(cr)
Data_l = Byte_read
Call Write_rtl8019as(cr , &H21)
Waitms 2
Call Write_rtl8019as(rbcr0 , &H00)
Call Write_rtl8019as(rbcr1 , &H00)
Hulp1 = Data_l And &H04
If Hulp1 <> 0 Then
     Resend = 0
Else
      If Hulp1 = 0 Then
           Call Read_rtl8019as(isr)
           Data_l = Byte_read
           Hulp1 = Data_l And &H02
           Hulp4 = Data_l And &H08
            Hulp3 = Hulp1 Or Hulp4
            If Hulp3 > 0 Then
                 Resend = 0
            Else
                 Resend = 1
            End If
      End If
End If
Call Write_rtl8019as(tcr , &H02)
Call Write_rtl8019as(cr , &H22)
Call Write_rtl8019as(bnry , Rxstart)
Call Write_rtl8019as(cr , &H62)
Call Write_rtl8019as(curr , Rxstart)
Call Write_rtl8019as(cr , &H22)
Call Write_rtl8019as(isr , &H10)
Call Write_rtl8019as(tcr , Tcrval)
End Sub

Will put some more comments here later on.

Step 11: Array Packet and its overlays...

We will pin-point array Packet on a fixed spot in memory.
Replace the existing Dim Packet(1500) as byte with

'don't change the address of Packet(1500) rest is depending on it!!!
Dim Packet(1500) as Byte At &HC0

And now we are overlaying this array packet with a lot of variables.

'Ethernet packet destination
Dim T_enetpacketdest0 As Byte At &HC0 Overlay
Dim T_enetpacketdest1 As Byte At &HC1 Overlay
Dim T_enetpacketdest2 As Byte At &HC2 Overlay
Dim T_enetpacketdest3 As Byte At &HC3 Overlay
Dim T_enetpacketdest4 As Byte At &HC4 Overlay
Dim T_enetpacketdest5 As Byte At &HC5 Overlay

'Ethernet packet source
Dim T_enetpacketsrc0 As Byte At &HC6 Overlay
Dim T_enetpacketsrc1 As Byte At &HC7 Overlay
Dim T_enetpacketsrc2 As Byte At &HC8 Overlay
Dim T_enetpacketsrc3 As Byte At &HC9 Overlay
Dim T_enetpacketsrc4 As Byte At &HCA Overlay
Dim T_enetpacketsrc5 As Byte At &HCB Overlay

'Ethernet packet type
Dim T_enetpackettype As Word At &HCC Overlay

Dim T_arp_hwtype1 As Byte At &HCF Overlay

'Arp
Dim T_arp_prttype1 As Byte At &HD1 Overlay
Dim T_arp_hwlen As Byte At &HD2 Overlay
Dim T_arp_prlen As Byte At &HD3 Overlay
Dim T_arp_op1 As Byte At &HD5 Overlay

'arp source ip address
Dim T_arp_sipaddr0 As Byte At &HDC Overlay
Dim T_arp_sipaddr1 As Byte At &HDD Overlay
Dim T_arp_sipaddr2 As Byte At &HDE Overlay
Dim T_arp_sipaddr3 As Byte At &HDF Overlay

'arp target IP address
Dim T_arp_tipaddr As Long At &HE6 Overlay

'IP header layout IP version and header length
Dim T_ip_vers_len As Byte At &HCE Overlay

'packet length
Dim T_ip_pktlen0 As Byte At &HD0 Overlay
Dim T_ip_pktlen1 As Byte At &HD1 Overlay

'protocol (ICMP=1, TCP=6, UDP=11)
Dim T_ip_proto As Byte At &HD7 Overlay

'header checksum
Dim T_ip_hdr_cksum0 As Byte At &HD8 Overlay
Dim T_ip_hdr_cksum1 As Byte At &HD9 Overlay
Dim T_ip_hdr_cksum As Word At &HD8 Overlay

'IP address of source
Dim T_ip_srcaddr0 As Byte At &HDA Overlay
Dim T_ip_srcaddr1 As Byte At &HDB Overlay
Dim T_ip_srcaddr2 As Byte At &HDC Overlay
Dim T_ip_srcaddr3 As Byte At &HDD Overlay
Dim T_ip_srcaddr As Long At &HDA Overlay

'IP address of destination
Dim T_ip_destaddr0 As Byte At &HDE Overlay
Dim T_ip_destaddr1 As Byte At &HDF Overlay
Dim T_ip_destaddr2 As Byte At &HE0 Overlay
Dim T_ip_destaddr3 As Byte At &HE1 Overlay
Dim T_ip_destaddr As Long At &HDE Overlay
Dim T_icmp_type As Byte At &HE2 Overlay
Dim T_icmp_code As Byte At &HE3 Overlay
Dim T_icmp_cksum0 As Byte At &HE4 Overlay
Dim T_icmp_cksum1 As Byte At &HE5 Overlay
Dim T_icmp_cksum As Word At &HE4 Overlay
'
Dim Tcp_srcporth As Byte At &HE2 Overlay
Dim Tcp_srcportl As Byte At &HE3 Overlay
Dim Tcp_destporth As Byte At &HE4 Overlay
Dim Tcp_destportl As Byte At &HE5 Overlay

Dim Tcp_seqnum3 As Byte At &HE6 Overlay
Dim Tcp_seqnum2 As Byte At &HE7 Overlay
Dim Tcp_seqnum1 As Byte At &HE8 Overlay
Dim Tcp_seqnum0 As Byte At &HE9 Overlay

Dim Tcp_acknum3 As Byte At &HEA Overlay
Dim Tcp_acknum2 As Byte At &HEB Overlay
Dim Tcp_acknum1 As Byte At &HEC Overlay
Dim Tcp_acknum0 As Byte At &HED Overlay

Dim Tcp_hdr As Byte At &HEE Overlay
Dim Tcp_flags As Byte At &HEF Overlay
Dim Tcp_cksumh As Byte At &HF2 Overlay
Dim Tcp_cksuml As Byte At &HF3 Overlay
Dim Tcp_cksum As Word At &HF2 Overlay
'
'UDP header
Dim T_udp_srcport0 As Byte At &HE2 Overlay
Dim T_udp_srcport1 As Byte At &HE3 Overlay

Dim T_udp_destport0 As Byte At &HE4 Overlay
Dim T_udp_destport1 As Byte At &HE5 Overlay
Dim T_udp_destport As Word At &HE4 Overlay

Dim T_udp_len0 As Byte At &HE6 Overlay
Dim T_udp_len1 As Byte At &HE7 Overlay

Dim T_udp_chksum0 As Byte At &HE8 Overlay
Dim T_udp_chksum1 As Byte At &HE9 Overlay
Dim T_udp_chksum As Word At &HE8 Overlay

Dim T_udp_data As Byte At &HEA Overlay

We can, without much effort, read and write in the array packet. At some places we can choose to write two BYTES or a single WORD (see for example T_udp_chksum)

And here is another funny one. At the DIM-part insert the next two lines. Some reservations for our IP-number we are going to use.

Dim Myip(4) As Byte At &H64
Dim My_ip As Long At &H64 Overlay

Myip(1)=192
Myip(2)=168
Myip(3)=0
Myip(4)=100

Have to add some more constants at the Const-part

'IP Protocol types
'icmp
Const Prot_icmp = &H01
'tcp
Const Prot_tcp = &H06
'udp
Const Prot_udp = &H11

When I compile this the total bytes are around 4200 bytes. 12% of the Atmega32's flash.

We are almost there to receive our first packet. After step 13 I will put the complete source as we have it till now. BTW if you get errors when using overlays, shift them more to the top of the program. If the memory is already taken by the reservation of a variable, you can't use that spot again.

Step 12: ARP, ICMP, UDP or TCP?

In the Getpacket routine we have to make some decisions, the packet that is arriving is it ARP, ICMP (ping), UDP or TCP? We have to add a few lines and the complete subroutine will look like this:

' Sub Getpacket
Call Write_rtl8019as(cr , &H1A)
For I = 0 To 4
     Call Read_rtl8019as(rdmaport)
     Pageheader(i + 1) = Byte_read
Next I
'watch it. overlay variables
Result16h = T_enetpacketlenh
Result16l = T_enetpacketlenl
Rxlen = Result16
Hulp2 = Rxlen + 1
For I = 1 To Hulp2
     Call Read_rtl8019as(rdmaport)
    
'dump any bytes that will overrun the receive buffer
     If I < 1500 Then
          Packet(i + 1) = Byte_read
     End If
Next I
Hulp1 = Byte_read And Rdc
If Hulp1 <> 64 Then
     Call Read_rtl8019as(isr)
End If
Call Write_rtl8019as(isr , &HFF)

' Routine to process an ARP packet
'
If T_enetpackettype = &H0608 Then
     If T_arp_hwtype1 = &H01 Then
          If T_arp_prttype1 = &H00 Then
               If T_arp_hwlen = &H06 Then
                    If T_arp_prlen = &H04 Then
                         If T_arp_op1 = &H01 Then
                              If My_ip = T_arp_tipaddr Then
                                     Call Arp
                              End If
                         End If
                    End If
              End If
         End If
     End If
End If


' Routine to go ahead with icmp, udp or tcp
'
If T_enetpackettype = &H0008 Then
     If T_ip_destaddr = My_ip Then
          Select Case T_ip_proto
               Case Prot_icmp : Call Icmp
               Case Prot_tcp : Call Tcp
               Case Prot_udp : Call Udp
          End Select
     End If
End If
End Sub

To get this compiled we have to add a few dummy routines:

At the declare-part of the program

Declare Sub Arp
Declare Sub Icmp
Declare Sub Udp
Declare sub Tcp

At the subroutine-part of the program

Sub Arp
End Sub

Sub Icmp
End Sub

Sub Udp
End Sub

Sub Tcp
End Sub

We are nearly there......

Step 13: ARP
Address Resolution Protocol (arp)

The address resolution protocol (arp) is a protocol used by the Internet Protocol (IP), specifically IPv4, to map IP network addresses to the hardware addresses (MAC-address) used by a data link protocol. The protocol operates below the network layer as a part of the interface between the OSI network and OSI link layer. 

The term address resolution refers to the process of finding an address of a computer in a network. The address is "resolved" using a protocol in which a piece of information is sent by a client process executing on the local computer to a server process executing on a remote computer. The information received by the server allows the server to uniquely identify the network system for which the address was required and therefore to provide the required address. The address resolution procedure is completed when the client receives a response from the server containing the required address.

First a few constants

Const Rsar0 = &h08
Const Rsar1 = &H09

Const Tbcr0 = &H05
Const Tbcr1 as &H06

Dim T as byte

And here is the ARP subroutine

' Routine to handle ARP-traffic
'
Sub Arp
'Start the NIC
Call Write_rtl8019as(cr , &H22)

'load beginning page for transmit buffer
Call Write_rtl8019as(tpsr , Txtstart)

'set start address for remote DMA operation
Call Write_rtl8019as(rsar0 , &H00)
Call Write_rtl8019as(rsar1 , &H40)

'clear the interrupts
Call Write_rtl8019as(isr , &HFF)

'load data byte count for remote DMA
Call Write_rtl8019as(rbcr0 , &H3C)
Call Write_rtl8019as(rbcr1 , &H00)

'do remote write operation
Call Write_rtl8019as(cr , &H12)

'write destination MAC address
Call Write_dest_mac

'write source address
For I = 1 To 6
     Call Write_rtl8019as(rdmaport , Mymac(i))
Next I

'arp target IP address
'arp_op1 = packet(&h16)

Packet(&H16) = &H02
For I = 0 To 9
     Hulp1 = &HCC + I
     T = Peek(hulp1)
     Call Write_rtl8019as(rdmaport , T)
Next I

'write ethernet module mac address
For I = 1 To 6
     Call Write_rtl8019as(rdmaport , Mymac(i))
Next I

'write myip
For I = 1 To 4
     Call Write_rtl8019as(rdmaport , Myip(i))
Next I

'write remote mac address
Call Write_dest_mac

'write remote IP address
Call Write_rtl8019as(rdmaport , T_arp_sipaddr0)
Call Write_rtl8019as(rdmaport , T_arp_sipaddr1)
Call Write_rtl8019as(rdmaport , T_arp_sipaddr2)
Call Write_rtl8019as(rdmaport , T_arp_sipaddr3)

'write som pad characters to fill out the packet to the minimum length
For I = 0 To &H11
     Call Write_rtl8019as(rdmaport , &H00)
Next I

'make sure the DMA operation has succesfully completed
Byte_read = 0
Do
     Hulp1 = Byte_read And Rdc
     Call Read_rtl8019as(isr)
Loop Until Hulp1 = 0

'load numbers of bytes to be transmitted
Call Write_rtl8019as(tbcr0 , &H3C)
Call Write_rtl8019as(tbcr1 , &H00)

'send the contents of the transmit buffer onto the network
Call Write_rtl8019as(cr , &H24)
End Sub

There is a part that will return more then once, so I put it in a subroutine

Declare Sub Write_dest_mac

Sub Write_dest_mac
Call Write_rtl8019as(rdmaport , T_enetpacketsrc0)
Call Write_rtl8019as(rdmaport , T_enetpacketsrc1)
Call Write_rtl8019as(rdmaport , T_enetpacketsrc2)
Call Write_rtl8019as(rdmaport , T_enetpacketsrc3)
Call Write_rtl8019as(rdmaport , T_enetpacketsrc4)
Call Write_rtl8019as(rdmaport , T_enetpacketsrc5)
End Sub

Should you ever decide to put the packet-array in external RAM, then the peek-option wouldn't work. You have to use the inp-instruction instead.

On a PC do a Start, Run, CMD

arp -a

to see the ARP-table. You will see the ARP-table with one or more Internet Addresses.

Do a PING to your AVR Ethernet board.

After that arp -a again
If your ARP-function is working you will see the Internet Address and Physical Address (MAC-address) of your AVR Ethernet board in the list.

From here on I continue with ICMP (ping), UDP and TCP. But now you have the base for your own applications. An 
  • Interrupt Service routine
  • Read/Write routines for the RTL8019as
  • an array called Packet with 1500 bytes read from the RTL8019as
  • a bunch of variables to every detail of the ethernetpacket header and
  • a response to an ARP-request.

The total amount of flash used is: 6098 Bytes.

Remarks

You can and should comment your program for clarity and your later sanity. You can use REM or ' followed by your comment. All statements after REM or ' are treated as comments so you cannot use statements on the same line after a REM statement.

Block comments can be used too:

'( start block comment
print "This will not be compiled
') end block comment

If you use these block comments to remark the complete
subroutine showregs, the total amount of flash that is used so far is:
5234 Bytes 

The sourcecode up to this point.

It is a small step......

Step 14: ICMP
We carry on with the next command PING.

PING

A utility to determine whether a specific IP address is accessible. It works by sending a packet to the specified address and waiting for a reply. PING is used primarily to troubleshoot Internet connections. There are many freeware and shareware Ping utilities available for personal computers.

It is often believed that "Ping" is an abbreviation for Packet Internet Groper, but Ping's author has stated that the names comes from the sound that a sonar makes.

There are a few new subroutines you have ta add:
ICMP, Setipaddrs, Icmp_checksum, Echopacket, Packetshape and IP_header_checksum.

Declare Sub Setipaddrs
Declare Sub Echopacket
Declare Sub Icmp_checksum
Declare Sub Packetshape
Declare Sub IP_header_cheksum

Dim Txlen as Word
Dim I_header_length As Word
Dim I_odd As Byte
Dim I_chksum32 As Long
Dim Hdr_chksum as Long

Dim Hulp6 As Word At &HAF
Dim Hulp6h As Byte At &HB0 Overlay
Dim Hulp6l As Byte At &HAF Overlay

Dim I_x As Word
Dim I_hulp1 As Word
Dim I_checksum16 As Word
Dim I_temp16 As Word

Dim I_value16 As Word At &HB1
Dim I_value16h As Byte At &HB2 Overlay
Dim I_value16l As Byte At &HB1 Overlay

Dim Ip_value16 As Word At &HB3
Dim Ip_value16h As Byte At &HB4 Overlay
Dim Ip_value16l As Byte At &HB3 Overlay

 

Sub Icmp

'set echo reply
T_icmp_type = &h00
T_icmp_code = &H00

'setup the IP-header
Call Setipaddrs
Call Icmp_checksum
Call Echopacket
End Sub

Routine to handle the source/destination address'
'
Sub Setipaddrs
T_ip_destaddr = T_ip_srcaddr

'make ethernet module IP address source address
T_ip_srcaddr = My_ip
Call Packetshape
Call Ip_header_checksum
End Sub

 

' Routine to echo a complete packet
'
Sub Echopacket
Call Write_rtl8019as(cr , &H22)
Call Write_rtl8019as(tpsr , Txtstart)
Call Write_rtl8019as(rsar0 , &H00)
Call Write_rtl8019as(rsar1 , &H40)
Call Write_rtl8019as(isr , &HFF)
Hulp1 = T_enetpacketlenl - 4
Call Write_rtl8019as(rbcr0 , Hulp1)
Call Write_rtl8019as(rbcr1 , T_enetpacketlenh)
Call Write_rtl8019as(cr , &H12)
Result16h = T_enetpacketlenh

Result16l = T_enetpacketlenl
Result16 = Result16 - 4
Txlen = Result16

'write the complete packet to the RTL8019AS from packet(1) to packet(txlen+1)
Hulp2 = Txlen + 1
For I = 1 To Hulp2
     Call Write_rtl8019as(rdmaport , Packet(i))
Next I
Byte_read = 0
While Hulp1 <> 0
     Hulp1 = Byte_read And Rdc
     Call Read_rtl8019as(isr)
Wend
Hulp1 = T_enetpacketlenl - 4
Call Write_rtl8019as(tbcr0 , Hulp1)
Call Write_rtl8019as(tbcr1 , T_enetpacketlenh)
Call Write_rtl8019as(cr , &H24)
End Sub

 

Sub Packetshape

'move hardware source address to destination address

T_enetpacketdest0 = T_enetpacketsrc0
T_enetpacketdest1 = T_enetpacketsrc1
T_enetpacketdest2 = T_enetpacketsrc2
T_enetpacketdest3 = T_enetpacketsrc3
T_enetpacketdest4 = T_enetpacketsrc4
T_enetpacketdest5 = T_enetpacketsrc5

' Make ethernet module mac address the source address
T_enetpacketsrc0 = Mymac(1)
T_enetpacketsrc1 = Mymac(2)
T_enetpacketsrc2 = Mymac(3)
T_enetpacketsrc3 = Mymac(4)
T_enetpacketsrc4 = Mymac(5)
T_enetpacketsrc5 = Mymac(6)
End Sub

 

' Routine to calculate a ICMP-checksum
'
Sub Icmp_checksum

'clear the ICMP checksum
T_icmp_cksum = &H00

'calculate the ICMP checksum
I_header_length = T_ip_pktlen1 - 20
'I_header_length = I_header_length - 20
I_odd = I_header_length Mod 2

'14 for MAC-part
'20 for IP-header
'start on 35
'ip_pktlen = 00 3c (60)
'icmp-packetlengte = ip_pktlen - ip_header
I_chksum32 = 0
'Total packetlength - ip_header - 1
Hulp6h = T_ip_pktlen0 
Hulp6l = T_ip_pktlen1
Hulp6 = Hulp6 + 13
For I_x = 35 To Hulp6 Step 2
     I_value16h = Packet(i_x)
     I_hulp1 = I_x + 1
     I_value16l = Packet(i_hulp1)
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     I_value16h = Packet(i_x)
     I_value16l = 0
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = I_chksum32
I_temp16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_temp16
T_icmp_cksum0 = High(i_checksum16)
T_icmp_cksum1 = Low(i_checksum16)
End Sub

' Routine to calculate a IP-header checksum
'
Sub Ip_header_checksum
Local Ip_x As Byte
Local Ip_hulp1 As Byte
Local Ip_chksum32 As Long
Local Ip_checksum16 As Word
Local Ip_temp16 As Word
Local Ip_header_length As Byte
'calculate the IP header checksum
T_ip_hdr_cksum = &H00
Hdr_chksum = 0
Ip_header_length = T_ip_vers_len And &H0F
Ip_header_length = 4 * Ip_header_length
Hulp1 = &H0E + Ip_header_length
Ip_chksum32 = 0
For Ip_x = 15 To Hulp1 Step 2
     Ip_value16h = Packet(ip_x)
     Ip_hulp1 = Ip_x + 1
     Ip_value16l = Packet(ip_hulp1)
     Ip_value16 = Not Ip_value16
     Ip_chksum32 = Ip_chksum32 + Ip_value16
Next Ip_x
Ip_checksum16 = Ip_chksum32
Ip_temp16 = Highw(ip_chksum32)
Ip_checksum16 = Ip_checksum16 + Ip_temp16
T_ip_hdr_cksum0 = High(ip_checksum16)
T_ip_hdr_cksum1 = Low(ip_checksum16)
End Sub

Ping at work. The first PING takes somewhat longer, it has to do with the ARP-routine. First the Mac-address of the IP-number must be fetched.

What I see on the AVR Ethernet board is both green and yellow LED's are blinking.

In Bascom-AVR, at Program, Show result (or with the hotkey Control-W) we can see that the flash used is 7176 Bytes, 21% of the flash of the Atmega32.

When I check the Hyperterm connection, it is still getting the Interrupt from RTL8019as message, this slows down things. So when I make the variable Debug 0, no messages will be send to the serial port and the Ping response will be faster. 7 and 4 ms.

Here the source of the Bascom-AVR code till this point

Step 15: ICMP and IP Checksum calculation explained
First I show you some captures of Sniffer screens. I use Iris 4.0.

Total frame size is 74 byte. The MAC-address we are using 00-10-20-30-40-50 is seen as a Welch Allyn, Data Collection device.

 

 

Destination MAC-address and source address, followed by DoD IP.

Destination MAC-address

Source MAC-address

 

IP version 4 header. 20 byte long.

45 = IP version 4 (that's the 4 in this byte) and a length of 20 bytes (4 times 5)

Length 60 bytes

Protocol 1: ICMP

Checksum IP-header

ICMP-header

Type 0: echo reply

ICMP checksum

The payload of a ping, with no special parameters in Windows, 32 byte.

And what if you would do a PING -l 200 (a ping with a payload of 200 bytes)

Payload of 200 bytes

I will show the calculation of the IP-header-checksum and of the ICMP-checksum here:

This is only an example to be run through the Bascom-AVR Simulator.
Don't insert this in your main code!!

To explain how the IP-header-checksum is calculated we take the ip-header-checksum subroutine and simulate the calculation of this checksum. 

Make a new program in Bascom-AVR and cut and paste the program that follows in it.
Compile it, start the Bascom-AVR simulator, make the terminal window a bit bigger and run the program. You can follow the calculation of the ip-header-checksum.

'
'IP_header_checksum Example in the Simulator
'
' Routine to calculate a IP-header checksum
'

' The frame is 74 bytes long
Dim Packet(74) As Byte At &HC0

' temp variables, only used in this example
Dim X As Byte
Dim Y As Byte

' a 4 byte long
Dim Hdr_chksum As Long

' from the original program
Dim Hulp1 As Byte

' from the original program
' a WORD at &HB3, and two seperate BYTES overlaying it at &HB3 and &HB4
Dim Ip_value16 As Word At &HB3
Dim Ip_value16h As Byte At &HB4 Overlay
Dim Ip_value16l As Byte At &HB3 Overlay

'from the original program (in the frame it has the value &H45)
Dim T_ip_vers_len As Byte At &HCE Overlay

'header checksum
'this will be stored in packet &HD8-&HC0 (&H18) and &HD9-&HC0 (&H19)
Dim T_ip_hdr_cksum0 As Byte At &HD8 Overlay
Dim T_ip_hdr_cksum1 As Byte At &HD9 Overlay
Dim T_ip_hdr_cksum As Word At &HD8 Overlay

Declare Sub Ip_header_checksum

Restore Frame

'Read the frame of 74 bytes in the array Packet()
For X = 1 To 74
     Read Y
     Packet(x) = Y
     Print "Packet(" ; X ; ") = " ; Hex(y)
Next X

' Oke, lets go, to the original Ip_header_checksum subroutine
Call Ip_header_checksum

Print "That's it folks....."


End

Frame:
Data &H00 , &H00 , &HC5 , &H0E , &HEC , &H5A , &H00 , &H10 , &H20 , &H30 , &H40 , &H50 , &H08 , &H00 , &H45 , &H00
Data &H00 , &H3C , &H29 , &H2C , &H00 , &H00 , &H80 , &H01 , &H8F , &H40 , &HC0 , &HA8 , &H00 , &H64 , &HC0 , &HA8
Data &H00 , &HA0 , &H00 , &H00 , &H2F , &H5C , &H02 , &H00 , &H24 , &H00 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66
Data &H67 , &H68 , &H69 , &H6A , &H6B , &H6C , &H6D , &H6E , &H6F , &H70 , &H71 , &H72 , &H73 , &H74 , &H75 , &H76
Data &H77 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66 , &H67 , &H68 , &H69


Sub Ip_header_checksum
Local Ip_x As Byte
Local Ip_hulp1 As Byte
Local Ip_chksum32 As Long
Local Ip_checksum16 As Word
Local Ip_temp16 As Word
Local Ip_header_length As Byte
'calculate the IP header checksum
T_ip_hdr_cksum = &H00
Hdr_chksum = 0
Ip_header_length = T_ip_vers_len And &H0F
Ip_header_length = 4 * Ip_header_length

'
Print "T_ip_vers_len " ; Hex(t_ip_vers_len)
'
Print "Ip_header_length (hex) " ; Hex(ip_header_length)
'

Hulp1 = &H0E + Ip_header_length
Ip_chksum32 = 0
For Ip_x = 15 To Hulp1 Step 2
     Ip_value16h = Packet(ip_x)
     Print "Ip_value16 high " ; Hex(ip_value16h) ; " ";
     Ip_hulp1 = Ip_x + 1
     Ip_value16l = Packet(ip_hulp1)
     Print "Ip_value16 low " ; Hex(ip_value16l)

     Print "Ip_value16 " ; Hex(ip_value16) ; " ones complement will become ";
     Ip_value16 = Not Ip_value16
     Print Hex(ip_value16)
     Print "and is added to Ip_checksum32"
     Ip_chksum32 = Ip_chksum32 + Ip_value16
Next Ip_x
Print "At the end of this calculation Ip_chksum32 is " ; Hex(ip_chksum32)
Print
Print "Ip_chksum32 is 32 bit long"
Print
print "And we take only the least significant 16 bit of it"
Print
Ip_checksum16 = Ip_chksum32
Print "Ip_checksum16 = " ; Hex(ip_checksum16)
Print
Print "Now we take to most significant 16 bit of it"
Print
Ip_temp16 = Highw(ip_chksum32)
Print "Highw(ip_cksum32) = " ; Hex(ip_temp16 )
Print
Print "We add them together ";
Ip_checksum16 = Ip_checksum16 + Ip_temp16
Print Hex(ip_checksum16)
Print
Print "And then we split them in a Highbyte and a Lowbyte"
T_ip_hdr_cksum0 = High(ip_checksum16)
T_ip_hdr_cksum1 = Low(ip_checksum16)
Print
Print "High byte of Ip-header-checksum " ; Hex(t_ip_hdr_cksum0)
Print "Low byte of Ip-header-checksum " ; Hex(t_ip_hdr_cksum1)
Print
End Sub

Run this program through the simulator and see if all things are right. Remember, this is the ip_header

And the checksum of this header can be found here:

And here is a snapshot from the result in the Simulator

.

ICMP-checksum is next.... Remember, this is the ICMP-part

And this is the checksum

This is the checksum

This is only an example to be run through the Bascom-AVR Simulator.
Don't insert this in your main code!!

'
' ICMP_checksum Example in the Simulator
'
' Routine to calculate an ICMP-checksum
'

' The frame is 74 bytes long
Dim Packet(74) As Byte At &HC0

' temp variables, only used in this example
Dim X As Byte
Dim Y As Byte

Dim I_header_length As Word
Dim I_odd As Byte
Dim I_chksum32 As Long
Dim I_x As Word
Dim I_hulp1 As Word
Dim I_checksum16 As Word
Dim I_temp16 As Word

Dim Hulp6 As Word At &HAF
Dim Hulp6h As Byte At &HB0 Overlay
Dim Hulp6l As Byte At &HAF Overlay

Dim I_value16 As Word At &HB1
Dim I_value16h As Byte At &HB2 Overlay
Dim I_value16l As Byte At &HB1 Overlay

Dim T_ip_pktlen0 As Byte At &HD0 Overlay
Dim T_ip_pktlen1 As Byte At &HD1 Overlay

Dim T_icmp_cksum0 As Byte At &HE4 Overlay
Dim T_icmp_cksum1 As Byte At &HE5 Overlay
Dim T_icmp_cksum As Word At &HE4 Overlay

Declare Sub Icmp_checksum

Restore Frame

'Read the frame of 74 bytes in the array Packet()
For X = 1 To 74
     Read Y
     Packet(x) = Y
     Print "Packet(" ; X ; ") = " ; Hex(y)
Next X

Call Icmp_checksum

End

Frame:
Data &H00 , &H00 , &HC5 , &H0E , &HEC , &H5A , &H00 , &H10 , &H20 , &H30 , &H40 , &H50 , &H08 , &H00 , &H45 , &H00
Data &H00 , &H3C , &H29 , &H2C , &H00 , &H00 , &H80 , &H01 , &H8F , &H40 , &HC0 , &HA8 , &H00 , &H64 , &HC0 , &HA8
Data &H00 , &HA0 , &H00 , &H00 , &H2F , &H5C , &H02 , &H00 , &H24 , &H00 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66
Data &H67 , &H68 , &H69 , &H6A , &H6B , &H6C , &H6D , &H6E , &H6F , &H70 , &H71 , &H72 , &H73 , &H74 , &H75 , &H76
Data &H77 , &H61 , &H62 , &H63 , &H64 , &H65 , &H66 , &H67 , &H68 , &H69


' Routine to calculate a ICMP-checksum
'
Sub Icmp_checksum
'clear the ICMP checksum
Print "You have to clear the ICMP-checksum first"
T_icmp_cksum = &H00
'calculate the ICMP checksum
Print
Print "IP-packetlength minus 20"
I_header_length = T_ip_pktlen1 - 20
'I_header_length = I_header_length - 20
Print
Print "The ICMP-payload combination can be a odd number."
Print "We have to pay attention to that while calculating the checksum."
I_odd = I_header_length Mod 2
'14 for MAC-part
'20 for IP-header
Print
Print "We start at Frame-position 35 to calculate the checksum"
'start on 35
'icmp-packetlengte = ip_pktlen - ip_header
I_chksum32 = 0
'Total packetlength - ip_header - 1
Hulp6h = T_ip_pktlen0
Hulp6l = T_ip_pktlen1
Hulp6 = Hulp6 + 13
Print
Print "Total packetlength is " ; Hulp6
Print
Print "This for/next loop goes from the first position of the ICMP-part to the end"
For I_x = 35 To Hulp6 Step 2
     I_value16h = Packet(i_x)
     Print "I_value16 high " ; Hex(i_value16h) ; " ";
     I_hulp1 = I_x + 1
     I_value16l = Packet(i_hulp1)
     Print "I_value16 low " ; Hex(i_value16l)
     I_value16 = Not I_value16
     Print Hex(i_value16)
     Print "and is added to I_chksum32"
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     Print
     Print "This is an important part."
     Print "If the total count of ICMP-part is ODD, the last byte has to become the high part"
     Print "and we have to put a zero in the low part. Took me some time to get this working."
     I_value16h = Packet(i_x)
     I_value16l = 0
     I_value16 = Not I_value16
     I_chksum32 = I_chksum32 + I_value16
End If
Print "At the end of this calculation I_chksum32 is " ; Hex(i_chksum32)
Print
Print "I_chksum32 is 32 bit long"
Print
Print "And we take only the least significant 16 bit of it"
Print
I_checksum16 = I_chksum32
Print "I_checksum16 = " ; Hex(i_checksum16)
Print
Print "Now we take to most significant 16 bit of it"
Print
I_temp16 = Highw(i_chksum32)
Print "Highw(i_cksum32) = " ; Hex(i_temp16 )
Print
Print "We add them together ";
I_checksum16 = I_checksum16 + I_temp16
Print Hex(i_checksum16)
Print
Print "And then we split them in a Highbyte and a Lowbyte"
T_icmp_cksum0 = High(i_checksum16)
T_icmp_cksum1 = Low(i_checksum16)
Print
Print "High byte of ICMP-checksum " ; Hex(t_icmp_cksum0)
Print "Low byte of ICMP-checksum " ; Hex(t_icmp_cksum1)
Print
End Sub

A snapshot of the Bascom-AVR simulator.

Step 16: UDP
After a big step 15, a small step 16 to get UDP running

First remove the dummy sub udp routine and insert the next UDP-routine:

' Routine to handle UDP-traffic
'
Sub Udp
If T_udp_destport0 = 0 Then
     If T_udp_destport1 = &H07 Then
          'build the IP header
          Call Setipaddrs
          'swap the UDP source and destinations port
          Swap T_udp_srcport0 , T_udp_destport0
          Swap T_udp_srcport1 , T_udp_destport1
          Call Udp_checksum
          Call Write_rtl8019as(cr , &H22)
          Call Echopacket
     End If
End if

This is only a small UDP-routine that echo's the typed characters in the EDTP Electronics Internet Test Panel. The port 5000 isn't used yet, this is the UDP-echo on port 7.

The Udp-checksum routine looks like this:

' Routine to calculate the Udp-checksum
'
Sub Udp_checksum
T_udp_chksum0 = &H00
T_udp_chksum1 = &H00
'checksum TCP header
I_chksum32 = 0
I_value16h = T_ip_srcaddr0
I_value16l = T_ip_srcaddr1
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_srcaddr2
I_value16l = T_ip_srcaddr3
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_destaddr0
I_value16l = T_ip_destaddr1
I_chksum32 = I_chksum32 + I_value16
I_value16h = T_ip_destaddr2
I_value16l = T_ip_destaddr3
I_chksum32 = I_chksum32 + I_value16
'proto
I_chksum32 = I_chksum32 + T_ip_proto
'packet length
I_value16h = T_udp_len0
I_value16l = T_udp_len1
I_chksum32 = I_chksum32 + I_value16
I_odd = T_udp_len1 Mod 2
Result16h = T_udp_len0
Result16l = T_udp_len1
'udp_srcport0 = packet(&h23)
Hulp1 = &H23
Hulp2 = &H23 + Result16
Hulp2 = Hulp2 - 2
For I_x = Hulp1 To Hulp2 Step 2
     I_value16h = Packet(i_x)
     Hulp3 = I_x + 1
     I_value16l = Packet(hulp3)
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     Incr Hulp2
     I_value16h = Packet(hulp2)
     I_value16l = 0
     I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_chksum32
' only 16 lower bits of i_chksum32 is taken...
I_checksum16 = Not I_checksum16
T_udp_chksum0 = High(i_checksum16)
T_udp_chksum1 = Low(i_checksum16)
End Sub

Oke. 8086 bytes used and already ARP, ICMP and UDP running. Ehm... 8086, sounds familiar...... 

And here is the Udp-subroutine if you want to use two different ports to send through.

' Routine to handle UDP-traffic
'
Sub Udp
If T_udp_destport0 = 0 Then
     If T_udp_destport1 = &H07 Then
          'build the IP header
          Call Setipaddrs
          'swap the UDP source and destinations port
          Swap T_udp_srcport0 , T_udp_destport0
          Swap T_udp_srcport1 , T_udp_destport1
          Call Udp_checksum
          Call Write_rtl8019as(cr , &H22)
          Call Echopacket
     End If
     Else
          'From within a VB-program
          If T_udp_destport = 5000 Then
                    Select Case T_udp_data
                         'Case 0 : Call action0
                         'Case 1 : Call action1
                         'Case 2 : Call action2
                   End Select
          End If
End Sub

The top part echo's what has been send, the bottom part reacts on port 5000 and you could take some action.

Step 17: UDP Checksum calculation explained
In a few words, the UDP-checksum is calculated as follows:

Take the IP-number of the source
192.168 (as word)
add
0.100     (as word)

Add the IP-number of the destination
192.168 (as word)
add
0.160     (as word)

Add
T_ip_proto (as low part of a word)

Add
t_udp_len1 t_udp_len0 (as a word)

Add the payload from packet(&H23) to the end of the udp-packet.

Add the highword of this result (a long) and the lowword.

Do a one complement

Store LSByte and MSByte in T_udp_chksum0 and T_udp_chksum1

Step 18: Combining checksum calculation from IP, ICMP and UDP

If you look close to the checksum-calculation you will see that parts of them are the same. With a small additional subroutine we can save up to 300 bytes of flash. We can also skip some variables. The best way to see which variables are not used anymore after code optimalisation is to compile and do a show result.

The General_part_checksum looks like this

Sub General_part_checksum(byval Val1 As Byte , Byval Val2 As Word)
For I_x = Val1 To Val2 Step 2
     I_value16h = Packet(i_x)
     Hulp3 = I_x + 1
     I_value16l = Packet(hulp3)
     I_chksum32 = I_chksum32 + I_value16
Next I_x
If I_odd = 1 Then
     Incr Val2
     I_value16h = Packet(val2)
     I_value16l = 0
     I_chksum32 = I_chksum32 + I_value16
End If
I_checksum16 = Highw(i_chksum32)
I_checksum16 = I_checksum16 + I_chksum32 
' only 16 lower bits of i_chksum32 is taken...
I_checksum16 = Not I_checksum16
Val3 = High(i_checksum16)
Val4 = Low(i_checksum16)
End Sub

 

And here is the total code until now

Step 19: Driving a LCD with an UDP-message

I connected a LCD to both boards using a PCF8574(A).

 

First we have to configure the two lines we are going to use for I2c.


'Configure I2c
Config Scl = Portc.0
Config Sda = Portc.1

Configure the RS and E line for the LCD.

Const Rs = 4
Const E = 7

'Dimension the variables

'LCD variables
Dim Lcd_t As String * 40
Dim Lcd_tempstr As String * 1
Dim Lcd_temp As Byte
Dim Lcd_x As Byte
Dim Lcd_hulp As Byte
Dim Lcd_h As Byte

Put the address of the PCF8574 in a constant

Const PCF8574 = &H40

The LCD is driven in 4-bit mode, by a PCF8574, a remote 8 bit I/O expander for I2C-bus.


              The PCF8574

'Subroutine to initialize the LCD display
Declare Sub Lcd_init
Declare Sub Lcd_char(byval Lcd_c As Byte)
'Subroutine to send a text to the LCD
Declare Sub Lcd_txt
'Subroutine to do a clear/home for the LCD
Declare Sub Lcdhome ' CLS/Home
' goto 0/0 of LCD
Declare Sub Firstline
' goto second line of LCD
Declare Sub Secondline
'Subroutine to show a default message on the LCD
Declare Sub Default_message


' Subroutine to initialize the LCD-display

Sub Lcd_init
Waitms 15
'set 8-bit mode (attention)
'toggle the E -line
I2csend Pcf8574 , &B10000011
I2csend Pcf8574 , &B00000011
Waitms 4
'set 8-bit mode (attention)
I2csend Pcf8574 , &B10000011
I2csend Pcf8574 , &B00000011
Waitms 1
'set 8-bit mode (attention)
I2csend Pcf8574 , &B10000011
I2csend Pcf8574 , &B00000011
Waitms 4
'set 4-bit operation
I2csend Pcf8574 , &B10000010
I2csend Pcf8574 , &B00000010
Waitms 1
'set 8-bit mode (attention)
I2csend Pcf8574 , &B10000010
I2csend Pcf8574 , &B00000010
I2csend Pcf8574 , &B10001000
I2csend Pcf8574 , &B00001000
Waitms 1
I2csend Pcf8574 , &B10000000
I2csend Pcf8574 , &B00000000
'Display off, cursor off, blink off
I2csend Pcf8574 , &B10001000
I2csend Pcf8574 , &B00001000
Waitms 1
I2csend Pcf8574 , &B10000000
I2csend Pcf8574 , &B00000000
'display on, cursor off, blinking off
I2csend Pcf8574 , &B10001100
I2csend Pcf8574 , &B00001100
Waitms 1
I2csend Pcf8574 , &B10000000
I2csend Pcf8574 , &B00000000
'clear display and send cursor to address 0
I2csend Pcf8574 , &B10000001
I2csend Pcf8574 , &B00000001
Waitms 1
End Sub


' Routine to send a character to the LCD-display
'
Sub Lcd_char(lcd_c As Byte)
If Lcd_c = 42 Then '* CLS/HOME
Call Lcdhome
Elseif Lcd_c = 35 Then '# second line
Call Secondline
Else
Lcd_temp = Lcd_c And &HF0
Rotate Lcd_temp , Right , 4
I2csend Pcf8574 , Lcd_temp
Set Lcd_temp.e
Set Lcd_temp.rs
I2csend Pcf8574 , Lcd_temp
Reset Lcd_temp.e
Reset Lcd_temp.rs
I2csend Pcf8574 , Lcd_temp
Lcd_temp = Lcd_c And &H0F
I2csend Pcf8574 , Lcd_temp
Set Lcd_temp.e
Set Lcd_temp.rs
I2csend Pcf8574 , Lcd_temp
Reset Lcd_temp.e
Reset Lcd_temp.rs
I2csend Pcf8574 , Lcd_temp
End If
End Sub


' Routine to send a string to the LCD-display
'
' Fill lcd_t with the text and call lcd_txt
'
Sub Lcd_txt
Lcd_hulp = Len(lcd_t)
For Lcd_x = 1 To Lcd_hulp
Lcd_tempstr = Mid(lcd_t , Lcd_x , 1)
Lcd_temp = Asc(lcd_tempstr)
Call Lcd_char(lcd_temp)
Next Lcd_x
End Sub


Sub Lcdhome
I2csend Pcf8574 , &B10000000
I2csend Pcf8574 , &B00000000
Waitms 2
I2csend Pcf8574 , &B10000001
I2csend Pcf8574 , &B00000001
Waitms 2
End Sub


Sub Secondline
Call Firstline
For Lcd_h = 1 To 40
     I2csend Pcf8574 , &B10000001
     I2csend Pcf8574 , &B00000001
     Waitms 2
     I2csend Pcf8574 , &B10000100
     I2csend Pcf8574 , &B00000100
     Waitms 2
Next Lcd_h
End Sub


Sub Firstline
I2csend Pcf8574 , &B10000000
I2csend Pcf8574 , &B00000000
Waitms 2
I2csend Pcf8574 , &B10000010
I2csend Pcf8574 , &B00000010
Waitms 2
End Sub


' Routine to send a default-message to the LCD
'
Sub Default_message
     Call Lcd_init
     Lcd_t = "Hello, World!"
     Call Lcd_txt
End Sub

Place a Call Default_message just before you go to the main Do Loop

Call Default_message

The LCD-routines are ready now to be used. Before you can use the LCD you have to initialize it with a Call LCD_init, after that, fill LCD_t with the text you want to place on the display and call the LCD_txt routine.
Place a * in your text to clear/home the LCD. Place a # in your text to go to the second line.


Step 20: Driving relays and getting info from the inputs

In this part I describe the switching of the relays that are standard on the AVR Ethernet board and can easily be attached to the Easy Ethernet board.

The relays are behind the 74HCT573 connected to the databus. We need a latch to keep the ones and zeros on the output of this chip.

The input-opto-couplers of the AVR Ethernet board are connected to PORTC, bit 4, 5, 6 and 7. 

' with PORTA relay bit 3 2 1 0 and PORTC opto bit 7 6 5 4

While the power is on this variable will represent the status of the relays.
Dim Relaystatus As Byte

 

Because we will switch the relays with UDP and TCP traffic, we have to overlay the packet-array with the variables for input, output (relays) and while we are at it, reserve some variables for the temperaturesensor.

' the UDP-data part
Dim Portpntr As Byte At &HEA Overlay
Dim Portval As String * 4 At &HEB Overlay
Dim Relaypntr As Byte At &HEF Overlay
Dim Relval As String * 4 At &HF0 Overlay
'Dim Temppntr As Byte At &HF4 Overlay
'Dim Tempval As String * 5 At &HF5 Overlay

We have to declare some subroutines. Will be used in the UDP part and in the TCP part (HTML)

Declare Sub Relayon(byval X As Byte)
Declare Sub Relayoff(byval X As Byte)
Declare Sub Status

Here you find an alias for the latch of the 74HCT573

Latch Alias Portd.3

Reset all relays to the off position

Reset Latch
Databus_ddr = &HFF
Databus = 0                                         ' put zero's on relays-port
Set Latch
Reset Latch
Relaystatus = 0                                  ' put zero's in
the relaystatus byte

We have to drive this UDP-server with a UDP-client. The EDTP Internet Test Panel is such a client. At the end of this small tutorial I will put a small Visual Basic program with a UDP-client that drives the relays and reads the opto-coupled-inputs of the AVR Ethernet board.

UDP-data 1 till 4 is used to switch on the relays.
UDP_data A1 till A4 is used to switch off the relays.

 

'From within a VB-program
If T_udp_destport = Udp_port Then
Select Case T_udp_data
     Case &H01 : Call Relayon(1)
     Case &H02 : Call Relayon(2)
     Case &H03 : Call Relayon(3)
     Case &H04 : Call Relayon(4)
     Case &HA1 : Call Relayoff(5)
     Case &HA2 : Call Relayoff(6)
     Case &HA3 : Call Relayoff(7)
     Case &HA4 : Call Relayoff(8)
End Select

 

This routine updates the relaystatus-byte and jumps to a routine to activate the relay.

' Routine to switch on relay X and update status
'
Sub Relayon(x)
Decr X
Relaystatus.x = 1
Call Status
End Sub

This routine updates the relaystatus-byte and jumps to a routine to activate the relay.

' Routine to switch off relay X and update status
'
Sub Relayoff(x)
X = X - 5
Relaystatus.x = 0
Call Status
End Sub

Here the actual switch is done

' Routine to switch on/off relais and get the status
'
Sub Status
Databus_ddr = &HFF
Databus = Relaystatus
Set Latch
Reset Latch
End Sub

Step 21: Reading and writing of the external 32 Kb EEPROM

'24lc256
'Write address of the 24LC256
Const E_ctlw = &HA0
'Read address of the 24LC256
Const E_ctlr = &HA1

'Address high
Dim E_ah As Byte
'Address low
Dim E_al As Byte
'High and low
Dim E_addr As Word
'Data to be written
Dim E_dat As Byte
Dim Dat As Byte

Declare Sub E_write(byval E_addr As Word , Dat As Byte)
Declare Sub E_read(byval E_addr As Word , E_dat As Byte)
Declare Sub 24lc256_test

Call 24lc256_test

 

' Here some routines you could use when you use the unused parts
' of the VA3TO/EIO-board

' Routine to test the 24LC256
'
Sub 24lc256_test
'Test read and writes
Print "Testing I2C Eeprom"
Dat = 0
For E_addr = &H1000 To &H1010
Call E_write(e_addr , Dat)
Call E_read(e_addr , E_dat)
Print E_dat ; " ";
Incr Dat
Incr Dat
Incr Dat                                                              ' +1 +1 +1
Next E_addr
Print
Print "Here a counter should appear from 0, 3, 6, 9, 12, 15, 18, 21, 24, 27 etc."
Print "End of Test"
End Sub

 

' Routine to write the 24LC256
'
Sub E_write(byval E_addr As Word , Dat As Byte)
E_ah = High(e_addr)
E_al = Low(e_addr)
I2cstart
I2cwbyte E_ctlw
I2cwbyte E_ah
I2cwbyte E_al
I2cwbyte Dat
I2cstop
Waitms 10
End Sub

 

' Routine to read the 24LC256
'
Sub E_read(byval E_addr As Word , E_dat As Byte)
E_ah = High(e_addr)
E_al = Low(e_addr)
I2cstart
I2cwbyte E_ctlw
I2cwbyte E_ah
I2cwbyte E_al
I2cstart
I2cwbyte E_ctlr
I2crbyte E_dat , Nack
I2cstop '
End Sub

 

Step 23: TCP

What do we need to get TCP working:

Incoming flags: (1 byte)

FIN
SYN
RST
PSH
ACK
URG

Outgoing flags: (1 byte)

FIN
SYN
RST
PSH
ACK
URG

Incoming Destination port (1 word)
Outgoing Destination Port (1 word)

Incoming Source port (1 word)
Outgoing Source port (1 word)

Incoming sequence-number (4 byte)
Outgoing sequence-number (4 byte)

Incoming acknowledge-number (4 byte)
Outgoing acknowledge-number (4 byte)

Incoming TCP-data (n-bytes)
Outgoing TCP-data (n-bytes)

Incoming TCP-datalength (1 word)
Outgoing TCP-datalength (1 word)

TCP-chksum (1 word)

I will add some dimensions and show you exact where you can find them.

First we have to declare some routines

Declare Sub Http
Declare Sub Tcp_checksum
Declare Sub Send_tcp_packet
Declare Sub Setup_packet
Declare Sub Assemble_ack
Declare Sub Tcpseqtomyseq

 

Then we have to Dimension some variables.

Dim Client_seqnum As Long At &H8E
Dim Client_seqnum0 As Byte At &H8E Overlay
Dim Client_seqnum1 As Byte At &H8F Overlay
Dim Client_seqnum2 As Byte At &H90 Overlay
Dim Client_seqnum3 As Byte At &H91 Overlay

Dim Incoming_ack As Long At &H92
Dim Incoming_ack0 As Byte At &H92 Overlay
Dim Incoming_ack1 As Byte At &H93 Overlay
Dim Incoming_ack2 As Byte At &H94 Overlay
Dim Incoming_ack3 As Byte At &H95 Overlay

Dim My_seqnum As Long At &HB7
Dim My_seqnum0 As Byte At &HB7 Overlay
Dim My_seqnum1 As Byte At &HB8 Overlay
Dim My_seqnum2 As Byte At &HB9 Overlay
Dim My_seqnum3 As Byte At &HBA Overlay

Dim Tcp_fin As Bit
Dim Tcp_syn As Bit
Dim Tcp_rst As Bit
Dim Tcp_psh As Bit
Dim Tcp_ack As Bit
Dim Tcp_urg As Bit

Dim Tcpdatalen_in As Word
Dim Tcpdatalen_out As Word

Dim Checktemp As Word
Dim Checksumout As Word
Dim Ip_packet_len As Word
Dim Flags As Byte
Dim Msg_temp As String * 55
Dim Y As Word
Dim Tempstring1 As String * 1
Dim Expected_ack As Long

Put some constants in the code

Const Synflag = 0
Const Finflag = 1

And here we have all subroutines to get HTTP working

' TCP
'
Sub Tcp
Local Work As Byte
Work = Tcp_flags
Tcp_fin = Work.0
Tcp_syn = Work.1
Tcp_rst = Work.2
Tcp_psh = Work.3
Tcp_ack = Work.4
Tcp_urg = Work.5

If Tcp_destporth = 0 Then
     Select Case Tcp_destportl
          Case 80 : Call Http
          'more TCP-protocols could be put here
     End Select
End If
End Sub

The routine to handle HTTP-traffic

Sub Http
#if Debug = 1
     Print "HTTP"
     Print
     Print "Destination port low " ; Tcp_destportl
     Print "Destination port high " ; Tcp_destporth
     Print
     Print "TCPdatalen_in " ; Tcpdatalen_in
     Print
     Print "Flags:"
     Print "FIN " ; Tcp_fin
     Print "SYN " ; Tcp_syn
     Print "RST " ; Tcp_rst
     Print "PSH " ; Tcp_psh
     Print "ACK " ; Tcp_ack
     Print "URG " ; Tcp_urg
     Print
     Print "Incoming:"
     Print "Tcp_seqnum3 " ; Hex(tcp_seqnum3)
     Print "Tcp_seqnum2 " ; Hex(tcp_seqnum2)
     Print "Tcp_seqnum1 " ; Hex(tcp_seqnum1)
     Print "Tcp_seqnum0 " ; Hex(tcp_seqnum0)
     Print
     Print "Tcp_acknum3 " ; Hex(tcp_acknum3)
     Print "Tcp_acknum2 " ; Hex(tcp_acknum2)
     Print "Tcp_acknum1 " ; Hex(tcp_acknum1)
     Print "Tcp_acknum0 " ; Hex(tcp_acknum0)
#endif
Local Msg_temp2 As String * 10
Local Tempword3 As Word
Local Z As Word
Local Ztemp As Word
Local Tempstring2 As String * 5
Result16h = T_ip_pktlen0
Result16l = T_ip_pktlen1
'
' calculate IP header length
' MSN is version (IPv4)
' LSN is length
Hulp1 = T_ip_vers_len And &H0F
Hulp1 = Hulp1 * 4
' calculate TCP header length
' MSN is length / 4
Hulp2 = Tcp_hdr And &HF0
Shift Hulp2 , Right , 4
Hulp2 = Hulp2 * 4
Tcpdatalen_in = Result16 - Hulp1
Tcpdatalen_in = Tcpdatalen_in - Hulp2
' If an ACK is received and the destination port address is valid
' and no data is in the packet
If Tcp_ack = 1 Then
     If Tcpdatalen_in = 0 Then
          #if Debug = 1
               Print "Tcp_ack = 1 , Tcpdatalen_in = 0 "
          #endif
          Incoming_ack0 = Tcp_acknum0
          Incoming_ack1 = Tcp_acknum1
          Incoming_ack2 = Tcp_acknum2
          Incoming_ack3 = Tcp_acknum3
          If Flags.synflag = 1 Then
               Reset Flags.synflag
               My_seqnum = Incoming_ack
               Tempword3 = &H37
               Read the HTML-code untul you get a line with endblock
              
Restore Htmlcode
               Do
                    Read Msg_temp
                    Msg_temp2 = Right(msg_temp , 8)
                    If Msg_temp2 = "endblock" Then
                         Exit Do
                    End If
                    For Y = 1 To Len(msg_temp)
                         Tempstring1 = Mid(msg_temp , Y , 1)
                         Packet(tempword3) = Asc(tempstring1)
                         Incr Tempword3
                    Next Y
               Loop
               Tcpdatalen_out = Tempword3 - 55 'minus headerlength
             'expect to get an acknowledgment of the message
               Expected_ack = My_seqnum + Tcpdatalen_out
               ' send the TCP/IP packet
               Call Send_tcp_packet
           End If
     End If
End If

' This code segment processes the incoming SYN from the Tenet client ' and sends back the initial sequence number (ISN) and acknowledges ' the incoming SYN packet

If Tcp_syn = 1 Then
     #if Debug = 1
          Print "Tcp_syn = 1"
     #endif
     Tcpdatalen_in = 1
     Set Flags.synflag
     Call Setup_packet
     Swap Tcp_srcportl , Tcp_destportl
     Swap Tcp_srcporth , Tcp_destporth
     Call Assemble_ack
     Call Tcpseqtomyseq
     Tcp_flags = 0
     Set Tcp_flags.1
     Set Tcp_flags.4
     Call Tcp_checksum
     Call Echopacket
End If

If Tcp_fin = 1 Then
     #if Debug = 1
          Print "Tcp_fin = 1"
     #endif
End If

If Tcp_psh = 1 Then
     #if Debug = 1
          Print "Tcp_psh = 1"
     #endif
     ' walk through the packet until GET / is found
     For Z = 40 To 255
          Ztemp = Z
          Tempstring2 = Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          Incr Ztemp
          Tempstring2 = Tempstring2 + Chr(packet(ztemp))
          If Tempstring2 = "GET /" Then
                Ztemp = Ztemp + 301
                Exit For
          End If
          #if Debug = 1
               Print "/GET gevonden"
          #endif
     Next Z
     'ztemp > 300 when GET / is found
     'ztemp <= 300 when GET / not found
     'when found ztemp has the pointer to it (plus 300)
     If Ztemp > 300 Then
          Ztemp = Ztemp - 300
          If Chr(packet(ztemp)) = "R" Then
          Incr Ztemp
          Hulp1 = Val(packet(ztemp))
          Select Case Hulp1
               Case 1 To 4 : 'Call Relayon(hulp1)
               Case 5 To 8 : 'Call Relayoff(hulp1)
          End Select
     End If
     End If
End If
End Sub

 

Sub Tcp_checksum
Local Whulp1 As Word
Local Whulp2 As Word
Local Whulp3 As Word
Local Tempword2 As Word
Tcp_cksum = 0
I_chksum32 = 0
Tempwordh = T_ip_srcaddr0
Tempwordl = T_ip_srcaddr1
I_chksum32 = Tempword
Tempwordh = T_ip_srcaddr2
Tempwordl = T_ip_srcaddr3
I_chksum32 = I_chksum32 + Tempword
Tempwordh = T_ip_destaddr0
Tempwordl = T_ip_destaddr1
I_chksum32 = I_chksum32 + Tempword
Tempwordh = T_ip_destaddr2
Tempwordl = T_ip_destaddr3
I_chksum32 = I_chksum32 + Tempword
I_chksum32 = I_chksum32 + T_ip_proto
Tempwordh = T_ip_pktlen0
Tempwordl = T_ip_pktlen1
Tempword2 = T_ip_vers_len And &H0F
Tempword2 = Tempword2 * 4
I_chksum32 = I_chksum32 + Tempword
I_chksum32 = I_chksum32 - Tempword2

Whulp2 = Tempword - 20
Whulp2 = Whulp2 + &H23
Whulp2 = Whulp2 - 2
Val1 = &H23
Val2 = Whulp2
I_odd = Val2 - Val1
I_odd = I_odd Mod
2
Call General_part_checksum(val1 , Val2)
Tcp_cksumh = Val3
Tcp_cksuml = Val4
End Sub

 

' Send the TCP-packet, a bit different then echopacket
'
Sub Send_tcp_packet
Ip_packet_len = 40 + Tcpdatalen_out
T_ip_pktlen1 = Low(ip_packet_len)
T_ip_pktlen0 = High(ip_packet_len)
Call Setup_packet
Swap Tcp_srcporth , Tcp_destporth
Swap Tcp_srcportl , Tcp_destportl
Call Assemble_ack
Call Tcpseqtomyseq
Tcp_flags = 0
'fin_out
Set Tcp_flags.0 ' piggyback FIN onto the page date
'ack_out
Set Tcp_flags.4
If Flags.finflag = 1 Then
     'fin_out
     Set Tcp_flags.0
     'clear the fin flag
     Reset Flags.finflag
End If
Call Tcp_checksum
Txlen = Ip_packet_len + 14
If Txlen < 60 Then
     Txlen = 60
End If
Call Write_rtl8019as(cr , &H22)
Call Write_rtl8019as(tpsr , Txstart)
Call Write_rtl8019as(rsar0 , &H00)
Call Write_rtl8019as(rsar1 , &H40)
Call Write_rtl8019as(isr , &HFF)
Hulp1 = Low(txlen)
Call Write_rtl8019as(rbcr0 , Hulp1)
Hulp1 = High(txlen)
Call Write_rtl8019as(rbcr1 , Hulp1)
Call Write_rtl8019as(cr , &H12)
For I = 1 To Txlen
     Call Write_rtl8019as(rdmaport , Packet(i))
Next
'make sure the DMA operation has succesfully completed
Byte_read = 0
Do
     Hulp1 = Byte_read And Rdc
     Call Read_rtl8019as(isr)
     Loop Until Hulp1 = 0
     Hulp1 = Low(txlen)
     Call Write_rtl8019as(tbcr0 , Hulp1)
Hulp1 = High(txlen)
Call Write_rtl8019as(tbcr1 , Hulp1)
Call Write_rtl8019as(cr , &H24)
End Sub

 

Sub Setup_packet
'Move IP source address to destination address
T_ip_destaddr = T_ip_srcaddr
'Make ethernet module IP address source address
T_ip_srcaddr = My_ip
'Move hardware source address to destination address
Call Packetshape
Call Ip_header_checksum
End Sub

Add the tcpdatalength in to the sequencenumber to get the acknowledgenumber

Sub Assemble_ack
Client_seqnum0 = Tcp_seqnum0
Client_seqnum1 = Tcp_seqnum1
Client_seqnum2 = Tcp_seqnum2
Client_seqnum3 = Tcp_seqnum3
Client_seqnum = Client_seqnum + Tcpdatalen_in
Tcp_acknum0 = Client_seqnum0
Tcp_acknum1 = Client_seqnum1
Tcp_acknum2 = Client_seqnum2
Tcp_acknum3 = Client_seqnum3
End Sub

Sub Tcpseqtomyseq
Tcp_seqnum0 = My_seqnum0
Tcp_seqnum1 = My_seqnum1
Tcp_seqnum2 = My_seqnum2
Tcp_seqnum3 = My_seqnum3
End Sub

And a small detail I nearly forgot, the HTML-code as data at the end of your program:

' htmlcode
'
Htmlcode:
Data "HTTP/1.0 200" , &H0D , &H0A , &H0D , &H0A
Data "<title>Ethernet I/O board</title></head>"
Data "<body style=" , &H22 , "font-family: Verdana; font-size: 20pt; "
Data "font-weight: Bold" , &H22 , " link=" , &H22 , "#000000" , &H22 , " vlink=" , &H22
Data "#000000" , &H22 , " alink=" , &H22 , "#000000" , &H22 , ">"
Data "TCP TEST<br>"
Data "<br><br>"
Data "<img border=" , &H22 , "0" , &H22
Data " src=" , &H22 , "https://benshobbycorner.nl/hobbycorner/images/bens.gif" , &H22 , ">"
Data "<br><br>Small tutorial. RTL8019as and Atmega" Data "endblock"

The extra carriage return/line feed after HTTP/1.0 200 is important. Else the browser won't recognize this as a HTML-page.

 

This is what you get.... A single HTML-page. The picture is from an external source.

Here the code until now

If you compile the code and leave DEBUG 1, the size will be 14518 bytes. If you put a 0 in DEBUG, the size will be 13224 bytes.

Step 24: TCP checksum

In a few words the checksum-calculation of TCP is done as follows:

Add this
T_ip_srcaddr0 and T_ip_srcaddr1 as word
T_ip_srcaddr2 and T_ip_srcaddr3 as word
T_ip_destaddr0 and T_ip_destaddr1 as word
T_ip_destaddr2 and T_ip_destaddr3 as word
T_ip_proto as LSbyte of a word
T_ip_pktlen0 and T_ip_pktlen1 as word
The least significant nibble times 4 as LSByte of a word
And then we add the complete payload of the TCP-packet
When the total number of bytes is odd we add the last byte as a MSByte of a word

Because we have done this before on IP and ICMP checksum we use the General_part_checksum routine.
 

Step 25: Gluing all parts together.....

The basic stuff, ARP, PING, UDP and TCP is working. Now we have to deal with the relays and the inputs. We have to 'run' them with both UDP and TCP. With UDP this is done by sending a triggerword to the board, and getting a response back. 

First we are going to work on the UDP-part and combine it with displaying messages on the LCD-display:

Const Triggerstring = "SEND INFORMATION NOW"
Const Lcdpresent = 1                           'put a 0 here when you don't have a LCD attached

If you count the characters you will see that this is exactly 20.

We are also dimensioning
Dim Triggerstr as String * 20
Dim Tempstring as string * 10

And in the UDP-subroutine we add this big part:

' Routine to handle UDP-traffic
'
Sub Udp
Triggerstr = ""
Hulp1 = &HEA
For Hulp2 = 1 To 20
     Hulp3 = Peek(hulp1)
     Triggerstr = Triggerstr + Chr(hulp3)
     Incr Hulp1
Next Hulp2
'From within a VB-program
Print "T_udp_destport = " ; T_udp_destport
If T_udp_destport = &H8813 Then
     Select Case T_udp_data
          Case &H01 : Call Relayon(1)
          Case &H02 : Call Relayon(2)
          Case &H03 : Call Relayon(3)
          Case &H04 : Call Relayon(4)
          Case &HA1 : Call Relayoff(5)
          Case &HA2 : Call Relayoff(6)
          Case &HA3 : Call Relayoff(7)
          Case &HA4 : Call Relayoff(8)
     End Select
     If Triggerstr = Triggerstring Then
          If Lcdpresent = 1 Then
               Lcd_t = "*UDP request#Sending: "
               Call Lcd_txt
          End If
          Portpntr = Asc( "P")
          Tempstring = Bin(inputport1)
          Portval = Left(tempstring , 4)
          If Lcdpresent = 1 Then
               Lcd_t = "P" + Portval
          End If
          Relaypntr = Asc( "R")
          Tempstring = Bin(relaystatus)
          Relval = Right(tempstring , 4)
          If Lcdpresent = 1 Then
               Lcd_t = Lcd_t + "R"
               Lcd_t = Lcd_t + Relval
               Call Lcd_txt
               Idletime = 0
          End If
          'Build The Ip Header
          Call Setipaddrs
          'swap the UDP source and destinations port
          Swap T_udp_srcport0 , T_udp_destport0
         
Swap T_udp_srcport1 , T_udp_destport1
          Call Udp_checksum
          Call Write_rtl8019as(cr , &H22)
          Call Echopacket
     End If
End If
End Sub

This is the UDP-client you can use when you want to switch the relays and read the inputs.

And here is the code. For Visual Basic

Private Sub Form_Load()
On Error Resume Next
Timer1.Interval = 5000
txtip.Text = "192.168.0.100"
txtport.Text = "5000"
With udp_PC
.RemoteHost = txtip.Text
.RemotePort = txtport.Text
.Bind 5002
End With
End Sub

Private Sub relay1off_Click()
On Error Resume Next
udp_PC.SendData Chr(&HA1)
End Sub

Private Sub relay1on_Click()
On Error Resume Next
udp_PC.SendData Chr(&H1)
End Sub

Private Sub relay2off_Click()
On Error Resume Next
udp_PC.SendData Chr(&HA2)
End Sub

Private Sub relay2on_Click()
On Error Resume Next
udp_PC.SendData Chr(&H2)
End Sub

Private Sub relay3off_Click()
On Error Resume Next
udp_PC.SendData Chr(&HA3)
End Sub

Private Sub relay3on_Click()
On Error Resume Next
udp_PC.SendData Chr(&H3)
End Sub

Private Sub relay4off_Click()
On Error Resume Next
udp_PC.SendData Chr(&HA4)
End Sub

Private Sub relay4on_Click()
On Error Resume Next
udp_PC.SendData Chr(&H4)
End Sub

Private Sub txtip_Change()
On Error Resume Next
udp_PC.RemoteHost = txtip.Text
End Sub

Private Sub txtport_Change()
On Error Resume Next
udp_PC.RemotePort = Val(txtport.Text)
End Sub

Private Sub udp_pc_dataarrival(ByVal bytestotal As Long)
On Error Resume Next
udp_PC.RemoteHost = txtip.Text
If udp_PC.RemotePort = Val(txtport.Text) Then
Dim strdata As String
udp_PC.GetData strdata
If Left$(strdata, 1) = "P" Then
retourUDP.Text = strdata
If Mid$(strdata, 10, 1) = 1 Then
Picture2(0).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(0).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 9, 1) = 1 Then
Picture2(1).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(1).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 8, 1) = 1 Then
Picture2(2).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(2).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 7, 1) = 1 Then
Picture2(3).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(3).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 5, 1) = 1 Then
Picture2(4).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(4).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 4, 1) = 1 Then
Picture2(5).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(5).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 3, 1) = 1 Then
Picture2(6).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(6).Picture = LoadPicture("RED.BMP")
End If
If Mid$(strdata, 2, 1) = 1 Then
Picture2(7).Picture = LoadPicture("GREEN.BMP")
Else
Picture2(7).Picture = LoadPicture("RED.BMP")
End If
End If
End If
End Sub

Private Sub Timer1_Timer()
On Error Resume Next
udp_PC.RemoteHost = txtip.Text
udp_PC.SendData "SEND INFORMATION NOW"
udp_PC.RemotePort = Val(txtport.Text)
End Sub

Private Sub Command2_Click()
Unload Me
End Sub

Here is the complete code for the Atmega32 until now. UDP completely working. We only have to get a HTML-page with some pictures to switch on/off relays and read the inputs.

Step 26: Sample of an application: A HTML-page to switch relays and read inputs

And here it is. A complete page, fits in a single packet(1500) array. There are a few strange commands in it and I will explain them. The strange commands are highlighted.

' htmlcode
'
Htmlcode:
Data "HTTP/1.0 200" , &H0D , &H0A , &H0D , &H0A
Data "<html><head><meta http-equiv=" , &H22 , "Refresh" , &H22 , " CONTENT=" , &H22 , "9;URL="
Data "get-ipnr"
Data &H22 , ">"
Data "<meta http-equiv=" , &H22 , "Pragma" , &H22 , " Content=" , &H22 , "no cache" , &H22 , ">"
Data "<title>Ethernet I/O board</title></head>"
Data "<body style=" , &H22 , "font-family: Verdana; font-size: 8pt; " Data "font-weight: Bold" , &H22 , " link=" , &H22 , "#000000" , &H22 , " vlink=" , &H22
Data "#000000" , &H22 , " alink=" , &H22 , "#000000" , &H22 , ">"
Data "<table border=" , &H22 , "10" , &H22 , " width=" , &H22 , "600" , &H22
Data " bordercolor=" , &H22 , "green" , &H22 , " height=" , &H22 , "225" , &H22
Data " cellspacing=" , &H22 , "12" , &H22 , "><tr>"
Data "get-info"
Data "<tr><tr>"
Data "<td><b>Input 1</td>"
Data "<td><b>Input 2</td>"
Data "<td><b>Input 3</td>"
Data "<td><b>Input 4</td>"
Data "<td><b>Relay 1</td>"
Data "<td><b>Relay 2</td>"
Data "<td><b>Relay 3</td>"
Data "<td><b>Relay 4</td>"
Data "</tr><tr><td height=" , &H22 , "100" , &H22
Data " colspan=" , &H22 , "4" , &H22 , " rowspan=" , &H22 , "2" , &H22 , " bordercolor=" , &H22
Data "white" , &H22 , " valign=" , &H22 , "bottom" , &H22 , ">"
Data "<p><b><font size=" , &H22 , "4" , &H22
Data " color=" , &H22 , "green" , &H22 , ">Ethernet I/O board"
Data "</font></b></p>" Data "</td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R1" , &H22 , "><b>on</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R2" , &H22 , "><b>on</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R3" , &H22 , "><b>on</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R4" , &H22 , "><b>on</a></td>"
Data "</tr><tr><td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R5" , &H22 , "><b>off</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R6" , &H22 , "><b>off</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R7" , &H22 , "><b>off</a></td>"
Data "<td height=" , &H22 , "50"
Data &H22 , "><a href=" , &H22 , "/R8" , &H22 , "><b>off</a></td>"
Data "</tr></table></body></html>" , &H0D , &H0A
Data "endblock"


And here the explanation:

<meta http-equiv="Refresh","CONTENT=9;URL= followed by "get-ipnr"

What is a META-TAG and what does this META-TAG do?

Meta Tag Overview

What are meta tags? They are information inserted into the "head" area of your web pages. Other than the title tag information in the head area of your web pages is not seen by those viewing your pages in browsers. Instead, meta information in this area is used to communicate information that a human visitor may not be concerned with. Meta tags, for example, can tell a browser what "character set" to use or whether a web page has self-rated itself in terms of adult content


Hope this makes any sense.....

With this META-TAG we instruct the browser to refresh automatic every 9 seconds. URL= thats perhaps a difficult part, but I try to explain it. Perhaps you gave your Easy Ethernet or AVR Ethernet I/O board a IP-address from your own private network. When you use this board at home at your private network, you have to refresh on it's private network IP-address. At my home this is 192.168.0.100 But what if you are using portforwarding or NAT (Network Address Translating). With portforwarding you use the IP-number you get from your ISP (Internet Service Provider) and put there a PORT-number behind. Your router at home redirects the information depending on the PORT-number to a device on your own network. So for example, every request on port 80 to your IP-number, is redirected to 192.168.0.100 (your Easy Ethernet or AVR Ethernet I/O board).  And with NAT the same, from the outside world, you don't want to refresh on your inside IP-number, but want to refresh on your IP-number known on the Internet. The Get-IPnr routine you will see later, will read the information you gave while configuring the Easy Ethernet or AVR Ethernet I/O board. 


Next one:

<meta http-equiv="Pragma","Content="no cache">

This Meta-tag instructs the browser not the cache the page. So every time the page is fetched, the complete data is loaded. If we wouldn't do that we wouldn't see any change in input-status.


With this command we instruct the microcontroller to get the status of relays and inputs. Imagine, you are switching a relay on, the command is send to the microcontroller, it takes the refresh-time as mentioned above to get the last settings of relays and and to get it displayed on the browser.

Data "get-info"


And here the last one

Data "endblock"

This is a identifier for the READ instruction that this is the last line. Perhaps you need other Read/Data instructions, you can put them after this line, with its own label.

Now replace part of the HTTP-subroutine with this part

If Tcp_ack = 1 Then
     If Tcpdatalen_in = 0 Then
          incoming_ack0 = Tcp_acknum0
          incoming_ack1 = Tcp_acknum1
          incoming_ack2 = Tcp_acknum2
          incoming_ack3 = Tcp_acknum3
          If Flags.synflag = 1 Then
               Reset Flags.synflag
               My_seqnum = Incoming_ack
               Tempword3 = &H37
               If Lcdpresent = 1 Then
                    Lcd_t = "*HTTP request"
                    Call Lcd_txt
                    Call Secondline
                    Lcd_t = "Sending: "
                    Call Lcd_txt
                    Tempstring = Bin(inputport1)
                    Lcd_t = "P" + Left(tempstring , 4)
                    Call Lcd_txt
                    Tempstring = Bin(relaystatus)
                    Lcd_t = "R" + Right(tempstring , 4)
                    Call Lcd_txt
                    Idletime = 0
               End If
               Restore Htmlcode
               Do
               Read Msg_temp
               Msg_temp2 = Right(msg_temp , 8)
               If Msg_temp2 = "endblock" Then
                    Exit Do
               End If
               If Msg_temp2 = "get-ipnr" Then
                    Readeeprom Hulp1 , 13
                    If Hulp1 = &HFF Then
                    Msg_temp = "https://" + Str(myip(1))
                    Msg_temp = Msg_temp + "." + Str(myip(2))
                    Msg_temp = Msg_temp + "." + Str(myip(3))
                    Msg_temp = Msg_temp + "." + Str(myip(4))
               Else
                    Msg_temp = ""
                    For Y = 1 To Hulp1
                          Hulp3 = Y + 13
                         Readeeprom Hulp2 , Hulp3
                          Msg_temp = Msg_temp + Chr(hulp2)
                     Next Y
               End If
          End If
          If Msg_temp2 = "get-info" Then
               If Inputport1.4 = 0 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Inputport1.5 = 0 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Inputport1.6 = 0 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Inputport1.7 = 0 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Relaystatus.0 = 1 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Relaystatus.1 = 1 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
               If Relaystatus.2 = 1 Then
                    Msg_temp = "<td bgcolor=green><b>on</td>"
               Else
                    Msg_temp = "<td bgcolor=red><b>off</td>"
               End If
               For Y = 1 To Len(msg_temp)
                    Tempstring1 = Mid(msg_temp , Y , 1)
                    Packet(tempword3) = Asc(tempstring1)
                    Incr Tempword3
               Next Y
                   If Relaystatus.3 = 1 Then
        
                  Msg_temp = "<td bgcolor=green><b>on</td>"
              
      Else
                   
       Msg_temp = "<td bgcolor=red><b>off</td>"
               
     End If
               
     For Y = 1 To Len(msg_temp)
                      
        Tempstring1 = Mid(msg_temp , Y , 1)
                         
     Packet(tempword3) = Asc(tempstring1)
                         
     Incr Tempword3
                     Next Y
               Else
                    For Y = 1 To Len(msg_temp)
                         Tempstring1 = Mid(msg_temp , Y , 1)
                         Packet(tempword3) = Asc(tempstring1)
                         Incr Tempword3
                         Next Y
                    End If
               Loop
               Tcpdatalen_out = Tempword3 - 55 'minus headerlength
               ' expect to get an acknowledgment of the message
               Expected_ack = My_seqnum + Tcpdatalen_out
               ' send the TCP/IP packet
               Call Send_tcp_packet
          End If
     End If
End If

Somewhere in this code you will find a readeeprom-command. During the configuration of the board we write several things to the eeprom of the Atmega32. How this is done is explained in the next chapter.

And here the result.

Here the code up to this point

Step 27: Configuration screen

Up to this point we have used...17306 bytes with Debug off, and 18352 with Debug on. With the next part we will use a lot of space. It is a configuration-screen to let the user change IP-number, port-number, presence of a LCD, address of the PCF8574(A) that is used to drive the LCD, the refresh-address for HTML and so on. If you are running your own boards and have a Bascom-AVR you could consider to delete the configuration-subroutine and fill in the EEPROM directly with the right information. The configuration-part takes about 7000 bytes!!! 

We have to declare some more subroutines, dimension some variables and create some subroutines. 

Declare sub Print_mac
Declare sub Read_mac
Declare sub Print_ip
Declare sub read_ip

Dim Yn as string * 2
Dim Mymacs(6) as string * 2
Dim Hulp5 as word at &h7E
Dim Hulp5h as byte at &h7F overlay
Dim Hulp5l as byte at &h7E overlay

Dim Pcf8574 as byte

We will be storing configuration items in the EEPROM.
Here is a table:

' In the EEPROM:
'
' 00 = .0 - 0 if LCD present (2 x 20)
' 00 = .1
' 00 = .2
' 00 = .3
' 00 = .4
' 00 = .5
' 00 = .6
' 00 = .7
' 01 = IP-number msb
' 02 = IP-number
' 03 = IP-number
' 04 = IP-number lsb
' 05 = MAC-address
' 06 = MAC-address
' 07 = MAC-address
' 08 = MAC-address
' 09 = MAC-address
' 10 = MAC-address
' 11 = Portnumber
' 12 = Portnumber
' 13 = Length URL-refresh string
' 14 = start of URL-refresh string
' .... upto end of URL-refresh string
'100 = address of PCF8574 or PCF8574A

Here the configuration subroutine. Before you do an Init_Rtl8019as you could do a

Call Configure

 

' Routine to get a configuration-screen on the RS232 port
'
Sub Configure
Local Adr As Word
Local Row As Byte
Local Ipnr As String * 15
Local Dot1 As Byte
Local Dot2 As Byte
Local Dot3 As Byte
Local Dot4 As Byte
Local Dot5 As Byte
Local Iptemp As String * 1
Local Ippart As String * 3
Local Macadr As String * 20
Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0
Print "Atmega32 Loader Tutotial"
Print
Print "Source written by Ben Zijlstra april 2005"
Print
Print "Present configuration:"
Print
Print "Network:"
Print
Print "IP-number: ";
Call Read_ip
Call Print_ip
Call Read_mac
Print
Print "MAC-address: ";
Print Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ;
Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
Print
Print
Print "Press 'y' to get into configuration mode"
Test:
Incr Hulp3
If Hulp3 = 65000 Then
     Incr Hulp1
     Hulp3 = 0
End If
If Hulp1 = 25 Then
     Goto Noresponse
End If
sbis USR,7
rjmp test
Configuremode:
If Lcdpresent = 1 Then
     Call Lcd_init
     Lcd_t = "Configure-mode"
     Call Lcd_txt
End If
Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0
Print
Print "Configure mode:"
Print
Print "Network:"
Print
Print "IP-number: ";
Call Print_ip
Input "Change IP-number (y/n) " , Yn
If Yn = "y" Then
     Print
     Print "Input IP-numbers as decimals, seperated by a dot (like 192.168.1.106)"
     Print
     Input Ipnr
     Hulp1 = Len(ipnr)
     For Hulp2 = 1 To Hulp1
          Incr Dot1
          Iptemp = Mid(ipnr , Hulp2 , 1)
          If Iptemp = "." Then
               Exit For
          End If
     Next
     Dot2 = Dot1
     Incr Dot1
     For Hulp2 = Dot1 To Hulp1
          Incr Dot2
          Iptemp = Mid(ipnr , Hulp2 , 1)
          If Iptemp = "." Then
               Exit For
          End If
     Next
     Dot3 = Dot2
     Incr Dot2
     For Hulp2 = Dot2 To Hulp1
          Incr Dot3
          Iptemp = Mid(ipnr , Hulp2 , 1)
          If Iptemp = "." Then
               Exit For
          End If
     Next
     Incr Dot3
     Ippart = Mid(ipnr , 1 , Dot1)
     Myip(1) = Val(ippart)
     Hulp1 = Dot2 - Dot1
     Ippart = Mid(ipnr , Dot1 , Hulp1)
     Myip(2) = Val(ippart)
     Hulp1 = Dot3 - Dot2
     Ippart = Mid(ipnr , Dot2 , Hulp1)
     Myip(3) = Val(ippart)
     Ippart = Mid(ipnr , Dot3)
     Myip(4) = Val(ippart)
     Print
     Print "New IP-number: ";
     Call Print_ip
     Print
     Input "Accept IP-number (y/n) " , Yn
     If Yn = "y" Then
          Writeeeprom Myip(1) , 1 : Writeeeprom Myip(2) , 2
          Writeeeprom Myip(3) , 3 : Writeeeprom Myip(4) , 4
     End If
     Print
     Print "IP-number: ";
     Call Read_ip
     Call Print_ip
End If
Print
Input "Change MAC-address (y/n) " , Yn
If Yn = "y" Then
     Mac:
     Print
     Print "Input MAC-address in hexadecimals, seperated by a - (like 00-34-35-36-37-48)"
     Print Input Macadr
     If Len(macadr) <> 17 Then Goto Mac
          Mymacs(1) = Mid(macadr , 1 , 2)
          Mymacs(2) = Mid(macadr , 4 , 2)
          Mymacs(3) = Mid(macadr , 7 , 2)
          Mymacs(4) = Mid(macadr , 10 , 2)
          Mymacs(5) = Mid(macadr , 13 , 2)
          Mymacs(6) = Mid(macadr , 16 , 2)
          Mymac(1) = Hexval(mymacs(1))
          Mymac(2) = Hexval(mymacs(2))
          Mymac(3) = Hexval(mymacs(3))
          Mymac(4) = Hexval(mymacs(4))
          Mymac(5) = Hexval(mymacs(5))
          Mymac(6) = Hexval(mymacs(6))
          Print
          Print "New MAC-address: ";
          Call Print_mac
          Print
          Input "Accept MAC-address (y/n) " , Yn
          If Yn = "y" Then
               Writeeeprom Mymac(1) , 5 : Writeeeprom Mymac(2) , 6
               Writeeeprom Mymac(3) , 7 : Writeeeprom Mymac(4) , 8
               Writeeeprom Mymac(5) , 9 : Writeeeprom Mymac(6) , 10
          End If
          Print
          Print "MAC-address: ";
          Call Read_mac
          Call Print_mac
     End If
     Print
Input "Would you like to use it's own IP-number as refresh-URL (y/n) " , Yn If Yn = "y" Then
     Hulp1 = 255
     Writeeeprom Hulp1 , 13
Else
     Print
     Input "Would you like to have another refresh-URL (y/n) " , Yn
     If Yn = "y" Then
               Input "Type the complete refresh URL " , Msg_temp
               Print
               Print "Refresh URL " ; Msg_temp
               Print
               Input "Accept Refresh-URL (y/n) " , Yn
               If Yn = "y" Then
                    Hulp2 = Len(msg_temp)
                    Writeeeprom Hulp2 , 13 ' write length
of Refresh URL
                    For Y = 1 To Hulp2
                         Hulp1 = Y + 13
                         Tempstring1 = Mid(msg_temp , Y , 1)
                         Hulp2 = Asc(tempstring1)
                         Writeeeprom Hulp2 , Hulp1
                    Next Y
               End If
          End If
     End If
     Print
     Hulp5 = 5000
     Writeeeprom Hulp5h , 12 ' &h1388 = 5000 dec = 19 high, 136 low.
     Writeeeprom Hulp5l , 11
     Input "Would you like to change to UDP Portnumber. It's default is 5000 (y/n) " , Yn
     If Yn = "y" Then
          Print
          Input "Type the portnumber < 65536 as decimal " , Hulp4
          Print
          Print "Portnumber UDP " ; Hulp5
          Print
          Input "Accept Portnumber UDP (y/n) " , Yn
          If Yn = "y" Then
               Writeeeprom Hulp5h , 12
               Writeeeprom Hulp5l , 11
          End If
     End If
     Print
     Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0
     Print
     Input "Would you like to see all devices on the I2c-bus (y/n) " , Yn If Yn = "y" Then
     Print "I2c-device locator 1 = no device 0 = device"
     Print
     Print " 0 2 4 6 8 A C E"
     Print " 0000 ";
     For Adr = 0 To 254 Step 2
          Row = Adr Mod 16 ' addresses
          If Row = 0 And Adr > 0 Then
               Print ' end of line?
               Print " " ; Hex(adr) ; " ";
          End If
          I2cstart ' generate start
          I2cwbyte Adr
          Print " " ; Err ; " "; ' 1 no device, 0
device
          I2cstop
     Next Adr
     Print
End If
Print
Input "Any key to continue...." , Yn
Print
Input "Would you like to use a LCD-display? " , Yn
Readeeprom Hulp1 , 0
If Yn = "y" Then
     Hulp1.0 = 0
     Writeeeprom Hulp1 , 0
     Print
     Print "There are two kinds of PCF8574 I2c-I/O-ports"
     Print
     Print "The PCF8574 has base address 40"
     Print "The PCF8574A has base address 70"
     Print
     Inputhex "At what address is the I2c-PCF8574x located : " , Pcf8574
     Writeeeprom Pcf8574 , 100
     Print
     Call Lcd_init
     Lcd_t = "LCD Testmessage"
     Call Lcd_txt
Else
     Hulp1.0 = 1
     Writeeeprom Hulp1 , 0
End If
Noresponse:
Print Chr(&H1b) ; "[2J"; ' ANSI goto 0/0
Print "Summary:"
Print
Print "Network:"
Print
Print "IP-number : " ; Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ;
Myip(4)
Print
Print "Mac-address: " ; Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ;
Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ;
Hex(mymac(6))
Print
Print "URL-refresh: ";
Readeeprom Hulp1 , 13
If Hulp1 = &HFF Then
    
Print "http://" ; Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ;  Myip(4)
     Else
     Msg_temp = ""
     For Y = 1 To Hulp1
          Hulp3 = Y + 13
          Readeeprom Hulp2 , Hulp3
          Msg_temp = Msg_temp + Chr(hulp2)
     Next Y
     Print Msg_temp
End If
Print
Print "UDP-port : ";
Readeeprom Hulp4h , 12
Readeeprom Hulp4l , 11
Print Hulp4
Print
If Lcdpresent = 0 Then
     Print "LCD-display: not used"
Else
     Print "LCD-display: used"
     Print
     Readeeprom Pcf8574 , 100
     Print "Address hex: " ; Hex(pcf8574)
End If
Print
Print
Print "Running..."
End Sub

' Routine to read the IP-numbers from the EEPROM
'
Sub Read_ip
Readeeprom Myip(1) , 1 : Readeeprom Myip(2) , 2
Readeeprom Myip(3) , 3 : Readeeprom Myip(4) , 4
End Sub

' Routine to print the IP-number
'
Sub Print_ip
Print Myip(1) ; "." ; Myip(2) ; "." ; Myip(3) ; "." ; Myip(4)
End Sub

' Routine to read the MAC-address from the EEPROM
'
Sub Read_mac
Readeeprom Mymac(1) , 5 : Readeeprom Mymac(2) , 6
Readeeprom Mymac(3) , 7 : Readeeprom Mymac(4) , 8
Readeeprom Mymac(5) , 9 : Readeeprom Mymac(6) , 10
End Sub

' Routine to print the MAC-address

'

Sub Print_mac
Print Hex(mymac(1)) ; "-" ; Hex(mymac(2)) ; "-" ; Hex(mymac(3)) ; "-" ; Hex(mymac(4)) ; "-" ; Hex(mymac(5)) ; "-" ; Hex(mymac(6))
End Sub

During configuration you get an question to see all I2c-devices on the bus. This is the screen you will see on the AVR Ethernet I/O board. I have a LCD connected on a PCF8574 on address &h40. There is another PCF8574 on my LCD board on address &h42. And the EEPROM on the AVR Ethernet I/O can be found on &hA0.

When you finished the configuration you will get an overview.
On my board it looks like this:

 

On several parts in the main program I have used static information. Now the configuration screen is running the total program looks like this:

Tutorial_complete.txt

Bytes used: with Debug on 24450, with No Debug 23478.

Step 28: Sample of an application: LM76 temperature sensor....

The code is complete but we have still some flash to add some nice features....


A real piece of art.... A LM76 for the AVR Ethernet I/O-board.

Here the DIM, Declare and subroutine to read a LM76-temperature sensor. Place them at the right position in the program.

'LM76
Dim Lm76temp As Word At &H070C
Dim Lm76high As Byte At &H070D Overlay
Dim Lm76low As Byte At &H070C Overlay
Dim Lm76temp2 As Long
Dim Lm76buf(2) As Byte

Declare Sub Lm76_test

Call Lm76_test

' Routine to test the LM76
'
Sub Lm76_test
I2csend &H90 , 0
I2creceive &H90 , Lm76buf(1) , 0 , 2
Print Bin(lm76buf(1))
Print Bin(lm76buf(2))
Lm76high = Lm76buf(1)
Lm76low = Lm76buf(2)
Shift Lm76temp , Right , 3
Lm76temp2 = Lm76temp
Lm76temp2 = Lm76temp2 * 625
Lm76temp2 = Lm76temp2 / 100
Print "Temperature = " ; Lm76temp2
End Sub

This piece of software will be integrated in the UDP and TCP part of the program.

I wonder how he did it....?

Step 29: Sample of an application: Simple Time Protocol Client

What if the AVR Ethernet board or the Easy Ethernet board would show date and time when it is waiting for a command. And what if we would use the SNTP-protocol. Simple Network Time Protocol. It would synchronize it's time with a SNTP-server on the Internet.

From this:

to this

to this



SNTP is an UDP-protocol. But instead of receiving UDP we should send UDP. We have to add some code.

'for NTP-routine
Dim S(4) As Byte
Dim L1 As Long At S Overlay ' Overlay a long variable to receive-string
' with overlay you need no transfer from the byte-array to a long-variable
Dim L2 As Long

'network time protocol
Dim Ntp As Bit

Set Ntp

Dim T_udp_data1 As Byte At &HEB Overlay
Dim T_udp_data2 As Byte At &HEC Overlay
Dim T_udp_data3 As Byte At &HED Overlay
Dim T_udp_data4 As Byte At &HEE Overlay
Dim T_udp_data5 As Byte At &HEF Overlay
Dim T_udp_data6 As Byte At &HF0 Overlay
Dim T_udp_data7 As Byte At &HF1 Overlay
Dim T_udp_data8 As Byte At &HF2 Overlay
Dim T_udp_data9 As Byte At &HF3 Overlay
Dim T_udp_data10 As Byte At &HF4 Overlay
Dim T_udp_data11 As Byte At &HF5 Overlay
Dim T_udp_data12 As Byte At &HF6 Overlay
Dim T_udp_data13 As Byte At &HF7 Overlay
Dim T_udp_data14 As Byte At &HF8 Overlay
Dim T_udp_data15 As Byte At &HF9 Overlay
Dim T_udp_data16 As Byte At &HFA Overlay
Dim T_udp_data17 As Byte At &HFB Overlay
Dim T_udp_data18 As Byte At &HFC Overlay
Dim T_udp_data19 As Byte At &HFD Overlay

Declare Sub Ntp_get
Declare Sub Ntp

$lib "datetime.lbx"
'
Dim W1 As Word
W1 = W1 / W1 ' force compiler to implement _div16

' Routine to get the NetWork Time from a time-server
'
Sub Ntp_get
Disable Int0
T_enetpacketlenl = 66
T_enetpacketlenh = 0
'MAC-header
'Destination hardware address ' You have to put your router-mac-address here
T_enetpacketdest0 = &H00 ' 00-0c-41-ae-7a-dc MAC-address of my router
T_enetpacketdest1 = &H0C
T_enetpacketdest2 = &H41
T_enetpacketdest3 = &HAE
T_enetpacketdest4 = &H7A
T_enetpacketdest5 = &HDC
' source (own source)
T_enetpacketsrc0 = Mymac(1)
T_enetpacketsrc1 = Mymac(2)
T_enetpacketsrc2 = Mymac(3)
T_enetpacketsrc3 = Mymac(4)
T_enetpacketsrc4 = Mymac(5)
T_enetpacketsrc5 = Mymac(6)
T_enetpackettype = &H0008 ' = &h0800
' fill IP-header
T_ip_vers_len = &H45
T_tos = &H00
T_ip_pktlen0 = &H00
T_ip_pktlen1 = &H30
T_id0 = &H4A
T_id1 = &HA5
T_flags = &H00
T_offset = &H00
T_ttl = &H80
'protocol (ICMP=1, TCP=6, UDP=11)
T_ip_proto = &H11
'header checksum
'T_ip_hdr_cksum0
'T_ip_hdr_cksum1
'IP address of source
T_ip_srcaddr0 = Myip(1)
T_ip_srcaddr1 = Myip(2)
T_ip_srcaddr2 = Myip(3)
T_ip_srcaddr3 = Myip(4)
'IP address of destination 'you have to put the IP-number of
T_ip_destaddr0 = 193 ' the NTP-server here
T_ip_destaddr1 = 67
T_ip_destaddr2 = 79
T_ip_destaddr3 = 202
'UDP-header
T_udp_srcport0 = &H13
T_udp_srcport1 = &H88
T_udp_destport0 = &H00 ' port 0025 = 37 NTP
T_udp_destport1 = &H25
T_udp_len0 = &H00
T_udp_len1 = &H1C
'T_udp_chksum0
'T_udp_chksum1
T_udp_data = Asc( "X")
T_udp_data1 = &H0A ' lf
T_udp_data2 = &H0D ' cr
T_udp_data3 = &H00
T_udp_data4 = &H00
T_udp_data5 = &H00
T_udp_data6 = &H00
T_udp_data7 = &H00
T_udp_data8 = &H00
T_udp_data9 = &H00
T_udp_data10 = &H00
T_udp_data11 = &H00
T_udp_data12 = &H00
T_udp_data13 = &H00
T_udp_data14 = &H00
T_udp_data15 = &H00
T_udp_data16 = &H00
T_udp_data17 = &H00
T_udp_data18 = &H00
T_udp_data19 = &H00
Call Ip_header_checksum
Call Udp_checksum
Call Echopacket
Enable Int0
End Sub

' Routine to convert the LONG from the NTP-server in to date and time
'
Sub Ntp
S(1) = T_udp_data
S(2) = T_udp_data1
S(3) = T_udp_data2
S(4) = T_udp_data3
Swap S(1) , S(4) : Swap S(2) , S(3)
L2 = L1 + 1139293696
L2 = L2 + 7200 ' offset UTC + 2 hours
Print "Date : " ; Date(l2)
Print "Time : " ; Time(l2)
Call Lcdhome
Lcd_t = "Date : "
Call Lcd_txt
Lcd_t = Date(l2)
Call Lcd_txt
Call Secondline
Lcd_t = "Time : "
Call Lcd_txt
Lcd_t = Time(l2)
Lcd_t = Left(lcd_t , 5)
Call Lcd_txt
End Sub

Replace the existing routine with this

' Sit and wait for an Interrupt from the RTL8019AS
'
Enable Interrupts
Config Date = Dmy , Separator = /
Enable Int0
On Int0 Rtl8019as_interrupt
'start the NIC
Call Write_rtl8019as(cr , &H22)
Do
If Lcdpresent = 1 Then
'idle-time
Incr Idletime
If Idletime > 2000000 Then
If Ntp = 0 Then
Disable Int0
Lcd_t = "*VA3TO/EIO-Board#Idle..."
Call Lcd_txt
Enable Int0
Idletime = 0
Else
Call Ntp_get
Idletime = 0
End If
End If
End If
Loop
End

Replace this at the UDP relays-part

'From within a VB-program
If T_udp_destport = Udp_port Then
If T_udp_srcport = &H2500 Then ' &h0025 NTP protocol
Call Ntp
Exit Sub
End If
Select Case T_udp_data
Case &H01 : Call Relayon(1)
Case &H02 : Call Relayon(2)
Case &H03 : Call Relayon(3)
Case &H04 : Call Relayon(4)
Case &HA1 : Call Relayoff(5)
Case &HA2 : Call Relayoff(6)
Case &HA3 : Call Relayoff(7)
Case &HA4 : Call Relayoff(8)
End Select

Here the code till this point

Step 29: How to put the SMD-parts on the board

With a fibreglass brush clean the pads for the SMD (here the RTL8019as)

 

Tin the pads with solder on two opposite corners of the SMD footprint.
 

 

Tack the SMD part onto the board at these two corners.

 

After tacking the SMD part on the two corners, apply flux to the full row of pins

 

Put the flux on one side of the SMD-part, and solder this part.
Continue until every side is fixed.

 

Soldering the SMD-part

 

Nice job!!!

 

Final inspection.

As a last step you could wipe the flux of the board with acetone and a Q-tip

Thanks to


Thanks to Fred Eady from www.edtp.com

Thanks to Mark Alberts, the creator of Bascom-AVR
www.mcselec.com

Thanks to Hugh Duff / VA3TO from www.drftech.com

Thanks to SMD-master San Bergmans from www.sbprojects.net

Two nice books!!!