Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)


+ Reply to Thread
Page 1 of 2 12 LastLast
Results 1 to 40 of 63
  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    With this post I hope to spark the interest in what could develop into a joint forum project. The purpose of the project is to get the Arduino Ethernet shield working with the AMICUS18 board and, of course PBP.


    Unfortunately the Ethernet shield isn't very directly compatible with the AMICUS18. The reason for this is that the shield picks up the SPI signals used to communicate with the W5100 ethernet chip and the onboard SD-card from the ICSP programming header on the Arduino. For apparent reasons this header does not exist on the AMICUS18. This means that Ethernet shield needs to be modifed in order to use it and this first post of mine is going to cover what and how I did to make it work with the AMICUS18.


    Before I go on I'd like to point out a couple of things:
    1) I'm using Ethernet shield v.6 which I got from NKC electronics, so what I describe here applies to that particular version. I do not know how it'll work with previous (or upcomming) versions of the shield.
    2) Doing these modification most likely voids any warranty you might have on the Ethernet shield.
    3) You do it at your own risk, don't blame me if you mess it up.



    Name:  CIMG1915.JPG
Views: 3647
Size:  162.3 KB

    As have been said the Ethernet shield uses a SPI interface to communicate with the host CPU. Since these signals are brought to the ICSP header on the shield we need to "move" them to pins that suits us better. The 18F25K22 that is used on the AMICUS18 has its SPI signals on RC3, 4 & 5 which are the same as pins 4, 3 & 2 on the Ethernet shield. Now, moving the SPI signals from the ICSP header to RC3, 4 & 5 wouldn't be too hard but unfortunately two out of three pins (RC3 & RC5) are already in use by the shield so we need to move those signals too - before we can connect the SPI signals to these I/O's.

    Pin 2 on the Ethernet shield (RC5 on the AMICUS18) carries the interrups signal from the W5100 chip. Since we might want to use this signal later on it would make sense to move it to an I/O-pin of the 18F25K22 capable of generating an interrupt. I've selected RB0 which on the Ethernet shield is labeled AREF. Fortunately AREF is not connected to anything on the shield so we're good to go there. By default the W5100 interrupt signal isn't actually connected to Pin2, instead there are two small solder pads on the back of the board which, when bridged with solder, connects the signal to Pin2. We'll use one of these pads to "hijack" the signal and re-route it to RB0 instead.

    Pin4 on the Ethernet shield carries the chip select signal for the onboard micro-SD card socket. This signal is an ordinary I/O so any pin will do really. I've selected RB2 which, on the Ethernet shield, is labeled 13.

    Why not RB1 you may think. It's because RB1 on the shield is GND and it would be hard to gain access to that pin without serious board surgury. This also brings up an important point: On the AMICUS18 board there's jumper that allows you the select if RB1 should "be" RB1 or if it's GND. This jumper should be in the GND location.

    When these two signals are moved we can connect the SPI interface signals from the 2X3 ICSP header to pins 2, 3 and 4 on the shield. Let's start with the trickiest one, Pin4, which carries the chip select signal for the SD-Card. First we need to "disconnect" the signal from Pin4, this is done by cutting the PCB trace just where it comes out from under the Arduino logo:

    Name:  CIMG1926.JPG
Views: 3874
Size:  154.3 KB

    With the trace cut we can turn the board over and find a good spot to "tap into" the signal:

    Name:  CIMG1927.JPG
Views: 3356
Size:  143.3 KB

    Running a wire from the via marked SD_SC to RB2 and a wire from the pad marked W5100_INT to RB0 is all it takes to "free up" the AMICU18's SPI pins so we can use them.

    In the next photo I've marked which pins in the ICSP header that we're interested in. SCK goes to RC3, MISO goes to RC4 and MOSI goes to RC5. I opted to remove the header altogether because it has a slight interference with the socket for the 18F25K20 on the AMICUS18 board.


    Name:  CIMG1922.JPG
Views: 3672
Size:  133.4 KB

    Here's the the back of the board with the modification complete. One trace cut and five jumper wires and we should be good to go. (I guess you can't really call that "Arduino shield compatible"....)

    Name:  CIMG1932.JPG
Views: 3348
Size:  170.6 KB

    /Henrik.

  2. #2
    Join Date
    Nov 2003
    Location
    Wellton, U.S.A.
    Posts
    5,924

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thank you Henrik,

    I promoted this to an article.
    http://www.picbasic.co.uk/forum/cont...r=346-amicus18
    Dave
    Always wear safety glasses while programming.

  3. #3
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    good write up on the modification Henrik,

    I have always dreamed of setting up a little web connected interface for home automation type stuff. This seems like it would be a great place to start... especially if it was based on PICbasic pro.

    I guess I better get me one of those amicus's and an ethernet shield.
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    You can definitely use the EM202 or one of Tibbos other modules, or you can use theXPort or perhaps something from Netburner, or maby the EZWebLynx or the DigiConnectMe. Or you might want to go "low level / hardcore" and use the ENC28J60 or ENC424J600 or some other ethernet control and write the TCP/IP stack yourself. There are many ways of doing it.

    But this thread wasn't supposed to be about other ways of doing it, it was meant to be about how to do it with the Wiznet W5100 which is what is being used on the Ethernet shield.

    By the way, I've got PING working....and I've got to a point where I can see the HTTP Get request in the W5100 Receive memory buffer :-)

    /Henrik.

  5. #5
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    For those wanting to follow along, but maybe not wanting to invest in the shield and Amicus18 right now, there is a $25 solution that uses the same W5100 ethernet chip. http://www.sparkfun.com/products/9473
    Last edited by ScaleRobotics; - 22nd May 2011 at 13:50.

  6. #6
    Join Date
    Apr 2003
    Location
    Cambridge - UK
    Posts
    884

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    My applogies to Henrick, quite right, this thread was (is) about using Arduino shields on the Amicus18. My post was out of order, promoting an alternative , was incorrect.

    Henrick, i stand corrected, I hope you accept my appology, thread hijacking is unacceptable. i've removed my post and those related to it.

  7. #7
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Henrick,

    Keep us posted on your progress on this... I have absolutely no experience getting into the "nuts and volts" of ethernet protocol... but, as I mentioned earlier, would love to have a little web interface to some home automation capability.

    Thanks for your efforts.
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  8. #8
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    I don't know what the "best" way to aproach this actually is. I don't think me posting hundreds or thousands of lines of code is going to actually help others that much and I don't think I'll get much out of it either.

    Instead I think I'll post snippets and routines, explaining what they do (or at least intend to do) and why. This way I think it's easier to tag along, one step at the time and, which is important, we can discuss suggestions on improvements and alternative ways of doing the same thing. In the end we will hopefully have a set of routines which can be used to build a working application.

    Before we go on I just have to point out that this is new territory for me too. I don't know much about ethernet, UDP, TCP, sockets, HTTP, HTML etc so this is very much a learning experience for me as well. Please feel free to correct any statements I make in error and please do bring suggestions on how to do things differently and, hopefully, better.


    Right, the Wiznet W5100 ethernet chip (which is used on the Ethernet shield) is controlled by reading and writing to its internal registers. There are quite a few registers and instead of using a bunch of 'magic numbers' (ie the adress of the register in the W5100's memory) I've put together a file (W5100_defs.pbp) containing the name and memory adress for the registers as well as a couple of other constants we might need. Attached to this post is v1.0 of that file (remove .txt extenstion), there might be newer versions in the posts to come.

    I've set up my AMICUS18 board to use the hardware USART at 115200 baud for debugging purposes. Then I set up the TRISB and TRISC registers to match the pinout of the modified Ethernet shield:
    Code:
    ' By default the AMICUS18 board uses the PIC18F25K20, make sure to
    ' set up MCSP or whatever IDE is being used to compile for the
    ' correct device.
     
    DEFINE OSC 64
    DEFINE LOADER_USED 1            ' We're using a bootloader.
    DEFINE HSER_RCSTA 90h           ' Enable serial port & continuous receive
    DEFINE HSER_TXSTA 24h           ' Enable transmit, BRGH = 1
    DEFINE HSER_CLROERR 1           ' Clear overflow automatically
    DEFINE HSER_SPBRG 138           ' 115200 Baud @ 64MHz, -0,08%
     
    SPBRGH = 0
    BAUDCON.3 = 1                   ' Enable 16 bit baudrate generator
     
    TRISC = %10010111
    ' RC7: USART RX
    ' RC6: USART TX
    ' RC5: ETHERNET SPI Data out (SDO/MOSI)
    ' RC4: ETHERNET SPI Data in (SDI/MISO)
    ' RC3: ETHERNET SPI clock (SCK) set to INPUT according to datasheet....
    ' RC2: N/U - currently
    ' RC2: N/U - currently
    ' RC1: N/U - currently
     
    TRISB = %11011011
    ' RB7: N/U - currently
    ' RB6: N/U - currently
    ' RB5: ETHERNET Chip select line to W5100
    ' RB4: N/U - currently
    ' RB3: N/U - currently
    ' RB2: ETHERNET Chip select line to SD-Card
    ' RB1: N/U and can not be used with the Ethernet shield
    ' RB0: ETHERNET Interrupt pin from W5100

    Then I pretty much cut'n'pasted the register definition/aliases for the MSSP module from the SPI master example on MELABS web site. However, I had to change the CKE bit from 0 to 1 in order to get reliable communications with the W5100. This feels a bit awkward but since doing that it has been rock solid so it must be right - right?
    Code:
    ' Aliases for the MSSP module status and control bits.
    SSPEN   VAR     SSPCON1.5   ' SSP Enable bit  
    CKP     VAR     SSPCON1.4   ' Clock Polarity Select  
    SMP     VAR     SSPSTAT.7   ' Data input sample phase  
    CKE     VAR     SSPSTAT.6   ' Clock Edge Select bit  
    SSPIF   VAR     PIR1.3      ' SPI interrupt flag
     
    'Set up the MSSP module.
    CKP = 0                     ' Clock idles low  
    CKE = 1                     ' Transmit on active to idle transition.  
    SSPIF = 0                   ' Clear buffer full status  
    SMP = 0                     ' Sample in middle of data 
    SSPEN = 1                   ' Enable SPI pins
    SSPCON1.0 = 1              'Slow down SPI clock to Fosc/16 for now.
    I also had to slow down the SPI clock from the default (Fosc/4) to Fosc/16 in order to get reliable communication. The AMICUS18 defaults to 64MHz so with Fosc/16 the SPI clock is running at 4Mhz which isn't directly slow but well below what the W5100 is capable of (if I read the datasheet correctly that would be ~14Mhz). However, the modification done to the shield and the fact that W5100 is on a separate board from the 18F25K20 may have an impact on the maximum speed achievable. Anyway, I'll leave it at Fosc/16 for now.

    Finally I included the file with all the register definitions for the W5100:
    Code:
    INCLUDE "W5100_defs.pbp"        ' Register definitions and constants for the W5100.
    That's all for this post.

    /Henrik.
    Attached Files Attached Files

  9. #9
    Join Date
    Sep 2009
    Posts
    674

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    I am interested in this project. For now, I have no hardware, no time to make it. So for now I'll watch and learn

  10. #10
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    In todays post I'll try to cover the basic input and output routines I use to communicate with the W5100.

    The W5100 can communicate with the main controller either over a parallel data bus featuring 14 adress lines, 8 data lines and a couple of control lines or it can use a serial interface, SPI. On the Ethernet shield the W5100 is setup to use the SPI interface which is fortunate because we simply don't have enough I/O lines on the AMICUS18.

    Every transaction to and from the W5100, when using SPI, requires 4 bytes. First an OP code telling the W5100 if we're doing a read or a write operation, then two bytes containing the adress we are writing or reading and finally the actual data. When writing to the W5100 WE put the data in the forth byte, when reading from the W5100 IT puts the data in the forth byte - basically. So for each transaction we pull the Chip Select line of the W5100, send 4 bytes and release the Chip Select line (pull high). Here's the code for my read and write subroutines:

    Code:
    WizAdress           VAR WORD       ' Adress in the W5100 to be read or written
    WizData             VAR BYTE       ' Data to be written or data read from the W5100
    WizResponse         VAR BYTE[4]    ' Respose to the four bytes in each SPI transaction. Should be 0,1,2,3 or 0,1,2,data
     
    W5100_Select        VAR PortB.5    ' Chip select line of W5100 connected to RB.5
    W5100_Select = 1
     
    '******************************************************************************************
    Read_W5100:
        WizResponse[0] = SSPBUF         ' Dummy read 
     
        W5100_Select = 0                ' Select W5100
     
        SSPBUF = W5100_READ_OP          ' Send OP code for read operation
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[0] = SSPBUF         ' Store result, should be 0
     
        SSPBUF = WizAdress.HighByte     ' Send high byte of adress
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[1] = SSPBUF         ' Store result, should be 1
        SSPBUF = WizAdress.LowByte      ' Send low byte of adress
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[2] = SSPBUF         ' Store result, should be 2
     
        SSPBUF = 0                      ' Send data
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[3] = SSPBUF         ' Store result, will be the actual data read at the specified adress
     
        WizData = WizResponse[3]        ' Copy databyte from array to WizData.
        W5100_Select = 1                ' Deselect the W5100
    RETURN
    '******************************************************************************************
     
    '******************************************************************************************
    Write_W5100:
        WizResponse[0] = SSPBUF         ' Dummy read.
     
        W5100_Select = 0                ' Pull chip select line low to select W5100
     
        SSPBUF = W5100_WRITE_OP         ' Send OP code for write operation
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[0] = SSPBUF         ' Store result, should be 0
     
        SSPBUF = WizAdress.HighByte     ' Send high byte of adress
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[1] = SSPBUF         ' Store result, should be 1
     
        SSPBUF = WizAdress.LowByte      ' Send low byte of adress
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[2] = SSPBUF         ' Store result, should be 2
     
        SSPBUF = WizData                ' Send the data byte
        While SSPSTAT.0 = 0 : WEND      ' Wait for operation to complete
        WizResponse[3] = SSPBUF         ' Store the result, should be 3
     
        W5100_Select = 1                ' Deselect the W5100
     
    RETURN
    '******************************************************************************************
    I've declared two variables of specific interest here, WizAdress and WizData. WizAdress is the adress in the W5100 we want to access and WizData is, in the case of a write operation, the data we want to write to that adress OR, in the case of a read operation, the data read FROM that adress. In the W5100_defs.pbp file attached to an earlier post the OP-codes for read and write operation as well as the names and adresses of all the registers are defined. Looking at the datasheet for the W5100 we can see that setting bit 7 of the Mode register will perform a reset of the device after which bit 7 is automatically cleared again. Doing this with help from the above subroutines is as simple as:
    Code:
    WizAdress = W5100_Mode : WizData = %10000000 : GOSUB Write_W5100
    In a similar manner we can read, for example, the interrupt register at location $0015 and output its content over the USART like this:
    Code:
    WizAdress = W5100_IR : GOSUB Read_W5100
    HSEROUT ["The status of the Interrupt register is: ", BIN8 WizData, 13]
    As ilustrated here, when writing TO the W5100 WE put the data in the WizData variable and when reading FROM the W5100 we GET the data in the WizData variable.


    To get the Ethernet shield to respond to a Ping all we need to do is set its MAC-adress, IP-adress and NetMask. Doing that is just a series of write operations:

    Code:
    SetUp:
        HSEROUT["W5100_Init",13]
     
        'Send Reset command to W5100.
        WizAdress = W5100_Mode  : WizData = 128 : Gosub Write_W5100
     
        ' Set MAC-adress, see sticker on the back of the Ethernet shield. 
        WizAdress = W5100_SHAR0 : WizData = $05 : GOSUB Write_W5100
        WizAdress = W5100_SHAR1 : WizData = $06 : GOSUB Write_W5100
        WizAdress = W5100_SHAR2 : WizData = $07 : GOSUB Write_W5100
        WizAdress = W5100_SHAR3 : WizData = $08 : GOSUB Write_W5100
        WizAdress = W5100_SHAR4 : WizData = $09 : GOSUB Write_W5100
        WizAdress = W5100_SHAR5 : WizData = $0A : GOSUB Write_W5100
     
        'Set IP adress of W5100    
        WizAdress = W5100_SIPR0 : WizData = 192 : GOSUB Write_W5100
        WizAdress = W5100_SIPR1 : WizData = 168 : GOSUB Write_W5100
        WizAdress = W5100_SIPR2 : WizData = 1   : GOSUB Write_W5100
        WizAdress = W5100_SIPR3 : WizData = 50  : GOSUB Write_W5100
        'Set net-mask
        WizAdress = W5100_SUBR0 : WizData = 255 : GOSUB Write_W5100
        WizAdress = W5100_SUBR1 : WizData = 255 : GOSUB Write_W5100    
        WizAdress = W5100_SUBR2 : WizData = 255 : GOSUB Write_W5100
        WizAdress = W5100_SUBR3 : WizData = 0   : GOSUB Write_W5100
        HSEROUT ["Basic setup done",13]
     
    Main:
    @ NOP
    GOTO MAIN
    Name:  Ping.jpg
Views: 3125
Size:  49.9 KB
    (Sorry about the Swedish screenshot but you'll get the idea...it works...)

    Without the HSEROUT statements we're looking at a just above 400 bytes (out of the 18F25K20's 16k) worth of program memory. Granted, only being able to respond to a ping isn't really worth much from a practical standpoint but it IS a start.


    Finally a word or two about the WizResponse array. When using SPI, data is shifted in and out of the PIC at the same time. Basically as one bit goes out at the "low end" of the MSSP modules shift register one bit goes at the "high end" so after 8 clock cycles the byte we loaded into SSPBUF have been exchanged with a byte from the W5100.

    When writing data to the W5100 it responds with 0, 1, 2, 3 for the four bytes it receives and I'm storing this response in the array WizResponse in order to potentionally use it to verify that the operation was successfull. When reading from the W5100 it responds with 0, 1, 2 and then the actual data in the 4th byte so all I'm doing is copying the data from the 4th byte of the array to the WizData variable before returning from the subroutine.

    /Henrik.

  11. #11
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    In todays post(s) I thought I'd try demonstrate how to set up one of the W5100's four sockets in TCP mode and how to read data that is received. In the end we'll be able to see the data that a web browser sends when it tries to reach the W5100.

    The W5100 can have four connections, or sockets as it called, open simultanously. What this means is that you can have one socket setup to listen for TCP trafic on port 80 (which is the standard port for HTTP) thus acting as a webserver. At the same time you can have another socket connect TO a server and send an email while a third socket connects to a NTP server and retrieves the correct time.

    For now we'll concentrate on a single socket though.

    First order of business is to initialize the W5100, like before this is done by performing a soft reset and then write to its SHAR, SIPR, SUBR and GAR registers to set the MAC-adress, IP-adress, net-mask and gateway adress. Setting the gateway adress isn't strictly needed for this to work but I did that anyway. For now I kept all the settings "hardcoded" but in a real device these would obviously have to be changable thru some kind of userinterface and stored in EEPROM or whatever.
    Code:
    '******************************************************************************************
    Init_W5100:
     
        'Perform a soft reset of the W5100
        WizAdress = W5100_Mode : WizData = 128 : Gosub Write_W5100
     
        ' Set W5100 MAC adress
        WizAdress = W5100_SHAR0 : WizData = $90 : GOSUB Write_W5100
        WizAdress = W5100_SHAR1 : WizData = $A2 : GOSUB Write_W5100
        WizAdress = W5100_SHAR2 : WizData = $DA : GOSUB Write_W5100
        WizAdress = W5100_SHAR3 : WizData = $00 : GOSUB Write_W5100
        WizAdress = W5100_SHAR4 : WizData = $48 : GOSUB Write_W5100
        WizAdress = W5100_SHAR5 : WizData = $03 : GOSUB Write_W5100
     
        ' Set W5100 IP adress
        WizAdress = W5100_SIPR0 : WizData = 192 : Gosub Write_W5100
        WizAdress = W5100_SIPR1 : WizData = 168 : Gosub Write_W5100
        WizAdress = W5100_SIPR2 : WizData = 1   : Gosub Write_W5100
        WizAdress = W5100_SIPR3 : WizData = 50  : Gosub Write_W5100
     
        ' Set W5100 Net mask
        WizAdress = W5100_SUBR0 : WizData = 255 : GOSUB Write_W5100
        WizAdress = W5100_SUBR1 : WizData = 255 : GOSUB Write_W5100
        WizAdress = W5100_SUBR2 : WizData = 255 : GOSUB Write_W5100
        WizAdress = W5100_SUBR3 : WizData = 0   : GOSUB Write_W5100
     
        ' Set W5100 gateway adress
        WizAdress = W5100_GAR0 : WizData = 192 : GOSUB Write_W5100
        WizAdress = W5100_GAR1 : WizData = 168 : GOSUB Write_W5100
        WizAdress = W5100_GAR2 : WizData = 1   : GOSUB Write_W5100
        WizAdress = W5100_GAR3 : WizData = 254 : GOSUB Write_W5100
    RETURN
    '******************************************************************************************
    Next task is to setup socket 0 to listen for TCP trafic on port 80. To do this we first write the value 1 to the sockets Mode register, this sets the protocol for the socket to TCP. Then we write the value 80 to the sockets Source Port register and then tell the W5100 to open the socket. Once the socket is open we can finally switch it into Listen mode which is what we want when acting as a server.
    Code:
    '******************************************************************************************
    Init_Socket_0:
        ' Set socket 0 protocol to TCP by assigning the sockets mode register the value 1.
        WizAdress = W5100_S0_MR       : WizData = 1  : GOSUB Write_W5100
     
        ' Set the port register of socket 0.
        WizAdress = W5100_S0_PORT0    : WizData = 0  : Gosub Write_W5100
        WizAdress = W5100_S0_PORT1    : WizData = 80 : GOSUB Write_W5100
     
    Open_Socket_0:
        ' Set the Command Register of socket 0 to OPEN - this inits the socket.
        WizAdress = W5100_S0_CR     : WizData = W5100_Sn_OPEN : GOSUB Write_W5100
     
        ' Now switch Socket 0 into Listen Mode.
        WizAdress = W5100_S0_CR  : WizData = W5100_Sn_LISTEN : GOSUB Write_W5100
    RETURN
    '******************************************************************************************
    Once the socket is in Listen mode it will automatically switch to Established mode when it receives a connection request from a client. We can check in which mode the socket is by reading the sockets Status register.
    Code:
    HSEROUT ["Program Start", 13]
    Main:
        WizAdress = W5100_S0_SR : GOSUB Read_W5100
        HSEROUT["Socket 0 status = $", HEX WizData,13]
        Pause 500
    Goto Main
    If you run this code with a serial terminal open and type in the W5100's IP-adress in a browser you should see the sockets status register changing from $14 (Listen) to $17 (Established):

    Name:  Socket status output.jpg
Views: 4221
Size:  49.3 KB

    ...to be continued in the next post.

    /Henrik.

  12. #12
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    ...continued from previous post...

    In the W5100 there's a total of 8k of RAM reserved for the sockets receive memory buffer as well as 8k for the sockets transmit memory buffer. The memory can be assigned to each socket in various configuration by writing to the RMSR and TMSR registers. By default both these registers are set to $55 which assigns 2k of RX and TX memory to each of the four sockets. Since we're only about to use one socket we COULD assign then full 8k of each buffer to socket 0 but for this exercise we'll just leave it at the default.

    Once we've established a connection with the client we can determine if any data has been received by reading the sockets Received Size register. The value returned tells us the number of "new" bytes in the sockets receive buffer. What it doesn't tell us is where in the buffer these bytes are....

    The sockets receive (and transmit) buffers are circular buffers meaning once they hit the highest memory adress they wrap around and start over at the beginning. The first byte we're interested in might be somewhere in the middle of the buffer and the last byte might be just before it even though it intuitively should be after the first byte. And, because all four sockets are assigned a "segment" of memory out of a total of 8k the actual start and end adresses of each sockets buffer can "move around" depending on the amount of memory assigned to each socket. If we take a look at the bottom of the W5100_defs.pbp file posted earlier we'll find this:
    Code:
    '**************************************************************************************
    ' ----------- THESE VALUES MAY NEED TO BE CHANGED BY THE USER OF THIS FILE ------------
    '**************************************************************************************
    ' Socket 0-3 RX memory START & END adresses and MASK values for calculating pointer.
    ' Make sure to change these if the RMSR value is changed from its default value of $55
     
    W5100_S0_RX_START        CON $6000  'Start adress of Socket 0 (not changeable)
    W5100_S0_RX_END          CON $67FF
    W5100_S0_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S1_RX_START        CON $6800  'Start adress of Socket 1 (depends on the size of socket 0)
    W5100_S1_RX_END          CON $6FFF
    W5100_S1_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S2_RX_START        CON $7000  'Start adress of socket 2 (depends on the size of sockets 0 & 1)
    W5100_S2_RX_END          CON $77FF
    W5100_S2_RX_MASK         CON $07FF  'Mask value for 2k
     
    W5100_S3_RX_START        CON $8000  'Start adress of socket 3 (depends on the size of sockets 0, 1 & 2)
    W5100_S3_RX_END          CON $8FFF
    W5100_S3_RX_MASK         CON $07FF  'Mask value for 2k
    '**************************************************************************************
    These are the physical start and end adresses for each sockets RX memory buffer when they are each assigned 2k of memory. If the memory allocation between sockets are changed these constants must be updated to reflect the start and end adress of each sockets memory area.

    OK, so how do we know where in the buffer our new data is? This is done by reading the sockets RX Read Pointer register but unfortunately this doesn't give us the real adress of the data, instead it gives us an offset into the sockets memory area. If the value of the pointer is 0 then the first byte of our new data is located at the start-adress of the sockets RX memory ($6000 in this case), if the value is $123 then the first byte is located at adress $6123. Not to bad.... But to make it a bit more complicated this pointer doesn't wrap around "in sync" with the circular buffer. The Rx Read Pointer register is a 16bit register meaning it counts from 0 to 65535 even though we only have 2048 bytes assigned to our buffer - so where is our data when the pointer value is $1234?

    This is where the sockets RX_MASK constant defined in the W5100_defs.pbp file comes in. When the raw pointer value is bitwise AND'ed with the RX_MASK value we'll get the correct offset into the sockets buffer. If we then add this offset to the physical start adress of sockets buffer we finally have the physical adress of the first byte of new data in the buffer.

    So now we know where the first byte of our new data is as well as how many byte there is to read, now we only need to figure out where the last byte is. Given the above this shouldn't be too hard to do... If the adress of the first byte plus the total number of bytes does not "extend" beyond the end of the buffer (no buffer wrap-around) we're done - just read from first byte to first byte + number of bytes. But if it does "extend" beyond the end of the buffer we need to first read up to the end of the buffer, then start over at the beginning, subtracting the number of bytes already accounted for and continue reading. I've wrapped all of this into, yet another, subroutine:
    Code:
    '******************************************************************************************
    ' Variables WizPtr, WizStart, WizEnd and WizSize (all WORDs) declared elsewhere.
     
    CheckBufferForData:
     
        ' Read the sockets receive size register.
        WizAdress = W5100_S0_RX_RSR0 : GOSUB Read_W5100 : WizSize.HighByte = WizData
        WizAdress = W5100_S0_RX_RSR1 : GOSUB Read_W5100 : WizSize.LowByte = WizData
     
        ' If received size is more than 0 we do indeed have new data in the buffer.
        ' In that case we read the socket 0 receive pointer register.
        IF WizSize > 0 THEN
     
            WizAdress = W5100_S0_RX_RD0 : Gosub Read_W5100 : WizPtr.HighByte = WizData
            WizAdress = W5100_S0_RX_RD1 : GOSUB Read_W5100 : WizPtr.LowByte = WizData
     
            ' Calculate the physical adress of the first "fresh" byte in the buffer.
            WizStart = W5100_S0_RX_START + (WizPtr & W5100_S0_RX_MASK)
     
            ' Is the buffer about to wrap around?
            IF WizStart + WizSize > W5100_S0_RX_END THEN
                WizEnd = W5100_S0_RX_START + ((WizStart + WizSize) - W5100_S0_RX_END)
                WizEnd = WizEnd - 2         ' Account for the "zero indexing"
     
            ELSE                                  'No wrap around.
     
                WizEnd = WizStart + WizSize - 1
            ENDIF
     
        ENDIF
    RETURN
    '******************************************************************************************
    When returning from this subroutine the physical adress of the first byte as well as that of the last byte of new data are available to us in the WizStart and WizEnd variables. If WizEnd is less than WizStart the buffer has wrapped around and we need to account for that when actually "extracting" the data. If WizEnd is bigger than [/i]WizStart[/i] then it's as simple as reading from the WizStart to WizEnd.
    Code:
    '*****************************************************************************************************
    GetData:
        HSEROUT [REP "-"\20, " Data read from Rx buffer follows ", REP "-"\20, 13]
     
        Counter = 0                                 ' Temporary as a dubug aid
     
        ' Do we have a buffer wrap around condition?
        If WizEnd < WizStart THEN
     
           ' When that happends we need to start reading at the first byte in the received
           ' package and go on until the end of the sockets RX memory....
            For WizAdress = WizStart to W5100_S0_RX_END
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            NEXT
     
            ' ...then we need to start over at the beginning of the buffer and keep reading
            ' until we've got all the bytes in the buffer.
            For WizAdress = W5100_S0_RX_START to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            Next
     
        ELSE       ' There's no buffer wrap-around.
     
            ' WizStart now contains the physical adress of the first byte to read.
            For WizAdress = WizStart to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
                Counter = Counter + 1                ' Keep count
            NEXT
     
        ENDIF
     
        HSEROUT [REP "-"\23, " End of data from Rx buffer ", REP "-"\23, 13]
        HSEROUT ["Total number of bytes read:", #Counter,13]
        HSEROUT [REP "-"\76, 13, 13]
    When all data have been "extracted" from the buffer we have to tell the W5100 that we're done so it can recycle that memory for new data that comes in. This is done by first updating the RX Read pointer register so it points to where we left off and finally issuing the RECV command to the sockets command register. Issuing the RECV command tells the W5100 that we're done with the recevied data up to the "adress" of the newly written RX Read pointer register.

    Finally, since we're currently not responding to the request from the client, we simply disconnect and switch back into Listen mode. Not very polite but it'll look the browser as if no one is at the other end so it's OK for now.
    Code:
        WizPtr = WizPtr + WizSize
     
        WizAdress = W5100_S0_RX_RD0 : WizData = WizPtr.HighByte : GOSUB Write_W5100
        WizAdress = W5100_S0_RX_RD1 : WizData = WizPtr.LowByte  : GOSUB Write_W5100
     
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_RECV : GOSUB Write_W5100
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_LISTEN : GOSUB Write_W5100
    Using the above code you'll be able to "see" the HTTP GET request issued by a web-browser pointing at the W5100's IP-adress:

    Name:  HTTP Get request.jpg
Views: 3254
Size:  84.7 KB

    To this post I've attached the exact file I compiled to arrive at the screenshot above (remove .txt extension), you'll also need the W5100_defs.pbp posted earlier in the thread.

    /Henrik.
    Attached Files Attached Files

  13. #13
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    Anyone knows what needs to be done to have a device listen to a "name" as well as its IP-adress?
    For example, I have a SOHO server which has a web-interface, I can access that by either typing in its IP-adress or its name in the adress field of the browser.

    I don't know if it's even possible to do with the W5100 but without knowing how the name is mapped to the IP-adress I don't know how to find out if it can be done. I'm guessing the W5100 needs to "present itself" to the network somehow but how does that mechanism work? Anyone knows or have a reference you can point me at?

    /Henrik.

  14. #14
    Join Date
    Dec 2005
    Posts
    1,073

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Quote Originally Posted by HenrikOlsson View Post
    Anyone knows what needs to be done to have a device listen to a "name" as well as its IP-adress?
    For example, I have a SOHO server which has a web-interface, I can access that by either typing in its IP-adress or its name in the adress field of the browser.
    I think your SOHO server (or router) has a DNS/DHCP server which maintains a list of names and corresponding IP addresses. This page might help.
    Last edited by dhouston; - 5th June 2011 at 16:11.

  15. #15
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thanks a lot Dave!
    Simply knowing it's the DNS servers job pairing up names with IP-numbers narrows the search for me. I feel I should've know that but for some reason I was only thinking of DHCP which I knew it wasn't.
    Wiznet has an appnote on DNS but I'm not sure it pertains to the W5100, but again, now I know what to look for.

    Thanks!
    /Henrik.

  16. #16
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    I now have a working DHCP client. This means I can just plug in the network cable and the thing gets an IP-adress from the DHCP server (my router in this case). It also passes a host name to the DHCP-server which then somehow passes that on to the DNS server that apparently runs on the router. In the end it means that I can now get to it by browsing to http://amicus or whatever name I wish the unit to have - good times :-)

    But, this has lead me to the unavoidable...
    The routines previously posted are more or less hardcoded to use socket 0. This is of course not optimal and now is the time to do something about that. Each socket has its own control and status registers etc at a fixed offset of $100 between them so it's pretty easy to calculate the correct adress based on the socket we want the routine to access. For example:
    Code:
    WizSocket VAR BYTE
    WizSocket = 2
     
    WizAdress = W5100_S0_MR + (WizSocket * $100) : WizData = 1 : GOSUB Write_W5100
    This will access socket 2's mode register - pretty straight forward.

    The problem comes with the resizable circular receive and transmitt buffers. The only fixed point here is the start of socket 0's RX and TX buffer. The end of it as well as the start AND end of the other sockets buffers can move around based on how we set it up. I'm looking for ideas on how to handle this effeciently and user friendly.

    Currently I've done this:
    Code:
    GetSocketRXAdress:
    ' Complete routine executes in 174 cycles.
        WizAdressOffset = WizSocket * $100
     
    ' These 3 lookup statements takes 259 bytes of program memory  
        Lookup2 WizSocket, [W5100_S0_RX_Start, W5100_S1_RX_Start, W5100_S2_RX_Start, W5100_S3_RX_Start], Socket_RX_Start
        Lookup2 WizSocket, [W5100_S0_RX_END,   W5100_S1_RX_END,   W5100_S1_RX_END,   W5100_S1_RX_END],   Socket_RX_End
        Lookup2 WizSocket, [W5100_S0_RX_MASK,  W5100_S1_RX_MASK,  W5100_S2_RX_MASK,  W5100_S3_RX_MASK],  Socket_RX_MASK  
     
    RETURN
    And of course a similar routine for the TX buffers. Instead of using W5100_S0_RX_Start etc the routines that accesses the RX buffer now uses Socket_RX_Start etc instead.

    These routines are then called when "changing sockets" meaning that we don't have to calculate the adresses every time we access a register we only do it when changing sockets. The routine to check the amount of free space in any sockets TX buffer then looks like this:
    Code:
    GetFreeTXSize:
        If WizSocket <> WizOldSocket THEN            ' No need to get offsets etc if we're using the same socket as last time.
            GOSUB GetSocketTXAdress
        ENDIF
        
        ' Get the total amount of free memory in the sockets transmit buffer.
        WizAdress = W5100_S0_TX_FSR0 + WizAdressOffset : GOSUB Read_W5100 : WizSize.HighByte = WizData
        WizAdress = W5100_S0_TX_FSR1 + WizAdressOffset : GOSUB Read_W5100 : WizSize.LowByte = WizData
        
        ' Get the pointer adress of where to put new data.
        WizAdress = W5100_S0_TX_WR0 + WizAdressOffset : GOSUB Read_W5100 : WizPtr.HighByte = WizData
        WizAdress = W5100_S0_TX_WR1 + WizAdressOffset : GOSUB Read_W5100 : WizPtr.LowByte = WizData
        
        'Calculate physical adress of first byte
        WizStart = Socket_TX_START + (WizPtr & Socket_TX_MASK)
        
        WizOldSocket = WizSocket             ' Remember which socket we used.
    RETURN
    I think this is a workable solution to allowing access to any socket without duplicating all the routines with hardcoded adresses AND without having to calculate/retreive the adresses every time we access the W5100.

    What I'm looking for is ideas on how to make it more efficient, I particullarly dislike the fact that the lookup routines (for both TX and RX buffers) takes well over 500 bytes (that's something like 10% of a my complete working web-server including a couple of hundred bytes HTML). Me think there must be a slicker way...

    Thank you in advance!

    /Henrik.

  17. #17
    Join Date
    Sep 2009
    Posts
    674

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Try something like this
    Code:
    Socket_RX_Start var word[4]
    Socket_RX_Start[0]=W5100_S0_RX_Start
    Socket_RX_Start[1]=W5100_S1_RX_Start
    Socket_RX_Start[2]=W5100_S2_RX_Start
    Socket_RX_Start[3]=W5100_S3_RX_Start
    Then you won't need GetSocketRXAdress. In routines just use
    WizStart = Socket_RX_Start[WizSocket] + (WizPtr & Socket_TX_MASK)

  18. #18
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hello again,
    I guess you can all see the bug in the Lookup code - the line where it looks up the sockets end adress currently returns the wrong adress for socket 2 and 3. As usual I can't edit the post :-(

    /Henrik.

  19. #19
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thanks!
    That's one solution which I actually have thought of but it comes at a cost of 48 vs 12 bytes of RAM. But in the end it might be worth it though, I'll give it second thought, thanks!

    /Henrik.

  20. #20
    Join Date
    Sep 2009
    Posts
    674

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    http://www.picbasic.co.uk/forum/showthread.php?t=14923
    Look at post #5.
    You can store data in eeprom, but then you must use READ.

  21. #21
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hello,
    Now that we know how to setup the W5100 to listen for TCP trafic on the correct port, how to determine if there's data to be read in the sockets RX memory buffer and where in the buffer that data is it's time to look at how we can parse the received data and act upon it. After all, simply "resending" the received data out the USART may be cool (for 5 minutes) but it doesn't really do anything for us, though it does serve as a nice debugging tool.

    So today I'll try to cover how we can load the recevied data from the W5100 into a local buffer, do some (for now very) simple parsing of that data in order to determine what the client wants and then build and send a response. The end result will be a simple web-page displayed in the browser window.

    Trying to parse the data directly in the W5100 proved to be a bit tricky so I decided to move it to RAM in the PIC and do the parsing there. To do this we first need somewhere to put the data, an array called Buffer of some, for now arbitrary, length:
    Code:
    BufferSize  CON     128
    Buffer      VAR     BYTE[BufferSize]' Local buffer for loading and unloading data from the W5100
    Then I modified the GetData routine posted earlier to not only "print" the received bytes on the serial terminal but to also stuff them into our previously declared array. The GetData routine now looks like this:
    Code:
    '*****************************************************************************************************
    GetData:
        HSEROUT [REP "-"\20, " Data read from Rx buffer follows ", REP "-"\20, 13]
     
        Counter = 0                                 ' Start loading the local array buffer at the beginning.
    
        ' Do we have a buffer wrap around condition?
        If WizEnd < WizStart THEN
     
        ' When that happends we need to start reading at the first byte in the received
        ' package and go on until the end of the sockets RX memory....
            For WizAdress = WizStart to W5100_S0_RX_END
                Gosub Read_W5100
                HSEROUT[WizData]
    
                ' Make sure we don't write outside of the allocated buffer.
                If Counter <= BufferSize THEN
                    Buffer[Counter] = WizData
                ENDIF
     
                Counter = Counter + 1                ' Keep count
    
            NEXT
     
            ' ...then we need to start over at the beginning of the buffer and keep reading
            ' until we've got all the bytes in the buffer.
     
            For WizAdress = W5100_S0_RX_START to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
    
                ' Make sure we don't write outside of the allocated buffer.            
                If Counter <= BufferSize THEN
                    Buffer[Counter] = WizData
                ENDIF
     
                Counter = Counter + 1                ' Keep count
            NEXT
     
        ELSE                                         ' There's no buffer wrap-around.
     
            ' WizStart now contains the physical adress of the first byte to read.
            For WizAdress = WizStart to WizEnd
                Gosub Read_W5100
                HSEROUT[WizData]
     
                ' Make sure we don't write outside of the allocated buffer.
                If Counter <= BufferSize THEN
                    Buffer[Counter] = WizData
                ENDIF
    
                Counter = Counter + 1                ' Keep count
    
            NEXT
     
        ENDIF
     
        HSEROUT [REP "-"\23, " End of data from Rx buffer ", REP "-"\23, 13]
        HSEROUT ["Total number of bytes read:", #Counter,13]
        HSEROUT [REP "-"\76, 13, 13]
     
        ' The variable RxPtr contains the "raw value" read from the sockets Rx Read Pointer Register.
        ' Now we need to be update that so it points one "step" ahead of the last location we just read.
        ' This is done to let the W5100 how "far" in the buffer we've read and that it can use any space
        ' "before" this location for new data. Note that this is not the ACTUAL adress of the data,
        ' just the unmasked index.
     
        WizPtr = WizPtr + WizSize
        WizAdress = W5100_S0_RX_RD0 : WizData = WizPtr.HighByte : GOSUB Write_W5100
        WizAdress = W5100_S0_RX_RD1 : WizData = WizPtr.LowByte  : GOSUB Write_W5100
    
        ' Then we send the RECV command to the sockets command register to let it know that
        ' we've handled all the data we currently are prepared to handle. This updates the
        ' pointers in the sockets memory so that it knows where to put new data. It is not
        ' until after issuing the RECV command that the sockets RX pointer is actually updated
        ' even though we write to it above.
    
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_RECV : GOSUB Write_W5100
     
    RETURN
    '*****************************************************************************************************
    As you can see we are no longer disconnecting and closing the connection at the end of the GetData routine, the reason is obviously because we intend to send a response to the client so we need to keep the connection open until we've done that.

    Now that we have the data in our local array Buffer (or at least the first 128 bytes of it) we are ready to start analysing it in order to determine what the client wants. Looking back at the screenshot of the received data being printed to the serial terminal we can see that simply browsing to the W5100's IP adress results in the first couple of bytes in the buffer looking like this:
    Code:
    GET / HTTP/1.1
    What we're interested in here is the GET part, that is what the client wants to do - it wants to GET something from our server. The client could have specified a specific resource to get after the first forward slash, like
    Code:
    GET /thatpage.html HTTP/1.1
    But for now we won't look at serving more than a single resource so as soon as we see the word GET we know we should respond with the webpage we've got in the PIC. Here's a very simple routine to parse the buffer, starting at the beginning:
    Code:
    '******************************************************************************************
    ' Very simple routine to parse the data in the local buffer.
    ' If the first word is GET we set a semaphore signaling the main program that we've got
    ' a GET request.
    '******************************************************************************************
    GetRequest VAR BIT
    
    ParseData:
    
    If Buffer[0] = "G" THEN
        IF Buffer[1] = "E" THEN
            IF Buffer[2] = "T" THEN
                IF Buffer[3] = " " THEN
                    GetRequest = 1
                    HSEROUT["Found GET request",13]
                 ENDIF
            ENDIF
        ENDIF
    ENDIF
    
    RETURN
    Once we've found the word GET at the very beginning of the received data we're going to respond with a simple web-page. To do that the first order of business is to determine the amount of free space in the socket transmit buffer and where it wants us to start loading the data. This is done in a similar way to checking the RX-buffer, in fact we're reusing the same variables (WizPtr, WisStart and WizSize) for this purpose.
    Code:
    '*****************************************************************************************************
    ' This routine checks the amount of free space in the sockets transmit register.
    ' It also gets the pointer of where to start loading data and calculates the
    ' physical adress for us. It returns the amount of free space in WizSize and
    ' the adress of the first byte in WizStart. WizPtr is the "raw" pointer value
    ' and is used to update the socket transmit pointer after writing to the buffer.
    '*****************************************************************************************************
    GetFreeTXSize:
     
        ' Get the total amount of free memory in the sockets transmit buffer.
        WizAdress = W5100_S0_TX_FSR0 : GOSUB Read_W5100 : WizSize.HighByte = WizData
        WizAdress = W5100_S0_TX_FSR1 : GOSUB Read_W5100 : WizSize.LowByte = WizData
     
        ' Get the pointer adress of where to put new data.
        WizAdress = W5100_S0_TX_WR0 : GOSUB Read_W5100 : WizPtr.HighByte = WizData
        WizAdress = W5100_S0_TX_WR1 : GOSUB Read_W5100 : WizPtr.LowByte = WizData
     
        'Calculate physical adress of first byte
        WizStart = W5100_S0_TX_START + (WizPtr & W5100_S0_TX_MASK)
     
    RETURN
    When we return from this subroutine the amount of free space is available to us in WizSize, the raw pointer value (working exactly as the pointer into the RX memory buffer) is avaliable to us in WizPtr and the actual, physical, memory adress of the first free location is available to us inWizStart.

    In order to somewhat effeciently load HTML data into the buffer I came up with the following routine. What it does is loading null-terminated strings from the local array Buffer to the sockets TX memory, it handles the circular buffer and it keeps track of the total number of bytes loaded to the buffer so we can tell the W5100 how "far" into the buffer it should send.
    Code:
    '******************************************************************************************
    ' Subroutine to load a string from the local array Buffer to the W5100 socket 0's
    ' TX memory buffer. The string can be any length but must end with NULL.
    '******************************************************************************************
    PutStringInTXBuffer:
     
        Counter = 0                                 ' Counter for indexing the local array we're about to load into the TX buffer.
    
        While Buffer[Counter] <> 0                  ' Last byte in each part is null.
    
        ' Check if we are about to write outside the sockets memory area.
        ' If so we wrap around to the start of the buffer.
    
            If WizStart + TotalCount > W5100_S0_TX_END then 
                WizStart = W5100_S0_TX_Start - TotalCount
            ENDIF
    
        ' Write byte to TX memory buffer.
            WizAdress = WizStart + TotalCount : WizData = Buffer[Counter] : GOSUB Write_W5100
            Counter = Counter + 1             ' Point at the next byte in the array.
            TotalCount = TotalCount + 1             ' Count total number of bytes for the response.
        WEND
    
    RETURN
    We can then "feed" the PutStringInTxBuffer routine with data like in the first half of the HandleGetRequest routine:
    Code:
    '******************************************************************************************
    ' Subroutine used to build a simple web-page in response to a GET request.
    '******************************************************************************************
    HandleGetRequest:
    
        RequestCount = RequestCount + 1
    
        GOSUB GetFreeTXSize
    
        ' Reset the total byte count. This will be incremented in the PutStringInTXBuffer routine
        ' and used to keep track of the total number of bytes loaded to the TX memory buffer.
        TotalCount = 0
    
        HSEROUT ["Loading buffer...",13]
    
        ArrayWrite Buffer,["HTTP/1.0 200 OK",13,10,0]
        GOSUB PutStringInTXBuffer
    
        ArrayWrite Buffer,["Content-Type: text/html",13,10,13,10,0]
        GOSUB PutStringInTXBuffer
    
        ArrayWrite Buffer,["<html><body><title>AMICUS18</title>",13,10,0]
        Gosub PutStringInTXBuffer
    
        ArrayWrite Buffer,["<h1>Embedded Web Server</h1>",13,10,0]
        Gosub PutStringInTXBuffer
    
        ArrayWrite Buffer,["<h3>PIC18F25K20 / W5100 Ethernet chip</h3>",13,10,0]
        GOSUB PutStringInTXBuffer
    
        ArrayWrite Buffer,["<p>Henrik Olsson 2011</p>",13,10,0]
        GOSUB PutStringInTXBuffer
    
        ArrayWrite Buffer,["<a href=",34,"http://www.picbasic.co.uk/forum",34,">Visit the PBP Forum</a>",13,10,0]
        Gosub PutStringInTXBuffer
    
        ArrayWrite Buffer,["<p>This page has been requested ", DEC RequestCount, " times since powerup/reset</p>",13,10,0]
        GOSUB PutStringInTXBuffer
    
        ArrayWrite Buffer,["</body></html>",13,10,13,10,0]
        GOSUB PutStringInTXBuffer
    
        HSEROUT[13, "done...",DEC TotalCount, " bytes.."]
    The above is the actual HTML response that we're sending as a result of the GET request. What's important here is that we must reset the TotalCount variable before starting to load data and then not touch it until we're done. Each "part" of the response must end with NULL as can be seen in the ArrayWrite operations. There's no real requirement to split the response up in a certain way, it's more for the purpose of readabillity.

    Using ArrayWrite like this is a simple aproach but it seems very (VERY) inefficient because each and every character/byte of adds 6 bytes(!) to the footprint of the code. This isn't really a problem for this type of simple web-page but for more complicated stuff it'll add up quickly. We'll have to look into getting the SD card working as a storage "container" for the HMTL pages but that's another topic.

    Once we've got all the data loaded to the TX buffer we're ready to send it and this is handled by the second half of the HandleGetRequest subroutine:
    Code:
        ' Now recalculate the transmit pointer so the W5100 knows where to stop sending. WizPtr currently contains
        ' the adress of the first free byte and TotalCount is the number of bytes of byte we've loaded to the buffer.
        WizPtr = WizPtr + TotalCount
    
        ' Write the updated pointer value to the W5100
        WizAdress = W5100_S0_TX_WR0  : WizData = WizPtr.HighByte : Gosub Write_W5100
        WizAdress = W5100_S0_TX_WR1  : WizData = WizPtr.LowByte : Gosub Write_W5100
    
        ' Issue a SEND command.
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_SEND : GOSUB Write_W5100
    
    WaitForCompletion:
        GOSUB Read_W5100
        If WizData <> 0 then Goto WaitForCompletion
    
        HSEROUT[".sent.",13]
    
        ' Then disconnect from the client.
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_DISCON : GOSUB Write_W5100
     
        ' And close the socket. We'll reopen the socket again in the main routine.
        WizAdress = W5100_S0_CR : WizData = W5100_Sn_CLOSE : GOSUB Write_W5100
        GetRequest = 0
     
    RETURN
    Note that I don't actually verify if the data I'm about to load will fit in the amount of free space provided in the sockets TX memory. Not doing that should be considered bad practice but for now I'll do it anyway since I know that the size of the buffer is more than 6 times the size of the data I'm writing to it.

    Now all we need to do in our main routine is to:
    1) Check the received size register of the socket, if it's >0 we'll handle the received data using the above routines.
    2) Since we're closing the socket after sedning the data it's the main routines job to re-open it and set it to listen mode.
    Code:
    HSEROUT ["Program start",13]
    
    GOSUB Init_W5100   ' Reset W5100 and set MAC, IP, NetMask and Gateway adress, 
    
    Main:
        ' The socket 0 status register automatically switches to SOCK_ESTABLISHED 
        ' when a valid request to connect comes in from a client.
        WizAdress = W5100_S0_SR : GOSUB Read_W5100
     
        If WizData = W5100_SOCK_ESTABLISHED then
            Gosub CheckBufferForData
            If WizSize > 0 then
                GOSUB GetData
                GOSUB ParseData
                If GetRequest = 1 then GOSUB HandleGetRequest
            ENDIF
        ENDIF
     
        ' If the socket is closed (we're closing it in the GetData routine) then
        ' we must re-open it so we are prepared for the next client request.    
        WizAdress = W5100_S0_SR : GOSUB Read_W5100
        If WizData = Socket_CLOSED THEN
            GOSUB Init_Socket_0
        ENDIF
     
    Pause 250
    Goto Main
    To this post I'm attaching the source file (remove the .txt extension and you'll need the W5100_defs.pbp file as well) I used to arrive at the following results:
    Name:  HTTP_2.jpg
Views: 3159
Size:  49.4 KB

    It compiles to 5275 bytes out of which the ArrayWrite statements (with a total of ~340 bytes of HTML) occupies 2038 bytes - yikes. So, without the HTML we're looking at less than 3.5k for the actual server code - not bad.

    /Henrik.
    Attached Files Attached Files

  22. #22
    Join Date
    Feb 2006
    Location
    Gilroy, CA
    Posts
    1,530

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Henrik, I just wanted to thank you for the excellent job you are doing. Very exciting! You have made it so easy for the rest of us. I just received my Wiznet module, the WIZ811MJ, which contains a W5100. So I hope to join the fun when time permits.

    Thanks again.

  23. #23
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Thanks Walter!
    And please do join the fun!

    Perhaps we should emphasise, in case that isn't 100% clear, that it's not needed to have an AMICUS18 board and Ethernet shield, it just happens to be my personal development setup. As long as you have a W5100 connected to a PIC via SPI you're all set - like a WIZ811MJ and your favourite PIC on a piece of perfboard.

    You can even re-write the Read_W5100 and Write_W5100 subroutines if you want to use shiftout/shiftin instead (it'll be kind of slow though....) or if you want to go to the more traditional adress/data interface (requires a boatload of I/O). That's all you'd need to change, all the other routines go "thru" those two so should work just the same.

    What I'm not happy with so far is that all posted code has been hardcoded for socket 0. I have changed it, as discussed in an earlier post, but decided to keep these initial examples hardcoded to keep it easier. Also, I'm NOT happy with the way ArrayWrite literally EATS the codespace, we'd have to come up with a SD card routine or perhaps one of the strings in codespace routines mixed with a couple of ArrayWrite for the dynamic data.

    Oh well, one step at a time...

    /Henrik.

    PS. DHCP is now working nicely, with proper handling of leasetime and renewal of lease. Very cool but it adds about 3kB and 42 bytes of RAM to the total footprint when included, I'll try to get that down a bit further.

  24. #24
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi everyone,
    Time for an update. As I wrote in the previous post I've had DHCP working nicely and today I thought it was time to show you how it can be used. However, for this to work nicely, there had to be some changes made to the overall structure of the package of files used. It gets easier to USE but a tad more complicated to maintain. I won't go into specific details, just give you the overall picture.


    First of all, the code now requires PBP3. This is beacuse I've added a bunch of conditional debug statements in the code making use of the wonderful conditional compilation feature of PBP3. For example, you can add #W5100_DEBUG_LEVEL 1 to the top of the code and you'll get a whole bunch of data sent over the USART, if you want even more details do #W5100_DEBUG_LEVEL 2. Do note though that these takes up a lot of program space so if the application grows they might not fit when "activated".


    The main include file for using the W5100 is called W5100.pbp and that's what you should included from the main program/application. It is also in THIS file that you may need to make some changes to match your project:

    1) BufferLenght CON 256 - This is the length of the local buffer (array) that gets declared, you can change this to match your needs. Don't make it too small though, the DHCP routines - if you're going to use those, requires a lenght of ~48 minimum.

    2) MAC-adress - This is the hardware adress of the device, change as needed - and please DO change it, don't use mine! As can be seen the MAC adress is defined as constants and can therefor currently not be changed at runtime - this MAY change or you can change it yourself.

    3) W5100_Select VAR PortB.5 - this is the PIC-pin to which ChipSelect pin of the W5100 is connected. My modified Arduino Ethernet Shield uses PortB.5

    This file (W5100.pbp) then includes two more files - W5100_defs.pbp (which we've looked at before) and W5100_subs.pbp which contains several of the routines discussed in earlier posts as well as others. Neither of these files 'should' need to be edited.


    In previous "exercises" we've hardcoded the IP-adress, netmask and gateway address of the W5100 - for DHCP to work properly this can't be done. So I've declared three arrays (in W5100_subs.pbp) called W5100_IP, W5100_Netmask and W5100_Gateway - all 4 bytes long. To set the network parameters of the W5100 we can now do something like:
    Code:
    W5100_IP[0] = 192
    W5100_IP[1] = 168
    W5100_IP[2] = 1
    W5100_IP[3] = 100
    
    W5100_NetMask[0] = 255
    W5100_NetMask[1] = 255
    W5100_NetMask[2] = 255
    W5100_NetMask[3] = 0
    
    W5100_Gateway[0] = 192
    W5100_Gateway[1] = 168
    W5100_Gateway[2] = 1
    W5100_Gateway[3] = 254
    
    GOSUB Init_W5100
    The Init_W5100 subroutine uses the above arrays and sets the parameters of the W5100 accordingly.

    To be continued in the next post....
    Last edited by mackrackit; - 2nd October 2011 at 17:11.

  25. #25
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    ...continued from previous post.

    OK, Dynamic Host Configuration Protocol - DHCP.
    DHCP is used to automatically configure a networkdevice without actually knowing anything about the network it's attached to. There are hundreds and hundreds of pages to read about how it works so I won't cover it in much detials, just give a basic overview.


    The process starts with the client sending out what's called a DHCP Discover message. This is sent as a brodcast message meaning it's addressed to any and all DHCP servers on the current subnet. One, or more, servers(s) will respond with a DHCP Offer message containing an offer for a lease of a specific IP adress for a specific amount of time. The client can then selects one of these offers and sends a DHCP request message, still as a broadcast message but it has now filled in the server it wants to use in a specific field in the message. If everything goes well the server responds with a DHCP Ack message meaning that it has now reserved the IP-adress for the amount of time specified and the client is free to use it. The client is NOT allowed to start using the IP-adress untill a valid DHCP Ack message is received. Along with the IP adress the server also provides other important setting such as the netmask and the gateway adress.

    Like I said, this is just the basic outline of the process - there are alot of details to get into, which we don't have to do right now.


    The DHCP implementation for the W5100/PBP3 is supplied as three files. The main file to include in your project is DHCP.pbp It's also in this file that a change or two may be needed. At the top, you'll find this:
    Code:
    HostNameLength          CON     6           ' <- Change this to match the length of the name.
    HostName                VAR     BYTE[HostNameLength]
    HostName[0] = "A"
    HostName[1] = "m"
    HostName[2] = "i"
    HostName[3] = "c"
    HostName[4] = "u"
    HostName[5] = "s"
    This is the host name that is supplied to DHCP server. It then (if it has the functionallity) passes it on the DNS server and the end-result is that the client device can be accessed by name, ie you can browse to http://amicus instead of trying to remeber the IP-adress. What you need to change is the HostNameLenght so that it matches exactly the lenght of the array containing the name you want to use. As in the example above the name is 6 bytes long therefor HostNameLenght is 6.

    The DHCP.pbp file then includes two more files, DHCP_subs.pbp and DHCP_defs.pbp - neither of which 'should' need any editing for normal use.

    As with the main "driver" files for the W5100 I've added a while bunch of debug-messages to the DHCP code. These can be enabled by adding a #DEFINE DHCP_DEBUG_LEVEL 1 or #DEFINE DHCP_DEBUG_LEVEL 2 to the application. Again, these adds quite a bit of code.

    When the DHCP process is complete we can get the network setting to use from the arrays DHCP_IP, DHCP_NetMask and DHCP_GateWay and use these to initilise the W5100.

    To be continued in the next post....

  26. #26
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    ...continued from previous post...

    OK, lets write some code....(finally)....

    The first thing to do, as always is to set up the hardware. I'm using the AMICUS18 and the modifed Arduino Ethernet Shield, here's the first part of the code:
    Code:
    DEFINE OSC 64                       ' Default oscillator speed on the AMICUS18 is 16*4MHz
    DEFINE LOADER_USED 1                ' We're using a bootloader.
    DEFINE HSER_RCSTA 90h               ' Enable serial port & continuous receive
    DEFINE HSER_TXSTA 24h               ' Enable transmit, BRGH = 1
    DEFINE HSER_CLROERR 1               ' Clear overflow automatically
    DEFINE HSER_SPBRG 138               ' 115200 Baud @ 64MHz, -0,08%
    
    SPBRGH = 0
    BAUDCON.3 = 1                       ' Enable 16 bit baudrate generator
    
    TRISC = %10010110                   ' RX, TX, MOSI, MISO, SCK, N/U, N/U, LED 
    TRISB = %11011011                   ' N/U, N/U, W5100_CS, N/U, N/U, SD_CS, N/U, W5100_Interrupt
    
    ' Aliases for the MSSP module status and control bits.
    SSPEN           VAR SSPCON1.5       ' SSP Enable bit  
    CKP             VAR SSPCON1.4       ' Clock Polarity Select  
    SMP             VAR SSPSTAT.7       ' Data input sample phase  
    CKE             VAR SSPSTAT.6       ' Clock Edge Select bit  
    SSPIF           VAR PIR1.3          ' SPI interrupt flag
    
    milliseconds    VAR WORD            ' Timer variable, used in the ISR.
    TMR1_Temp       VAR WORD            ' Used when reloading the timer in the ISR
    i               VAR BYTE            ' General purpose.
    
    CLEAR
    
    ' Set up the MSSP module.
    CKP = 0                             ' Clock idles low  
    CKE = 1                             ' Transmit on active to idle transition.  
    SSPIF = 0                           ' Clear buffer full status  
    SMP = 0                             ' Sample in middle of data 
    SSPEN = 1                           ' Enable SPI pins
    SSPCON1.0 = 1                       ' Slow down SPI clock to Fosc/16 for now.
    Then we need some what to keep track of time. The DHCP server leases us an IP-adress for a finite (usually) amount of time and we need to keep track of that time so we can renew the lease before the time runs out. For this purpose I'm using Darrels DT-Ints so I'm including his files and while I'm at it I'll also include the W5100.pbp and the DHCP.pbp files and the set up an interrupt to trip when TMR1 overflows:
    Code:
    INCLUDE "DT_Ints-18.bas"            ' <- Darrel Taylores interupt engine
    INCLUDE "ReEnterPBP-18.bas"         ' <- Part of Darrels interrupt engine.
    INCLUDE "W5100.pbp"                 ' <- Main W5100 "driver" and auxiallary routines.
    INCLUDE "DHCP.pbp"                  ' <- Main file for the DHCP engine.
    
    '--- Set up TMR1 interupt.
    ASM
    INT_LIST  macro    ; IntSource,     Label,       Type, ResetFlag?
            INT_Handler     TMR1_INT, _Tick,    PBP,   yes
        endm
        INT_CREATE               ; Creates the interrupt processor
    ENDASM
    
    TMR1_Reload CON 25539                           ' Reload value for 200Hz @64MHz, 1:2 prescaler
    T1CON.0 = %00010001                             ' Start timer 1, 1:2 prescaler
    @ INT_ENABLE TMR1_INT
    I've put my TMR1 Interrupt service routine at the end of the program but to keep the disussion on track I'll show it here:
    Code:
    ' This is the TMR1 interrupt service routine.
    Tick:
        milliseconds = milliseconds + 5
    
        If milliseconds => 1000 THEN
            DHCP_Seconds = DHCP_Seconds + 1
            milliseconds = 0
        ENDIF
    
        T1CON.0 = 0
    
        TMR1_Temp.HighByte = TMR1H
        TMR1_Temp.LowByte = TMR1L
        TMR1_Temp = TMR1_Temp + TMR1_Reload
        TMR1H = TMR1_Temp.HighByte
        TMR1L = TMR1_Temp:LowByte
    
        T1CON.0 = 1
    @ INT_RETURN
    This runs at ~200Hz, each time it increments the millisecond variable by 5 and when it reaches 1000 (1 second) the variable DHCP_Seconds is incremented - and this is the important thing. The DHCP code must be called periodically, even after an IP-adress had been recieved. This is because it has to keep track of the lease time which it does by subtracting the number of seconds on DHCP_Seconds from the total lease time given to us by the server. It then resets DHCP_Seconds to 0.

    So, "we" must make sure that DHCP_Seconds contains "true time" and GOSUB DHCP periodically. We don't have to increment DHCP_Seconds at exactly 1Hz, we can increment it by 10 every 10 seconds or whatever but it's impotant that it's fairly close to real time. Likewise, we don't have to GOSUB DHCP every second or even every minute but because DHCP_Seconds is a BYTE variable we must atleast do it once per 4 minutes or there abouts.

    Next we initilise the W5100 and set the network parameters to "all zeros":
    Code:
    Begin:
        HSEROUT["Program start...", 13]
        ' A substantial pause is needed for the W5100 to start up and establish
        ' a link with the network. 
        Pause 2000                                  
       
        ' We don't know any network parameters so these should all be set to 0
        ' when using DHCP.
        HSEROUT["Clearing IP, Netmask and Gateway adress",13]
        For i = 0 to 3
            W5100_IP[i] = 0         ' Set our IP adress to 0.0.0.0
            W5100_Netmask[i] = 0    ' Set our netmask to 0.0.0.0
            W5100_GateWay[i] = 0    ' Set gatway adress to 0.0.0.0
        NEXT
        ' Now initilise the W5100 with the above parameters.
        GOSUB Init_W5100
    There's a bit variable called DHCP_Enabled which should be set to 1 if DHCP is to be used. There might be ocations where we want to have the abbility to use DHCP but also to turn it off, this flag makes sure that the DHCP code doesn't execute, even if called, if not specifically "enabled". Then we must tell the DHCP code which of the W5100's four sockets to use, this is done thru the DHCP_Socket variable. Finally we set the state of the DHCP state machine (it's all implemented as a big state machine by the way) to DHCP_State_Start.

    Now, all we need to to is keep GOSUB'ing DHCP periodically and if everything goes well we'll have all the network parameters we need within a second or three:
    Code:
        DHCP_Enabled = 1                            ' Enable DHCP. 
        DHCP_Socket = 2                             ' Use socket 2 for DHCP.
        DHCP_State = DHCP_State_Start               ' Reset the DHCP state machine.
    
        ' Loop around here until we recieve a valid IP adress.
        HSEROUT["Entering DHCP",13]
        While DHCP_State <> DHCP_State_Bound
            GOSUB DHCP
            Pause 500
        WEND
    The key to detect when the process is finished is that the state variable (DHCP_State) is set to DHCP_State_Bound and that's what weäre using here. We keep calling DHCP untill we see the correct state. As long as we also updates DHCP_Seconds in the background the DHCP state machine will automatically issue retransmissions at specific intervals (as per the DHCP specification).

    Once we see the bound state we can get the parameters from the DHCP_IP, DHCO_NetMask and DHCP_GateWay arrays and use those to re-initilise the W5100:
    Code:
        ' At this point we should have received a set of valid network paramters from
        ' the DHCP server on the network. Let's start by simply printing them out.
            HSEROUT["The offered IP adress is: "]
            For i = 0 to 3
                HSEROUT[#DHCP_IP[i], "."]
            NEXT
        
            HSEROUT[13,"The gateway is: "]
            For i = 0 to 3
                HSEROUT[#DHCP_Gateway_IP[i], "."]
            NEXT
        
            HSEROUT[13, "The netmask is: "]
                For i = 0 to 3
                HSEROUT[#DHCP_NetMask[i], "."]
            NEXT
        
        ' The DHCP engine does NOT actually SET the IP adress for us, it just TELLS
        ' us what to use. We must make the changes ourselves:
        HSEROUT[13,"Setting the W5100 parameters..."]
        For i = 0 to 3
            W5100_IP[i] = DHCP_IP[i]
            W5100_GateWay[i] = DHCP_GateWay_IP[i]
            W5100_NetMask[i] = DHCP_NetMask[i]
        NEXT
    
    
        ' Now re-initilise the W5100 with the new network parameters.
        GOSUB Init_W5100
        HSEROUT["DONE!",13]
        HSEROUT["Try pingning by IP or name!",13]
    Don't forget that we still need to have DHCP_Seconds updated in the ISR and we still have to GOSUB DHCP on a regular basis so that it can automatically renew the IP adress lease when it's about to expire:
    Code:
    Main:
     Toggle PortC.0
     Pause 500
     GOSUB DHCP
    Goto Main
    Name:  DHCP_1.jpg
Views: 2936
Size:  47.5 KB

    Name:  DHCP_2.jpg
Views: 3013
Size:  53.9 KB
    Attached to this post are all the updated files (W5100.pbp, W5100_defs.pbp, W5100_subs.pbp, DHCP.pbp, DHCP_defs.pbp, DHCP_subs.pbp and the DHCP_Demo application used to get the above results.

    /Henrik.
    Attached Files Attached Files

  27. #27
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    In post #24 above, second paragraph regarding the conditional debug output. Those two statements should of course read:
    #DEFINE W5100_DEBUG_LEVEL 1 or #DEFINE W5100_DEBUG_LEVEL 2

    As usual we're not allowd to correct such mistakes if not imediatley detected :-(

  28. #28
    Join Date
    Nov 2003
    Location
    Wellton, U.S.A.
    Posts
    5,924

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    WOW!!!
    Nice work Henrik.
    Dave
    Always wear safety glasses while programming.

  29. #29
    Join Date
    Sep 2009
    Posts
    674

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    Nice work. But i think that you have some mistake.
    You wrote
    Code:
    W5100_Gateway[0] = 192
    W5100_Gateway[0] = 168 
    W5100_Gateway[0] = 1 
    W5100_Gateway[0] = 254
    If i understand it should be
    Code:
    W5100_Gateway[0] = 192
    W5100_Gateway[1] = 168 
    W5100_Gateway[2] = 1 
    W5100_Gateway[3] = 254

  30. #30
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Ah, yes of course, thanks for spotting that. It should be OK in the example project attached to previous message.
    I wish I could go back and fix it in the post but I can't :-(

    /Henrik.

  31. #31
    Join Date
    Sep 2009
    Posts
    674

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Maybe you should be moderator on AMICUS18 section...

  32. #32
    Join Date
    Nov 2003
    Location
    Wellton, U.S.A.
    Posts
    5,924

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    The code in post #24 is fixed.
    Dave
    Always wear safety glasses while programming.

  33. #33
    Join Date
    Jan 2005
    Location
    Montreal, Quebec, Canada
    Posts
    2,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Henrik, Bravo!

    Robert

  34. #34
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hey Henrik (and others),
    I just received my Amicus18 and the Arduino Ethernet Shield...

    I have not completed the modifications yet... but am starting that tonight.

    I assume your example code is intended to overwrite the Amicus boot loader? (I did read the amicus with my PicKit2 and save the hex file, so I think I can go back to the boot loader if I want.)

    How has your progress been?? Where are you hoping to go with your experiments?

    I would like to end up with a device that...
    I can log into and read or view values like temperature, voltage status bits etc.
    That could possible email me or text me if a given value changes beyond a set point.
    Be able to remotely change values and bits.
    ?? other things yet un thought up.

    I do not have any experience programing ethernet values via a PIC.

    I have plenty of experience Pinging... seting static IP... subnet mask... Telnet... etc. From a Windows XP machine.

    Thanks for any help, direction and code examples you (and others) can give.
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  35. #35
    Join Date
    Oct 2009
    Location
    Utah, USA
    Posts
    427

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Henrik,

    My Amicus board came with a 18F25K20 NOT the 18F25K22 that you mention in your article. I see the difference is 3.3v vs 5v. Am I going to need to get the K22 chip?

    Thanks
    Dwight
    These PIC's are like intricate puzzles just waiting for one to discover their secrets and MASTER their capabilities.

  36. #36
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi Dwight,
    Wonderful, please join the fun!

    No, I wrote 25K22 in error, it's meant to say 25K20 so you're fine - just use the AMICUS18 as it is. (You CAN definitely use the 25K22 if you want but I think you'd need to run it on 3.3V, I don't remember if the W5100 has 5V tolerant inputs). I'd like to stress, again, that you don't need an AMICUS18 at all, the routines will work with ANY PIC that has a MSSP module.

    No, don't overwrite/replace the bootloader. Add DEFINE LOADER_USED 1 at the top and set MCSP (or whatever you use) up to use the AMICUS Loader. It'll be a lot faster than re-flashing with the PicKit2.

    Progress is good but a bit slow. This is all new territory for me, researching protocols, reading RFC's, trying, failing, trying again.

    The goal is basically a set of routines, lets call it a "framework", which you can use to build the application you need. It won't do everything but it'll provide the base functionallity upon which we can write the actual application. At the moment I have the basic routines used to communicate with the W5100 and a couple of support routines to make using sockets and moving data in and out of easier. This is allready presented and published in the thread.

    I have DHCP (Dynamic Host Configuration Protocol) working - also presented in the thread.
    I have SNTP (Simple Net Time Protocol) working, (derived from Charles really accurate clock code but without using LONGs)
    I have SMTP (Simple Mail Transfer Protocol) (with authentication) basically working (it sends mails) but it needs more work to increase the robustness and improve the interface to the main application.

    When I'm happy with the SMTP code I think I'll take on DNS and if/when that works I think I'll try to make some kind of HTTP server "building block" but I'm not sure it'll work since each and every application will be different. Perhaps it's just better to write that from scratch - I don't know. The biggest hurdle, as I see it, is how to handle a "filesystem" of some sort on the PIC. A single, very simple web-page is not a problem but it gets complicated when you want multiple pages, perhaps images etc. Actually storing them (SD-card, SPI-flash, I2C-EEPROM whatever) is one part, keeping track of them (ie. the filesystem) is another and how to get them INTO the memory device is a third.

    The application you envision is pretty much what I had in mind when I started but I'm trying to write the code as generic as possible. That way you can included the functionallity you need and leave the rest out. With the code and examples already presented you should be able to get DHCP and a simple web-server going. Perhaps you'll get a general HTTP server building block going before I get to it.

    Keep us posted on your progress!

    /Henrik.

  37. #37
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi,
    Just thought I'd give those with interest in this project a short update of what I've been doing.

    Take a look at the following screenshot:

    Name:  W5100_Test.jpg
Views: 3016
Size:  124.8 KB

    The screenshot itself is pretty self explainatory but I'll do a short explanation of what is happening (it's quite a bit).

    At bootup the DHCP code is executed to retrieve the network parameters from the DHCP server, then the W5100 is configured with those parameters. Then the SNTP code is executed to retrieve current time from an SNTP server on the net. Then a series of DNS queries are run, looking up the IP-adress of some well known URL's. The last URL that DNS queries is the SMTP server for my email account. Once it gets that IP-adress it uses it in the SMTP code to login to my account, using authentication (no encryption just AUTH LOGIN (BASE64 encoded username and password)), and sends an email to my account - which you can see next to the serial terminal.


    There is one thing I don't get though. Browsing to to the IP-adress retrieved by the DNS code gets me to the correct places in all but ONE instances, namely THIS SITE. If I PING www.picbasic.co.uk I get the same IP as my DNS code and if I run Wireshark and browse to www.picbasic.co.uk its DNS-records shows the same IP-adress but if I browse to that very same IP I do not end up here. I'm pretty sure it's not an error in my code since every other URL I've tried seems to work. It has to be something specific with where and how this site is hosted. Anyone know, and can explain, what's going on?

    Anyway the DNS code currently "only" handles normal queries, not reversed or any other type, like MX etc. It does handle both direct type A responses and CNAME responses, either single or "nested". For example, the query for microsoft results in a response with no less than three CNAME records (aliases) before finally arriving at the IP-adress shown.

    That's it, just wanted to show you what's going on at my end. If anyone else is doing anything cool, please tell!

    /Henrik.
    Last edited by HenrikOlsson; - 25th October 2011 at 19:19.

  38. #38
    Join Date
    Nov 2003
    Location
    Wellton, U.S.A.
    Posts
    5,924

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    It has to be something specific with where and how this site is hosted. Anyone know, and can explain, what's going on?
    Shared/virtual machine/site.
    Dave
    Always wear safety glasses while programming.

  39. #39
    Join Date
    Feb 2011
    Location
    South Carolina, USA
    Posts
    7

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Henrik, have you been able to work on this project any more?

    john

  40. #40
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,253

    Default Re: Using the Arduino Ethernet shield with AMICUS18 (Joint forum project?)

    Hi John,
    Yes, a little bit. I've been working on external SPI flash routines for storing HTML-data and use "dynamic variables" in the HTML. This now works fairly well and I'm working on a way to use MPFS2 images. As it stands I can read the FAT table and access the files alright but I'm trying to figure out the correct/best way to have more than one file open at a time. I tend to be a bit (perhaps a bit too) conservative when it comes to using RAM and I try to make the most out of it. Having multiple file "open" seems to be quite a hog in that respect but perhaps I'll just have to accept that and just get it working.

    (It would be nice to have proper functions and local variables in PBP)

    Is there anything in particular you're trying/want to do or thinking about?

    /Henrik.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts