<html>\nThe choice of the <em>IP address</em> depends on the network in which the device is used, therefore it can be easily modified in the\nfile <em>StackTsk.h</em>, changing the following defines:\n<pre class="code"> // example: 10.0.0.6\n#define MY_DEFAULT_IP_ADDR_BYTE1 (10)\n#define MY_DEFAULT_IP_ADDR_BYTE2 (0)\n#define MY_DEFAULT_IP_ADDR_BYTE3 (0)\n#define MY_DEFAULT_IP_ADDR_BYTE4 (6)</pre>\nIn the same way, it is also possible to change the <em>MAC address</em>:<br />\n<pre class="code"> // example: 00:04:A3:00:00:00\n#define MY_DEFAULT_MAC_BYTE1 (0x00)\n#define MY_DEFAULT_MAC_BYTE2 (0x04)\n#define MY_DEFAULT_MAC_BYTE3 (0xA3)\n#define MY_DEFAULT_MAC_BYTE4 (0x00)\n#define MY_DEFAULT_MAC_BYTE5 (0x00)\n#define MY_DEFAULT_MAC_BYTE6 (0x00)</pre>\nThe IP address can automatically be assigned by a DHCP server. To do that, uncomment the following line in file <em>StackTsk.h</em>:\n<pre class="code">//#define STACK_USE_DHCP</pre>\n\n<br />\nIn the example, LEDA and LEDB pins are used like general purpose I/O pins, so the ETHLED config word is set to FALSE.\nIf you want to use Ethernet LEDs, just turn ON that config.\n<pre class="code">#pragma config XINST=OFF, WDT=OFF, FOSC2=ON, FOSC=HSPLL, ETHLED=ON</pre>\n<br />\nNow, the TCP/IP stack can be compiled and loaded into the PIC.<br />\n<br />\n</html>
[img[DIP40|DIP40Socket.jpg]]
FTPmicro\n[[Schematic]]\nPinOut
config.options.chkHttpReadOnly = true;
<html>\nLet's see a real-world example of how to read, in real-time, the temperature from the on-board sensor and toggle two LEDs, from a single HTML (+ JavaScript) page.<br />\n<br />\nIn this example, <a target="_blank" href="http://en.wikipedia.org/wiki/AHAH" title="AHAH">AHAH</a> (Asynchronous HTTP And HTML) is used to send asyncronous\nHTTP requests. <br />\nThe CGI file we are going to use (<em>status.cgi</em>) is very simple; it has an HTML table with some variables in it, like the temperature and the current\nstatus of the LEDs.\n<pre class="code">&lt;table class=&quot;status_table&quot;&gt;\n &lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Temperature&lt;/th&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;td&gt;System&lt;/td&gt;&lt;td&gt;%00&deg;C&lt;/td&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;LED Status&lt;/th&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;td&gt;LED 1&lt;/td&gt;&lt;td&gt;%01&lt;/td&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;td&gt;LED 2&lt;/td&gt;&lt;td&gt;%02&lt;/td&gt;&lt;/tr&gt;\n&lt;/table&gt;\n</pre>\nUsed variables are:\n<ul>\n <li>00: the temperature;</li>\n <li>01: the first LED;</li>\n <li>02: the second LED;</li>\n</ul>\nThis page will be requested periodically to know the system status. <br />\nNow we are going to see the main page (<em>index.htm</em>), that contains two buttons to control the LEDs and, thanks to AHAH, it will also contain\nthe CGI page (loaded into the <em>tempdiv</em> div).\n<pre class="code">&lt;html&gt;\n &lt;head&gt;\n &lt;title&gt;FTPMicro: Temperature Example&lt;/title&gt;\n &lt;script type=&quot;text/javascript&quot; src=&quot;/ahah.js&quot;&gt;&lt;/script&gt;\n &lt;script type=&quot;text/javascript&quot;&gt;\n function refresh() {\n if (lo) return true; // stop loading when another request is in progress\n noloadAHAH('/Status.cgi','tempdiv','GET'); // request status.cgi and then load it into "tempdiv"\n }\n window.setInterval(&quot;refresh()&quot;,2000);\n &lt;/script&gt;\n &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;all&quot; href=&quot;/style.css&quot; /&gt;\n &lt;/head&gt;\n &lt;body&gt;\n &lt;h1&gt;FTPMicro&lt;/h1&gt;\n &lt;div class=&quot;bar&quot;&gt;\n The world smallest WebServer and FtpClient with DHCP and UDP features\n &lt;/div&gt;\n &lt;div id=&quot;tempdiv&quot; class=&quot;left&quot;&gt;\n Loading...\n &lt;/div&gt;\n &lt;div class=&quot;left&quot;&gt;\n &lt;form&gt; \n &lt;table class=&quot;status_table&quot;&gt;\n &lt;tr&gt;&lt;th&gt;LED Toggle&lt;/th&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;td&gt;&lt;input type=&quot;submit&quot; value=&quot;LED 1&quot; onclick=&quot;javascript: noloadAHAH('/Status.cgi?t=1','tempdiv','GET'); return false;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;\n &lt;tr&gt;&lt;td&gt;&lt;input type=&quot;submit&quot; value=&quot;LED 2&quot; onclick=&quot;javascript: noloadAHAH('/Status.cgi?t=2','tempdiv','GET'); return false;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;\n &lt;/table&gt;\n &lt;/form&gt;\n &lt;/div&gt;\n &lt;/body&gt;\n&lt;/html&gt;\n</pre>\nThis page is fairly short, but maybe you can't understand how AHAH is used!<br />\nIn the page header, there is a Javascript that, using the <em>setInterval</em> function,\nrefreshes the CGI page loaded in tempdiv, at regular intervals.<br />\nAnother important sections is the buttons' one; the use AHAH to send command without refreshing the page.\nIn this example, the parameters "t" is used to identify a "toggle" command, and it is followed by the value of the LED to toggle; so <em>/Status.cgi?t=1</em> is\nthe command for the first LED, and <em>/Status.cgi?t=2</em> for the second one. After the command has been executed, the requested page is returned (<em>status.cgi</em>)\nand it will be always loaded into his div.<br />\nIn the example, a CSS file is used to define the style, but it's not indispensable.<br />\n<br />\n<div align="center"><img src="http://www.itentropy.it/images/tutorials/http_example.jpg" alt="" /></div>\nBelow, there are the methods <em>ProcessIO</em>, <em>HTTPExecCmd</em> e <em>HTTPGetVar</em>; here the first:\n<pre class="code">\nstatic char Temperature[8];\n\nstatic void ProcessIO(void)\n{\n signed long temp;\n\n // Start A/D conversion\n ADCON0bits.GO = 1;\n\n // Wait until A/D conversion is done\n while(ADCON0bits.GO);\n\n temp = (long)(*(WORD*)(&ADRESL)) * 322 - 50000; // not a precise conversion...\n itoa(temp/1000, Temperature);\n}</pre>\nThis method is called periodically, when the stack is not doing anything else. In this example,\nthe function does an A/D conversion, then it converts the tension to a temperature, and finally the temperature is put in a string.<br />\n<br />\n<pre class="code">#define VAR_TEMPERATURE (0x00)\n#define VAR_LED1 (0x01)\n#define VAR_LED2 (0x02)\n\n#define CMD_LED1 (0x1)\n#define CMD_LED2 (0x2)</pre>\nThese defines are used do set the value to varibles and commands.<br />\n<pre class="code">void HTTPExecCmd(BYTE** argv, BYTE argc)\n{\n if (argv[1][0] == 't') {\n switch (argv[2][0] - '0') {\n case CMD_LED1 :\n LED1_IO = !LED1_IO;\n break;\n case CMD_LED2 :\n LED2_IO = !LED2_IO;\n break;\n }\n }\n}</pre>\nWe have seen that the only used command is "status.cgi?t=x", where <em>x</em> is the LED to toggle.\nWith this command, when <em>HTTPExecCmd</em> is invoked, the first element of <em>argv</em> will be "status.cgi", and it is left unchanged;\nthe second item will e "t" (the if statement is used to assure that); finally, the third one will be the number of the LED in an ASCII character, so we have\nto convert it in an integer subtracting '0' from it.<br />\n<pre class="code">WORD HTTPGetVar(BYTE var, WORD ref, BYTE* val)\n{\n switch (var) {\n case VAR_LED1 :\n *val = LED1_IO ? '1' : '0';\n break;\n \n case VAR_LED2 :\n *val = LED2_IO ? '1' : '0';\n break;\n\n case VAR_TEMPERATURE:\n *val = Temperature[(BYTE)ref];\n if(Temperature[(BYTE)ref] == '\s0')\n return HTTP_END_OF_VAR;\n else if(Temperature[(BYTE)++ref] == '\s0' )\n return HTTP_END_OF_VAR;\n return ref;\n default : break;\n }\n return HTTP_END_OF_VAR;\n}</pre>\nThe <em>HTTPGetVar</em> method returns (into <em>val</em>) a LED status or the string <em>Temperature</em>; in the first case\nwe only have to assing '0' or '1' to <em>val</em>, while in the second case, we have to assign to <em>val</em> one byte of the string at\na time.<br />\n<br />\nIn the source code there are some defines like <em>LEDx_IO</em>, that you can find in <em>Compiler.h</em>:\n<pre class="code"> #define LED1_IO (LATAbits.LATA0)\n #define LED1_TRIS (TRISAbits.TRISA0)\n #define LED2_IO (LATAbits.LATA1)\n #define LED2_TRIS (TRISAbits.TRISA1)</pre>\nIn this example, Ethernet LEDs are used, but you can use any I/O pin by changing that defines.<br />\n<br />\n</html>
<html>\nTo store files in the SD card, a FAT Filesystem is used, so they can be written directly from a PC.<br />\nThe FAT16 driver is implemented by the file <em>fat16.c</em>, using the same interface of the MPFS filesystem (read only for now).\nThis means that the FAT16 methods have the same name, parameters and behaviour of the ones in <em>MPFS.c</em>.<br />\n<br />\nTo understand how the code works, we are going to see how the FAT16 is made up.<br />\nFirst of all, the memory is divided in <em>sectors</em> of 512 bytes; <em>n</em> sectors form a <em>cluster</em>,\nwhere <em>n</em> is computed and written in a certain position of the memory when this last is being formatted.\nThe <em>cluster</em> is the base unit of FAT16; as an example, a file occupies an entire cluster even though it is empty.<br />\nThe first absolute sector is the <em>MBR</em> (Master Boot Record), and it principally contains executable code to boot the OS.\nBut we are interested in a little portion of that sector, that is the <em>Partition Table</em>,\nwhere there are informations of the partitions.\nParticurarly, there we found the starting sector of the filesystem.\n<table class="simple_table" style="width:500px">\n <tr>\n <td><b>Offset</b></td>\n <td><b>Description</b></td>\n <td><b>Size</b></td>\n </tr>\n\n <tr>\n <td>0x000</td>\n <td>Executable Code</td>\n <td>446 Bytes</td>\n </tr>\n <tr>\n <td>0x1BE</td>\n <td>First Partition Entry</td>\n <td>16 Bytes</td>\n </tr>\n <tr>\n <td>0x1CE</td>\n <td>Second Partition Entry</td>\n <td>16 Bytes</td>\n </tr>\n <tr>\n <td>0x1DE</td>\n <td>Third Partition Entry</td>\n <td>16 Bytes</td>\n </tr>\n <tr>\n <td>0x1EE</td>\n <td>Fourth Partition Entry</td>\n <td>16 Bytes</td>\n </tr>\n <tr>\n <td>0x1FE</td>\n <td>Boot sector signature</td>\n <td>2 Bytes</td>\n </tr>\n</table>\n<br />\nEach partition entry contains:\n<table class="simple_table" style="width:500px">\n <tr>\n <td><b>Offset</b></td>\n <td><b>Description</b></td>\n <td><b>Size</b></td>\n </tr>\n <tr>\n <td>0x00</td>\n <td>Partition state (active/inactive)</td>\n <td>1 Byte</td>\n </tr>\n <tr>\n <td>0x01</td>\n <td>Starting Head</td>\n <td>1 Byte</td>\n </tr>\n <tr>\n <td>0x02</td>\n <td>Starting Cylinder/Sector </td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x04</td>\n <td>Partition Type</td>\n <td>1 Byte</td>\n </tr>\n <tr>\n <td>0x05</td>\n <td>Ending Head</td>\n <td>1 Byte</td>\n </tr>\n <tr>\n <td>0x06</td>\n <td>Ending Cylinder/Sector</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td><b>0x08</b></td>\n <td><b>Starting address in sectors</b></td>\n <td><b>4 Bytes</b></td>\n </tr>\n <tr>\n <td>0x0C</td>\n <td>Size of the partition in sectors</td>\n <td>4 Bytes</td>\n </tr>\n</table>\nSo, the item we are interested in is at the address 0x1C6 of the MBR.<br />\n<br />\nAt this point, we are able to read the first sector of the FAT16 (the <em>Boot Record</em>), where there are\nmany useful informations that we have to save somewhere.\n<table class="simple_table" style="width:500px">\n <tr>\n <td><b>Offset</b></td>\n <td><b>Description</b></td>\n <td><b>Size</b></td>\n </tr>\n <tr>\n <td>0x000</td>\n <td>Jump Code & NOP</td>\n <td>3 Bytes</td>\n </tr>\n <tr>\n <td>0x003</td>\n <td>OEM Name</td>\n <td>8 Bytes</td>\n </tr>\n <tr>\n <td>0x00B</td>\n <td>Bytes Per Sector</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td><em>0x00D</em></td>\n <td><em>Sectors Per Cluster</em></td>\n <td><em>1 Byte</em></td>\n </tr>\n <tr>\n <td><em>0x00E</em></td>\n <td><em>Reserved Sectors</em></td>\n <td><em>2 Bytes</em></td>\n </tr>\n <tr>\n <td><em>0x010</em></td>\n <td><em>Number of FATs</em></td>\n <td><em>1 Byte</em></td>\n </tr>\n <tr>\n <td><em>0x011</em></td>\n <td><em>Max entries in Root Directory</em></td>\n <td><em>2 Bytes</em></td>\n </tr>\n <tr>\n <td>0x013</td>\n <td>Partition size in sectors (&lt; 32MB)</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x015</td>\n <td>Media Descriptor (HardDisk, ...)</td>\n <td>1 Byte</td>\n </tr>\n <tr>\n <td><em>0x016</em></td>\n <td><em>Sectors Per FAT</em></td>\n <td><em>2 Bytes</em></td>\n </tr>\n <tr>\n <td>0x018</td>\n <td>Sectors Per Track</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x01A</td>\n <td>Number of Heads</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x01C</td>\n <td>Number of Hidden Sectors</td>\n <td>4 Bytes</td>\n </tr>\n <tr>\n <td>0x020</td>\n <td>Partition Size in Sectors</td>\n <td>4 Bytes</td>\n </tr>\n <tr>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <td>0x1FE</td>\n <td>Boot Sector Signature: always 0x55AA</td>\n <td>2 Bytes</td>\n </tr>\n</table>\n<br />\nThe next drawing shows the organization of the memory (not in scale):\n<div class="center"><img src="fat.gif" alt="" /></div>\nWe already know the MBR and the FBR, while the other sections are:\n<ul>\n <li><em>FAT Area (File Allocation Table)</em>: in a FAT file system, files are not necessarily contiguous, but the can be fragmented all over the memory;\n for that reason, there is a FAT Area where there are cluster chains that are used to reassemble a file from its clusters.</li>\n <li><em>FAT2</em>: it is a security copy of the FAT Area.</li>\n <li><em>Root Directory</em>: it contains the list of the files stored in the root directory.</li>\n <li><em>Data Area</em>: the files are stored here (divided in clusters).</li>\n</ul>\n<br />\nIn the MPFS filesystem, all files are contiguous, so for simplicity in the Microchip stack, each file is identified by his linear address\nin the memory (that address is called <em>handle</em>). But in the FAT16 filesystem, this information is not enough;\nfor that reason, there is a table (the <em>handleTable</em>) that contains some informations of each open file. The <em>MAX_OPENED_FILES</em> define\nis equal to the max number of entries of that table.\n<pre class="code">\ntypedef struct {\n BYTE attrib;\n WORD cluster; // cluster in DataArea\n BYTE sector; // sector in cluster\n WORD byte; // byte in sector;\n DWORD filesize;\n} handleEntry;\n\nhandleEntry handleTable[MAX_OPENED_FILES];</pre>\n<br />\nNow we can see how the FAT driver initialization is done:\n<pre class="code">/* partition data */\nDWORD partitionStart; \nBYTE clusterSize; // sectors per cluster\nDWORD fatStart;\nDWORD rootStart;\nDWORD dataArea;\nBYTE rootSize;\n\nBOOL FATInit() {\n char* buf_pt = &sd_buffer[0];\n char i;\n\n sdPresent = SDInit();\n\n if (!sdPresent)\n return FALSE;\n\n SDReadSector(0);\n partitionStart = *(DWORD*)(buf_pt+0x1C6);\n\n SDReadSector(partitionStart);\n clusterSize = buf_pt[0x0D]; \n fatStart = partitionStart + *(WORD*)(buf_pt+0x0E);\n rootStart = fatStart + (*(WORD*)(buf_pt+0x16)) * buf_pt[0x10];\n rootSize = *(WORD*)(buf_pt+0x11) * sizeof(dirEntry) / SECTOR_SIZE;\n dataArea = rootStart + rootSize;\n\n for (i = 0; i &lt; MAX_OPENED_FILES; i++)\n handleTable[i].attrib = FREE_HANDLE;\n\n mpfsOpenCount = 0;\n\n return TRUE;\n}</pre>\nIn this method the SD card and the Handle table are initialized and some addresses are computed:\n<table class="simple_table" style="width:700px">\n <tr>\n <td><b>Variable</b></td>\n <td><b>Description</b></td>\n <td><b>Value</b></td>\n </tr>\n <tr>\n <td>clusterSize</td>\n <td>Number of sectors in a cluster</td>\n <td>&nbsp;</td>\n </tr>\n <tr>\n <td>fatStart</td>\n <td>Address of the FAT Area</td>\n <td>start of partition + reserved sectors</td>\n </tr>\n <tr>\n <td>rootStart</td>\n <td>Address of the Root Directory</td>\n <td>Address of the FAT + (nuber of FAT copies * FAT size)</td>\n </tr>\n <tr>\n <td>rootSize</td>\n <td>Size of the Root Directory</td>\n <td>Number of entries * size of an entry</td>\n </tr>\n <tr>\n <td>dataArea</td>\n <td>Address of the Data Area</td>\n <td>Address of the Root Dir + Size of the Root Dir</td>\n </tr>\n</table>\nAll these addresses and sizes are in sectors of 512 bytes. <br />\n<br />\nA Directory is composed by entries of fixed size (32 bytes),\neach of which contains the following fields (only for ShortFileName entries):\n<table class="simple_table" style="width:500px">\n <tr>\n <td><b>Offset</b></td>\n <td><b>Description</b></td>\n <td><b>Size</b></td>\n </tr>\n <tr>\n <td>0x00</td>\n <td>Name</td>\n <td>8 Bytes</td>\n </tr>\n <tr>\n <td>0x08</td>\n <td>Extension</td>\n <td>3 Bytes</td>\n </tr>\n <tr>\n <td>0x0B </td>\n <td>Attributes</td>\n <td>1 Byte </td>\n </tr>\n <tr>\n <td>0x0C </td>\n <td>Reserved</td>\n <td>1 Byte </td>\n </tr>\n <tr>\n <td>0x0D </td>\n <td>Creation Date & Time</td>\n <td>7 Bytes </td>\n </tr>\n <tr>\n <td>0x14 </td>\n <td>Extended Attributes</td>\n <td>2 Bytes </td>\n </tr>\n <tr>\n <td>0x16</td>\n <td>Time</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x18</td>\n <td>Date</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x20</td>\n <td>First cluster of the File</td>\n <td>2 Bytes</td>\n </tr>\n <tr>\n <td>0x1C</td>\n <td>Size of the file in bytes</td>\n <td>4 Byte</td>\n </tr>\n</table>\n\nThe items <em>Name</em> and <em>Extension</em> are always written in upper case, and empy characters are filled with spaces (0x20 in hex).\nFor example, <em>Goofy.txt</em> will be\n<pre class="code">GOOFY TXT 47 4F 4F 46 59 20 20 20 54 58 54</pre>\n<br />\nThe <em>MPFSOpen</em> method searches for a file in the memory and if it find that file, it will create and return an handle:\n<pre class="code">MPFS MPFSOpen(BYTE* name){\n BYTE scount, ecount;\n dirEntry* entry;\n char* buf_pt = &sd_buffer[0];\n char i;\n char name_ext[11];\n\n if (!sdPresent) return MPFS_INVALID;\n\n for (i=0; i&lt;11; i++)\n name_ext[i] = 0x20;\n \n for (i=0; i&lt;8 && *name!='.' && *name; i++)\n name_ext[i] = toupper(*name++);\n\n if (*name == '.') {\n name++;\n for (i=8; i&lt;11 &amp;&amp; *name; i++)\n name_ext[i] = toupper(*name++);\n }\n\n scount = 0;\n while (scount &lt; rootSize) {\n SDReadSector(rootStart + scount);\n\n ecount = 16;\n entry = (dirEntry*)buf_pt;\n while (ecount--) {\n if (entry-&gt;attrib == 0x0F) goto next; // Long File Name\n if (entry-&gt;name_ext[0] == 0x00 || entry-&gt;name_ext[0] == 0xE5) goto next; // Empty Entry\n if (memcmp((void*)name_ext, (void*)entry-&gt;name_ext, 11) == 0) { // found!\n for (i=0; i &lt; MAX_OPENED_FILES && handleTable[i].attrib!=FREE_HANDLE; i++);\n if (i == MAX_OPENED_FILES) return MPFS_NOT_AVAILABLE;\n handleTable[i].attrib = USED_HANDLE;\n handleTable[i].cluster = entry-&gt;cluster;\n handleTable[i].filesize = entry-&gt;size; \n handleTable[i].sector = 0; \n handleTable[i].byte = 0;\n mpfsOpenCount++;\n return i;\n }\n next:\n entry++;\n }\n\n scount++;\n }\n\n return MPFS_INVALID;\n}</pre>\nIn the first section, the filename is written in an array in the 8+3 format. In this way,\nwe can compare it with the one stored in a directory entry. At this point, all the entries are read, until we found the desired file.<br />\nWhen the file is found, the code will create a new handle and saves some informations:\n<ul>\n <li><em>filesize</em>: file size in bytes.</li>\n <li><em>cluster</em>: first cluster of the file in Data Area.</li>\n <li><em>sector</em>: initialized to 0; it indicates the current sector in the current cluster.</li>\n <li><em>byte</em>: similar to the previous; it contains the current byte index in the current sector.</li>\n</ul>\n<br />\nWith <em>MPFSGetBegin</em> the driver makes the card ready to read the file opened using <em>MPFSOpen</em>, this is done loading\nin the buffer the sector pointed by the variables in the HandleTable:\n<pre class="code">BOOL MPFSGetBegin(MPFS handle){\n if (handle >= MAX_OPENED_FILES || handleTable[handle].attrib != USED_HANDLE)\n return FALSE; // invalid handle\n\n if (handleTable[handle].filesize == 0)\n return TRUE;\n\n _currentHandle = handle;\n SDReadSector(dataArea + (handleTable[handle].cluster-2)*clusterSize + handleTable[handle].sector);\n\n return TRUE;\n}</pre>\nIn the FAT16 filesystem, two cluster are reserved, so the first cluster of the Data Area is the number 2.<br />\n<br />\nThe <em>MPFSGet</em> method reads and return a byte at a time. If this byte is already present in the SD buffer,\nwe can read it from the buffer, otherwise we have to calculate the address of the next sector to load.\nIf this sector is located in the same cluster of the previous, it is the following, else the FAT Area must be read to\nknow the nect cluster of the file.\n<pre class="code">BYTE MPFSGet() {\n char* buf_pt = &sd_buffer[0];\n BYTE b = 0;\n WORD tmp;\n\n if (_currentHandle == MPFS_INVALID)\n return 0;\n\n if (handleTable[_currentHandle].byte >= SECTOR_SIZE) {\n handleTable[_currentHandle].byte = 0;\n handleTable[_currentHandle].sector++;\n if (handleTable[_currentHandle].sector >= clusterSize) {\n tmp = handleTable[_currentHandle].cluster;\n SDReadSector(fatStart + tmp / (SECTOR_SIZE /2) ); // read FAT Area\n handleTable[_currentHandle].cluster = *(WORD*)(buf_pt + (tmp % (SECTOR_SIZE/2)) * 2); // next cluster in chain\n handleTable[_currentHandle].sector = 0;\n }\n SDReadSector(dataArea + (handleTable[_currentHandle].cluster-2)*clusterSize + handleTable[_currentHandle].sector);\n }\n\n\n b = buf_pt[handleTable[_currentHandle].byte++];\n handleTable[_currentHandle].filesize--;\n\n return b;\n}</pre>\nIn the FAT Area, each word of 16 bit answer to a cluster. As an example, if we are reading a file starting at the cluster 4 (the fifth),\nto know were the file continues, we only have to read the corresponding word in the FAT, that is the fifth one.<br />\nSo, to do that, we have to know where to read the value of the next cluster in the chain; so, first we must calculate the\nsector:\n<pre class="code">current_cluster / (SECTOR_SIZE /2)</pre>\nAt this point, the word to read is at address (in bytes)\n<pre class="code">(current_cluster % (SECTOR_SIZE/2)) * 2</pre>\nin that sector.<br />\n<br />\nOther used methods:\n<pre class="code">\nvoid MPFSClose() {\n mpfsOpenCount--; // decrementa il numero di file aperti\n handleTable[_currentHandle].attrib = FREE_HANDLE; // libera l'handle\n}\n\nBOOL MPFSIsEOF(void) {\n return (handleTable[_currentHandle].filesize == -1); // se la dimensione è -1, il file è finito\n}</pre>\n<br />\n</html>
[img[FTPmicro|ftpmicro.jpg][ftpmicroH.jpg]] [img[FTPmicro_back|ftpmicro_back.jpg][ftpmicro_backH.jpg]]
/***\n|Name|FontSizePlugin|\n|Created by|SaqImtiaz|\n|Location|http://tw.lewcid.org/#FontSizePlugin|\n|Version|1.0|\n|Requires|~TW2.x|\n!Description:\nResize tiddler text on the fly. The text size is remembered between sessions by use of a cookie.\nYou can customize the maximum and minimum allowed sizes.\n(only affects tiddler content text, not any other text)\n\nAlso, you can load a TW file with a font-size specified in the url.\nEg: http://tw.lewcid.org/#font:110\n\n!Demo:\nTry using the font-size buttons in the sidebar, or in the MainMenu above.\n\n!Installation:\nCopy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.\nThen put {{{<<fontSize "font-size:">>}}} in your SideBarOptions tiddler, or anywhere else that you might like.\n\n!Usage\n{{{<<fontSize>>}}} results in <<fontSize>>\n{{{<<fontSize font-size: >>}}} results in <<fontSize font-size:>>\n\n!Customizing:\nThe buttons and prefix text are wrapped in a span with class fontResizer, for easy css styling.\nTo change the default font-size, and the maximum and minimum font-size allowed, edit the config.fontSize.settings section of the code below.\n\n!Notes:\nThis plugin assumes that the initial font-size is 100% and then increases or decreases the size by 10%. This stepsize of 10% can also be customized.\n\n!History:\n*27-07-06, version 1.0 : prevented double clicks from triggering editing of containing tiddler.\n*25-07-06, version 0.9\n\n!Code\n***/\n\n//{{{\nconfig.fontSize={};\n\n//configuration settings\nconfig.fontSize.settings =\n{\n defaultSize : 100, // all sizes in %\n maxSize : 200,\n minSize : 40,\n stepSize : 10\n};\n\n//startup code\nvar fontSettings = config.fontSize.settings;\n\nif (!config.options.txtFontSize)\n {config.options.txtFontSize = fontSettings.defaultSize;\n saveOptionCookie("txtFontSize");}\nsetStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\sn","fontResizerStyles");\nsetStylesheet("#contentWrapper .fontResizer .button {display:inline;font-size:105%; font-weight:bold; margin:0 1px; padding: 0 3px; text-align:center !important;}\sn .fontResizer {margin:0 0.5em;}","fontResizerButtonStyles");\n\n//macro\nconfig.macros.fontSize={};\nconfig.macros.fontSize.handler = function (place,macroName,params,wikifier,paramString,tiddler)\n{\n\n var sp = createTiddlyElement(place,"span",null,"fontResizer");\n sp.ondblclick=this.onDblClick;\n if (params[0])\n createTiddlyText(sp,params[0]);\n createTiddlyButton(sp,"+","increase font-size",this.incFont);\n createTiddlyButton(sp,"=","reset font-size",this.resetFont);\n createTiddlyButton(sp,"–","decrease font-size",this.decFont);\n}\n\nconfig.macros.fontSize.onDblClick = function (e)\n{\n if (!e) var e = window.event;\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return false;\n}\n\nconfig.macros.fontSize.setFont = function ()\n{\n saveOptionCookie("txtFontSize");\n setStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\sn","fontResizerStyles");\n}\n\nconfig.macros.fontSize.incFont=function()\n{\n if (config.options.txtFontSize < fontSettings.maxSize)\n config.options.txtFontSize = (config.options.txtFontSize*1)+fontSettings.stepSize;\n config.macros.fontSize.setFont();\n}\n\nconfig.macros.fontSize.decFont=function()\n{\n\n if (config.options.txtFontSize > fontSettings.minSize)\n config.options.txtFontSize = (config.options.txtFontSize*1) - fontSettings.stepSize;\n config.macros.fontSize.setFont();\n}\n\nconfig.macros.fontSize.resetFont=function()\n{\n\n config.options.txtFontSize=fontSettings.defaultSize;\n config.macros.fontSize.setFont();\n}\n\nconfig.paramifiers.font =\n{\n onstart: function(v)\n {\n config.options.txtFontSize = v;\n config.macros.fontSize.setFont();\n }\n};\n//}}}
FTPmicro is a small web-server, as big as a [[DIP40]] package, based on the PIC18F67J60. This chip is an 8-bit\nPICmicro with an integrated 10BaseT Ethernet controller.\nDespite the small dimensions, the board has some interesting peripherals, like a microSD card slot,\n a TC1047 temperature sensor, and many analog and digital I/O pins.\nBut even more important is the software that can be performed on this device. Using the Microchip TCP/IP stack,\nsuited for this particular hardware, it is possible to have, in few minutes, a working HTTP and UDP server with DHCP client.\n\nFTPMicro doesn't need any external component: loading the compiled software in the PIC and powering\nthe device at 5V is all you need.\n\nIt also has a comfortable connector for the ICD2 programmer/debugger by Microchip. Thanks to the SD memory,\na large number of files can be saved, and they will be available to the HTTP server. In this way, you can store a complete web-site,\nwith both static (HTML) and dynamic (CGI) pages.\nBy reading data in real-time and sending commands, it is possible to control any hardware.\n
[img[ICD2|icd2.jpg][icd2_h.jpg]]
Type the text for 'InterfaceOptions'
GettingStarted\n[[The SD Memory]]\n[[Configuration]]\n[[The HTTP Server]]\n[[Example]]\n[[Source Code]]\n[[Schematic]]\nPinOut\n[[FAT16.c]]\n[[SD.c]]\n\n[[Electronics BLOG|http://dev.emcelettronica.com]]\n\n@@bgcolor(yellow):''[[ORDER NOW|http://store.emcelettronica.com]]''@@\n\n[img[Italiano|it-flag.jpg][index.html]]\n\n^^<<today "DDD DD MMM YYYY">>^^\n© [[EMCelettronica Srl|http://www.emcelettronica.com]]\n\n^^[img[favicon.ico]] TiddlyWiki <<version>>^^\n\n<html>\n<!-- ADDFREESTATS.COM AUTOCODE V4.0 -->\n<script type="text/javascript">\n<!--\nvar AFS_Account="00621133";\nvar AFS_Tracker="auto";\nvar AFS_Server="www6";\nvar AFS_Page="DetectName";\nvar AFS_Url="DetectUrl";\n// -->\n</script>\n<script type="text/javascript" src="http://www6.addfreestats.com/cgi-bin/afstrack.cgi?usr=00621133">\n</script>\n<noscript>\n<a href="http://www.addfreestats.com" >\n<img src="http://www6.addfreestats.com/cgi-bin/connect.cgi?usr=00621133Pauto" border=0 title="AddFreeStats.com Free Web Stats!"></a>\n</noscript>\n<!-- ENDADDFREESTATS.COM AUTOCODE V4.0 -->\n</html>
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">\n</script>\n<script type="text/javascript">\n_uacct = "UA-811279-9";\nurchinTracker();\n</script>\n\n\n\n
[img[PIC18F67J60|pic18f67j60.jpg][pic18f67j60_h.jpg]]
Choose Your preferited <<fontSize font-size: >> and click on Pin Name for description\n|!PIN|!DIR|!NAME| |!PIN|!DIR|!NAME| |!PIN|!DIR|!NAME| |!PIN|!DIR|!NAME|\n|bgcolor(#EEEEEE): 1 |bgcolor(#EEEEEE): |bgcolor(#EEEEEE): GND | |bgcolor(#EFEFD0): 9 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE0 | |bgcolor(#EEEEEE): 17 |bgcolor(#EEEEEE): |bgcolor(#EEEEEE): GND | |bgcolor(#FF8888): 25 |bgcolor(#FF8888): |bgcolor(#FF8888): 3V3 |\n|bgcolor(#EFEFD0): 2 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB5 | |bgcolor(#EFEFD0): 10 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE1 | |bgcolor(#FF8888): 18 |bgcolor(#FF8888): |bgcolor(#FF8888): 3V3 | |bgcolor(#EFEFD0): 26 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RA2 |\n|bgcolor(#EFEFD0): 3 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB4 | |bgcolor(#EFEFD0): 11 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE2 | |bgcolor(#EFEFD0): 19 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF6 | |bgcolor(#EFEFD0): 27 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RA3 |\n|bgcolor(#EFEFD0): 4 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB3 | |bgcolor(#EFEFD0): 12 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE3 | |bgcolor(#EFEFD0): 20 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF5 | |bgcolor(#EFEFD0): 28 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RC6 |\n|bgcolor(#EFEFD0): 5 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB2 | |bgcolor(#EFEFD0): 13 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE4 | |bgcolor(#EFEFD0): 21 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF4 | |bgcolor(#EFEFD0): 29 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RC7 |\n|bgcolor(#EFEFD0): 6 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB1 | |bgcolor(#EFEFD0): 14 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RE5 | |bgcolor(#EFEFD0): 22 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF3 | |bgcolor(#EFEFD0): 30 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RC0 |\n|bgcolor(#EFEFD0): 7 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RB0 | |bgcolor(#FFEE66): 15 |bgcolor(#FFEE66): |bgcolor(#FFEE66): +5V | |bgcolor(#EFEFD0): 23 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF2 | |bgcolor(#EFEFD0): 31 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RC1 |\n|bgcolor(#EFEFD0): 8 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RG4 | |bgcolor(#EEEEEE): 16 |bgcolor(#EEEEEE): |bgcolor(#EEEEEE): GND | |bgcolor(#EFEFD0): 24 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RF1 | |bgcolor(#EFEFD0): 32 |bgcolor(#EFEFD0): I/O |bgcolor(#EFEFD0): RC2 |
<html>\nThe <a target="_blank" href="http://www.sandisk.com/Assets/File/OEM/Manuals/SD_SDIO_specsv1.pdf">MMC/SD</a> memory cards\nhave several communication protocols (some of which are optional), particularly the SD (<em>Secure Digital</em>) memories always support the SPI protocol.<br />\nTo speak with a SD memory, the controller have to send a command, that is always followed by a response from the card.<br />\nThe SPI mode is activated holding down the CS signal when sending the CMD0 command (that makes the card in <em>Idle Mode</em>).\nAt this point, to finish the initialization, we have to send the ACMD41 command until the memory exits from the <em>Idle</em> state\n(card status is read in its response). <br />\nThe ACMD41 is an <em>Application-Specific</em> command, so it is preceded by <em>CMD55</em>.<br />\n<br />\n<pre class="code">char SDInit() \n{\n char i;\n char status;\n \n TRISCbits.TRISC5 = 0;\n TRISCbits.TRISC4 = 1;\n TRISCbits.TRISC3 = 0;\n SSP1STAT = 0x00;\n SSP1CON1 = 0x30; // SPI master, Fosc/4\n PIR1bits.SSP1IF = 0;\n SD_CS_TRIS = 0;\n SD_CS = 1; \n\n for(i=0; i &lt; 16; i++)\n spi(0xFF); // extra clock for powerup \n\n SD_CS = 0;\n\n SDCmd(CMD0, 0); // Send reset command\n if (SDWait()!=0x01)\n goto SDError;\n\n i = 20;\n do {\n SDCmd(CMD55, 0);\n SDWait();\n SDCmd(ACMD41, 0); // send ACMD41 to finish the initialization\n if (i-- &lt; 0)\n goto SDError;\n } while ((SDWait()&1) != 0x00); // wait until the memory is not in idle state\n\n SD_CS = 1;\n return TRUE;\nSDError:\n SD_CS = 1;\n return FALSE;\n}</pre>\nWith the <em>SDInit</em> method, the SPI module is configured, and then the operations described above are executed.<br />\n<br />\nIn the previous code fragment, two methods are used: <em>SDCmd</em> is used to send a command to the memory, and <em>SDWait</em> waits for the response.\n<pre class="code">char SDCmd(char cmd, unsigned long param)\n{\n spi(0xFF); // extra clock\n spi(cmd); // command\n spi(param >> 24);\n spi(param >> 16);\n spi(param >> 8);\n spi(param);\n spi(cmd == CMD0 ? 0x95 : 0xFF); // CRC, mandatory only for CMD0\n}\n\nchar SDWait(void)\n{\n unsigned short count = 0xFF;\n char response;\n\n while((response=spi(0xFF)) == 0xFF && --count > 0); // wait for a response different from 0xFF, with timeout\n\n return response;\n}</pre>\n<br />\nFinally, the method that we use to read a sector from the card with <em>CMD17</em> is <em>SDReadSector</em>.\nData can be transferred from the card to the controller (and vice versa) only in blocks of fixed size (512 by default).\n<pre class="code">char SDReadSector(unsigned long sector)\n{\n int i;\n char* buf_pt = &sd_buffer[0];\n char status;\n\n SD_CS = 0;\n SDCmd(CMD17, (sector * SECTOR_SIZE)); // read from the address = sector*512\n\n if(SDWait()!=0x00 || SDWait()!=0xFE) // wait for two responses\n {\n SD_CS = 1;\n sdPresent = FALSE; // if the reading fails, we suppose that the card is not present\n return FALSE; \n }\n\n for (i = 0; i &lt; SECTOR_SIZE; i++)\n buf_pt[i] = spi(0xFF); // read block\n\n spi(0xFF);\n spi(0xFF); // flush CRC\n\n SD_CS = 1;\n return TRUE;\n}</pre>\nRead data is stored in a buffer, that has to fit exactly two memory banks (of 256 bytes each), to do that the <em>pragma</em> directive is used;\nbesides that, the RAM occupied by the buffer must be declared as Protected.\n</html>
[img[Schematic|schematic.jpg][schematicH.jpg]]
The world smallest WebServer and FtpClient with DHCP and UDP features
FTPmicro
<html>\nThe FTPMicro's software, is available at <a href="FTPMicro_1v01.rar">this address</a>.<br />\nTha source code includes the example illustrated in the article </html> "[[A Pratical Example]]".\n
/*{{{*/\n* html .tiddler {\n height: 1%;\n}\n\nbody {\n font-size: .75em;\n font-family: arial,helvetica;\n margin: 0;\n padding: 0;\n}\n\nh1,h2,h3,h4,h5 {\n font-weight: bold;\n text-decoration: none;\n padding-left: 0.4em;\n}\n\nh1 {font-size: 1.35em;}\nh2 {font-size: 1.25em;}\nh3 {font-size: 1.1em;}\nh4 {font-size: 1em;}\nh5 {font-size: .9em;}\n\nhr {\n height: 1px;\n}\n\na{\n text-decoration: none;\n}\n\ndt {font-weight: bold;}\n\nol { list-style-type: decimal }\nol ol { list-style-type: lower-alpha }\nol ol ol { list-style-type: lower-roman }\nol ol ol ol { list-style-type: decimal }\nol ol ol ol ol { list-style-type: lower-alpha }\nol ol ol ol ol ol { list-style-type: lower-roman }\nol ol ol ol ol ol ol { list-style-type: decimal }\n\n.txtOptionInput {\n width: 11em;\n}\n\n#contentWrapper .chkOptionInput {\n border: 0;\n}\n\n.externalLink {\n text-decoration: underline;\n}\n\n.indent {margin-left:3em;}\n.outdent {margin-left:3em; text-indent:-3em;}\ncode.escaped {white-space:nowrap;}\n\n.tiddlyLinkExisting {\n font-weight: bold;\n}\n\n.tiddlyLinkNonExisting {\n font-style: italic;\n}\n\n/* the 'a' is required for IE, otherwise it renders the whole tiddler a bold */\na.tiddlyLinkNonExisting.shadow {\n font-weight: bold;\n}\n\n#mainMenu .tiddlyLinkExisting, \n#mainMenu .tiddlyLinkNonExisting,\n#sidebarTabs .tiddlyLinkNonExisting{\n font-weight: normal;\n font-style: normal;\n}\n\n#sidebarTabs .tiddlyLinkExisting {\n font-weight: bold;\n font-style: normal;\n}\n\n.header {\n position: relative;\n}\n\n.header a:hover {\n background: transparent;\n}\n\n.headerShadow {\n position: relative;\n padding: 4.5em 0em 1em 1em;\n left: -1px;\n top: -1px;\n}\n\n.headerForeground {\n position: absolute;\n padding: 4.5em 0em 1em 1em;\n left: 0px;\n top: 0px;\n}\n\n.siteTitle {\n font-size: 3em;\n}\n\n.siteSubtitle {\n font-size: 1.2em;\n}\n\n#mainMenu {\n position: absolute;\n left: 0;\n width: 10em;\n text-align: right;\n line-height: 1.6em;\n padding: 1.5em 0.5em 0.5em 0.5em;\n font-size: 1.1em;\n}\n\n#sidebar {\n position: absolute;\n right: 3px;\n width: 16em;\n font-size: .9em;\n}\n\n#sidebarOptions {\n padding-top: 0.3em;\n}\n\n#sidebarOptions a {\n margin: 0em 0.2em;\n padding: 0.2em 0.3em;\n display: block;\n}\n\n#sidebarOptions input {\n margin: 0.4em 0.5em;\n}\n\n#sidebarOptions .sliderPanel {\n margin-left: 1em;\n padding: 0.5em;\n font-size: .85em;\n}\n\n#sidebarOptions .sliderPanel a {\n font-weight: bold;\n display: inline;\n padding: 0;\n}\n\n#sidebarOptions .sliderPanel input {\n margin: 0 0 .3em 0;\n}\n\n#sidebarTabs .tabContents {\n width: 15em;\n overflow: hidden;\n}\n\n.wizard {\n padding: 0.1em 0em 0em 2em;\n}\n\n.wizard h1 {\n font-size: 2em;\n font-weight: bold;\n background: none;\n padding: 0em 0em 0em 0em;\n margin: 0.4em 0em 0.2em 0em;\n}\n\n.wizard h2 {\n font-size: 1.2em;\n font-weight: bold;\n background: none;\n padding: 0em 0em 0em 0em;\n margin: 0.2em 0em 0.2em 0em;\n}\n\n.wizardStep {\n padding: 1em 1em 1em 1em;\n}\n\n.wizard .button {\n margin: 0.5em 0em 0em 0em;\n font-size: 1.2em;\n}\n\n#messageArea {\nposition:absolute; top:0; right:0; margin: 0.5em; padding: 0.5em;\n}\n\n*[id='messageArea'] {\nposition:fixed !important; z-index:99;}\n\n.messageToolbar {\ndisplay: block;\ntext-align: right;\n}\n\n#messageArea a{\n text-decoration: underline;\n}\n\n.popup {\n font-size: .9em;\n padding: 0.2em;\n list-style: none;\n margin: 0;\n}\n\n.popup hr {\n display: block;\n height: 1px;\n width: auto;\n padding: 0;\n margin: 0.2em 0em;\n}\n\n.listBreak {\n font-size: 1px;\n line-height: 1px;\n}\n\n.listBreak div {\n margin: 2px 0;\n}\n\n.popup li.disabled {\n padding: 0.2em;\n}\n\n.popup li a{\n display: block;\n padding: 0.2em;\n}\n\n.tabset {\n padding: 1em 0em 0em 0.5em;\n}\n\n.tab {\n margin: 0em 0em 0em 0.25em;\n padding: 2px;\n}\n\n.tabContents {\n padding: 0.5em;\n}\n\n.tabContents ul, .tabContents ol {\n margin: 0;\n padding: 0;\n}\n\n.txtMainTab .tabContents li {\n list-style: none;\n}\n\n.tabContents li.listLink {\n margin-left: .75em;\n}\n\n#displayArea {\n margin: 1em 17em 0em 14em;\n}\n\n\n.toolbar {\n text-align: right;\n font-size: .9em;\n visibility: hidden;\n}\n\n.selected .toolbar {\n visibility: visible;\n}\n\n.tiddler {\n padding: 1em 1em 0em 1em;\n}\n\n.missing .viewer,.missing .title {\n font-style: italic;\n}\n\n.title {\n font-size: 1.6em;\n font-weight: bold;\n}\n\n.missing .subtitle {\n display: none;\n}\n\n.subtitle {\n font-size: 1.1em;\n}\n\n.tiddler .button {\n padding: 0.2em 0.4em;\n}\n\n.tagging {\nmargin: 0.5em 0.5em 0.5em 0;\nfloat: left;\ndisplay: none;\n}\n\n.isTag .tagging {\ndisplay: block;\n}\n\n.tagged {\nmargin: 0.5em;\nfloat: right;\n}\n\n.tagging, .tagged {\nfont-size: 0.9em;\npadding: 0.25em;\n}\n\n.tagging ul, .tagged ul {\nlist-style: none;margin: 0.25em;\npadding: 0;\n}\n\n.tagClear {\nclear: both;\n}\n\n.footer {\n font-size: .9em;\n}\n\n.footer li {\ndisplay: inline;\n}\n\n* html .viewer pre {\n width: 99%;\n padding: 0 0 1em 0;\n}\n\n.viewer {\n line-height: 1.4em;\n padding-top: 0.5em;\n}\n\n.viewer .button {\n margin: 0em 0.25em;\n padding: 0em 0.25em;\n}\n\n.viewer blockquote {\n line-height: 1.5em;\n padding-left: 0.8em;\n margin-left: 2.5em;\n}\n\n.viewer ul, .viewer ol{\n margin-left: 0.5em;\n padding-left: 1.5em;\n}\n\n.viewer table {\n border-collapse: collapse;\n margin: 0.8em 1.0em;\n}\n\n.viewer th, .viewer td, .viewer tr,.viewer caption{\n padding: 3px;\n}\n\n.viewer table.listView {\n font-size: 0.85em;\n margin: 0.8em 1.0em;\n}\n\n.viewer table.listView th, .viewer table.listView td, .viewer table.listView tr {\n padding: 0px 3px 0px 3px;\n}\n\n.viewer pre {\n padding: 0.5em;\n margin-left: 0.5em;\n font-size: 1.2em;\n line-height: 1.4em;\n overflow: auto;\n}\n\n.viewer code {\n font-size: 1.2em;\n line-height: 1.4em;\n}\n\n.editor {\nfont-size: 1.1em;\n}\n\n.editor input, .editor textarea {\n display: block;\n width: 100%;\n font: inherit;\n}\n\n.editorFooter {\n padding: 0.25em 0em;\n font-size: .9em;\n}\n\n.editorFooter .button {\npadding-top: 0px; padding-bottom: 0px;}\n\n.fieldsetFix {border: 0;\npadding: 0;\nmargin: 1px 0px 1px 0px;\n}\n\n.sparkline {\n line-height: 1em;\n}\n\n.sparktick {\n outline: 0;\n}\n\n.zoomer {\n font-size: 1.1em;\n position: absolute;\n padding: 1em;\n}\n\n.cascade {\n font-size: 1.1em;\n position: absolute;\n overflow: hidden;\n}\n/*}}}*/
<html>\nThanks to the HTTP server is possible to control the device by a web-browser, with any Operating System (TCP/IP capable).<br />\nThis server is able to read files from the SD card, and send them to the client.\nWith a GET request, we can interact with the stack software in two ways: "reading", by asking for a CGI page, and "writing", by sending\na request with parameters.<br />\nA CGI page (Common Gateway Interface) is a simple HTML page, but it can also contain the escaping character % followed by a two-digit number;\nthis number identify a varibile that can be returned by the software.<br />\nThat last thing is done by calling the <em>HTTPGetVar</em> function, in file <em>MainDemo.c</em>:\n<pre class="code">WORD HTTPGetVar(BYTE var, WORD ref, BYTE* val)</pre>\nthe first parameter is the requested variable number; the second one is an index used in multi-byte variables;\nand the last one is a pointer where we can write the ref-nth byte of the desired CGI variable.<br />\nThe value returned by this method is HTTP_END_OF_VAR if <em>val</em> the last (or the only) byte of the variable, otherwise\nit returns the index of the next byte, that is ref+1.<br />\n<br />\nA short example:\n<pre class="code">&lt;b&gt;Variable 1&lt;/b&gt;: %01 &lt;br&gt;\n&lt;b&gt;Variable 2&lt;/b&gt;: &lt;em&gt;%02&lt;/em&gt;</pre>\nIn this CGI file there are two varibles, so <em>HTTPGetVar</em> will be called minimum two times, the first one with var=1, then\nthe function will put in val, for example, the char 'A', and it will terminate returning HTTP_END_OF_VAR.\nAfter that, <em>HTTPGetVar</em> will be called with var=2.\nThis time suppose that the variable is a string (e.g., "Hello"), so the function will be called many times, until the string is over and\ntherefore HTTP_END_OF_VAR is returned.<br />\nAt this point, the server sends the requested page with the values of the variables in it:\n<pre class="code">&lt;b&gt;Variable 1&lt;/b&gt;: A &lt;br&gt;\n&lt;b&gt;Variable 2&lt;/b&gt;: &lt;em&gt;Hello&lt;/em&gt;</pre>\n<br />\nNow, let's see how to interact with the software by adding parameters to a request.\nA request of that type is like this:\n<pre class="code">http://10.0.0.6/page.htm?param1=val1&param2=val2</pre>\nThis string is analized by the HTTP server and then passed to <em>HTTPExecCmd</em>, in the form of string array.<br />\nThe prototype of this method is the following:\n<pre class="code">void HTTPExecCmd(BYTE** argv, BYTE argc)</pre>\nSo, in the previous example, <em>argv</em> is:\n<pre class="code">argv[0] = page.htm\nargv[1] = param1\nargv[2] = val1\nargv[3] = param2\nargv[4] = val2</pre>\nand <em>argc</em> is 5. So, writing some code in this method, the software can execute differnt tasks depending on the given parameters.<br />\nAt last, when this function terminates, <em>argv[0]</em> must contain the name of the file that will be returned to the client; it can also be\nleft unchanged (in this case, "page.htm" will be sent).<br />\n<br />\n\n</html>
<html>\nFiles are stored in the SD card using the FAT16 filesystem, so it is compatible with almost any PC.\nYou can easily transfer any file from a computer to the card (in root directory only), but first you have to format this card with that Filesystem.<br />\nLong file names are supported, but a limit is imposed by the following define, located in <em>FAT16.h</em>:\n<pre class="code">#define MAX_FILENAME_LEN (32)</pre>\n<br />\nThe <em>hot-plug</em> feature allow you to insert and remove the SD card whenever you want while the device is turned on.<br />\n<br />\n</html>
Type the text for 'WikiWord'