Making a HTTP Server with MCU SDK (and a DHCP Server too) Todd Mullanix TCP/IP Stack H T T P OS Adaptation Layer Network Service Manager D H C P D N S Standard BSD Sockets Interface Route Manager TCP UDP ICMP IGMP IP IF Manager NAT Network Initialization T E L N E T T F T P ARP Ethernet IF Hardware Adaptation Layer Stack Event Scheduler Ethernet Serial Packet Port Driver Driver Timer Driver User LED Driver TCP/IP Key Features Supports both IPv4 and IPv6 C O N F I G U R A T I O N DHCP Client and Server HTTP Server Standard BSD Sockets interface Zero-copy sockets interface available Highly configurable to meet footprint constraints Example on how to create daemon task required by TCP/IP stacks Hardware 2 Let’s Make a WebServer! Steps • Create and build new “empty” project • Load and debug the empty project • Add in Networking stack • Add HTTP pages • Add in a DHCP Server 3 Creating an Empty Project First let’s create an empty project via the CCS Project Wizard. Doing “File->New->CCS Project” will start the wizard on the right. You need to 1. Give the project a unique name 2. Select the Family and Variant 3. Select the Connection (optional, but we’ll use this later) 4. Select the MCU SDK Empty project for Concerto (TMDXDOCKH52C1) Hit “Next” 4 Creating an Empty Project This page shows which software is being used. Note: MCU SDK automatically includes SYS/BIOS, NDK, Ipc, UIA and *Ware. So you do not need to specify them. Hit “Finish” 5 Creating an Empty Project Once the new project is created, select “Properties” to get the following window. This is the place to change compiler and linker options and other settings. The General page shows the product software configuration (again). 6 Building the httpServer Project • Let’s look at the files in the new project – – – – empty.c: main source file empty.cfg: main configuration file TMDXDOCKH52C1.c: board specific peripheral runtime configuration TMDXDOCKH52C1.h: header file for board specific peripheral runtime configuration APIs – TMDXDOCKH52C1.cmd: Linker command file – Board.h: Small “shim” header file to make board specific APIs generic. – F28M35H52C1.ccxml: Target Configuration file. • Build project (right click, Project menu or build icon ) – Build output goes to the console window – One warning that we are working on removing for the GA release of MCU SDK. – After the build is successful, if asked, do not load the application yet. 7 Let’s Make a WebServer! Steps • Create and build new “empty” project • Loading and debugging the empty project • Add in Networking stack • Add HTTP pages • Add in a DHCP Server 8 Loading the Project • Debug the application. This will do the following: – Launch the Debug session – Connect to the M3 – Load the application 9 Run the project • Run. You’ll probably halt in main(). This is configurable via the Tools->Debugger Options->Generic Debugger Options • Open RTOS Object View (Tools->ROV) Look at HeapMem and Task (shown below) • Open System Anaylzer (Tools->System Analyzer->Live) and select Start One event at this point. Notice that the Task handle address corresponds to the Idle Task shown in ROV above. 10 Debug Information • Resume • Suspend • Look at System Analyzer’s Live Session The LoggingSetup module is controlling what gets logged. We’ll look more at this later when there is more interesting data… 11 Let’s Make a WebServer! Steps • Create and build new “empty” project • Loading and debugging the empty project • Add in Networking stack • Add HTTP pages • Add in a DHCP Server 12 Adding Networking Stack • Open up empty.cfg and view System Overview • Add EMAC by right clicking and selecting “Use EMAC” This will add the EMAC driver and allows you to configure it. Note: MCUSDK and GPIO are already there. 13 Adding Networking Stack Currently there is not much configuration for the driver. This will be expanded in future releases. The instrumented library includes assert checking and Log events to help in debugging. 14 Adding Networking Stack • Go back to the MCU SDK System Overview and do a “Use” on the “TCP/IP” box. • You can now deselect IPv6 (below the IP box). • If you select the IP box, it goes to the IP configuration. You can specify whether to use a static IP address or DHCP. (We’ll play with this later) 15 Adding Networking Stack • In Empty.c, add the following bolded line around line 53. This initializes the EMAC driver Board_initGPIO(); Board_initEMAC(); • In TMDXDOCKH52C1.c, you need to change the MAC address to match the sticker on your board. Note: there is a request to place the MAC address into a dedicated location in ROM. UInt8 macAddress[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; Note: Once you have your company’s own custom board, you’ll need to make your own “board” source file. 16 Adding Networking Stack • Navigate to include HTTP Server by left clicking TCP/IP box from MCU SDK System Overview. • Right Click on HTTP and select “Use HTTP”. You should see the following now. 17 Adding Networking Stack • Select “Instance” and hit “Add”. We’ll leave the defaults. • Save and build application. 18 Adding Networking Stack • Before loading, Close System Analyzer Session. • Load and halt at main(). – Look at ROV’s Task, EMAC and Ndk windows • Load and halt at main(). [Cortex_M3_0] 00000.000 mmBulkAlloc(): could not allocate memory. [Cortex_M3_0] 00000.000 out of memory: handle=0x20e375, size=10 [Cortex_M3_0] Service Status: DHCPC : Failed : : 000 [Cortex_M3_0] ti.sysbios.heaps.HeapMem: line 294: out of memory: handle=0x20009100, size=1804 ti.sysbios.heaps.HeapMem: line 294: out of memory: handle=0x20009100, size=2048 xdc.runtime.Error.raise: terminating execution What happened! Look at HeapMem in ROV. There is not enough memory. The default Networking settings are too big. We are working to fix this for the next release. But let’s figure out how to fix this. 19 Fitting Networking Stack into Target First, the default Memory heap is too small. Select the Memory page in the outline window and increase the heap to 25088 (0x6200) 20 Fitting Networking Stack into Target Additionally, select the “Task” page and reduce the Idle Task size to 1024 (instead of 2048). 21 Fitting Networking Stack into Target There are several other fields that need tweaking. Instead of doing this graphically, you can do this via a text file. Close the graphical configuration and right click on the empty.cfg file and select Open With-> Text Editor and add the following lines at the bottom. Save the file. (Again we are trying to make this easier for the next release) Program.stack = 1024; var Udp = xdc.useModule('ti.ndk.config.Udp'); Udp.receiveBufSize = 2048; var Tcp = xdc.useModule('ti.ndk.config.Tcp'); Tcp.transmitBufSize = 2048; Tcp.receiveBufSize = 2048; Tcp.receiveBufLimit = 2048; Global.lowTaskStackSize = 1792; Global.normTaskStackSize = 1792; Global.highTaskStackSize = 1792; Global.ndkThreadStackSize = 1792; Global.pktSizeFrameBuf = 1552; Global.pktNumFrameBufs = 16; Global.memRawPageCount = 8; 22 Running HTTP Server • Make sure you have the Ethernet cable connection! • Rebuild and load the application. Once you run it, you should see the following: [Cortex_M3_0] [Cortex_M3_0] [Cortex_M3_0] [Cortex_M3_0] [Cortex_M3_0] Service Service Service Network Service Status: DHCPC : Enabled Status: HTTP : Enabled Status: DHCPC : Enabled Added: If-1:146.252.161.8 Status: DHCPC : Enabled : : : Running : 000 : 000 : 000 : Running : 017 • Look at ROV’s Task, Ndk, and EMAC • Open browser to IP Address… What’s wrong! The reason is because we have no web content on the target. 23 Let’s Make a WebServer! Steps • Create and build new “empty” project • Loading and debugging the empty project • Add in Networking stack • Add HTTP pages • Add in a DHCP Server 24 Adding HTTP Server Content The following slides are going to summarize the <mcusdk>/products/<ndk>/docs/spru524h.pdf’s “Appendix E: Web Programming with the HTTP Server” section… 25 Adding HTTP Server Content Let’s add a simple index.html page for the target. 1. Create a simple default.html file that contains “Hello World” in the same directory as your project. 2. Convert this to a Char array that can be included into the application. Run the binsrc.exe that is in the <mcusdk>products\<ndk>\packages\ti\ndk\tools\binsrc directory. Add this directory to your path or use the full path name >binsrc.exe default.html default.h DEFAULT This creates a default.h file, which has the Char array representation of the webpage. • Added the following header files into the empty.c file #include <ti/ndk/inc/netmain.h> #include "default.h" 26 Adding HTTP Server Content 4. Add the following functions into empty.c Void AddWebFiles(Void) { //Note: both DEFAULT_SIZE and DEFAULT are defined in default.h efs_createfile("index.html", DEFAULT_SIZE, (UINT8 *)DEFAULT); } Void RemoveWebFiles(Void) { efs_destroyfile("index.html"); } 5. Add the following into the empty.cfg file Global.stackInitHook = '&AddWebFiles'; Global.stackDeleteHook = '&RemoveWebFiles'; 6. Rebuild, load, run and now point your browser at the IP address. FYI: WireShark is a great free Network Analyzer that helps view the packets. 27 Adding HTTP Server Content Let’s add a second page. Make an greetings.html file that contains “Greets Everyone” and a picture (e.g. dspchip.gif). 1. Run the binsrc.exe convertor on the page and the image. >binsrc.exe greetings.html greetings.h GREETINGS >binsrc.exe dspchip.jpg dspchip.h DSPCHIP 2. Include the greetings.h and dspchip.h files into your project. #include "greetings.h" #include "dspchip.h" 3. Add greetings and dspchip into the AddWebFiles/RemoveWebFiles Void AddWebFiles(Void) { efs_createfile("index.html", DEFAULT_SIZE, (UINT8 *)DEFAULT); efs_createfile("greetings.html", GREETINGS_SIZE, (UINT8 *)GREETINGS); efs_createfile("dspchip.jpg", DSPCHIP_SIZE, (UINT8 *)DSPCHIP); } Void RemoveWebFiles(Void) { efs_destroyfile("index.html"); efs_destroyfile("greetings.html"); efs_destroyfile("dspchip.jpg"); } 28 Adding HTTP Server Content Rebuild, load and run the target now. Point your browser at the IP address. It should still be the “Hello World” page. Now point to <ipaddr>/greetings.html. You should see the greeting and the picture. It is also possible to pull the pages from the network or from an SD card. 29 Adding HTTP Server Content: CGI Pages are great, but how do you add “smarts” to the page? For example, how long has the target been up? The NDK supports CGI scripts to be on the target. The basic idea is that you write a CGI function in ‘C’ that plugs into the EFS. Let’s add the number of SYS/BIOS Clock ticks that have occurred since the system has started. 1. Add a getTicks.cgi command onto the default.html page. <body> Hello World<br> <br> Get current Clock tick&nbsp; <a href="getTicks.cgi">getTick.cgi</a><br> 2. Convert the default.htm via binsrc >binsrc.exe default.html default.h DEFAULT 30 Adding HTTP Server Content: CGI Now add the following code into empty.c #include <ti/sysbios/knl/Clock.h> Int getTicks(SOCKET s, int length) { Char buf[200]; static UInt scalar = 0; if (scalar == 0) { scalar = 1000000u / Clock_tickPeriod; } httpSendStatusLine(s, HTTP_OK, CONTENT_TYPE_HTML); httpSendClientStr(s, CRLF); httpSendClientStr(s, "<html><head><title>SYS/BIOS Clock "\ "Time</title></head><body><h1>Time</h1>\n"); System_sprintf(buf, "<p>Up for %d seconds</p>\n", ((unsigned long)Clock_getTicks() / scalar)); httpSendClientStr(s, buf); httpSendClientStr(s, "</table></body></html>"); return (1); } 31 Adding HTTP Server Content: CGI Now hook the cgi into the EFS in empty.c Void AddWebFiles(Void) { Void *pFxn; efs_createfile("index.html", DEFAULT_SIZE, (UINT8 *)DEFAULT); efs_createfile("greetings.html", GREETINGS_SIZE, (UINT8 *)GREETINGS); efs_createfile("dspchip.jpg", DSPCHIP_SIZE, (UINT8 *)DSPCHIP); pFxn = (Void*) &getTicks; efs_createfile("getTicks.cgi", 0, (UINT8 *) pFxn); } Void RemoveWebFiles(Void) { efs_destroyfile("index.html"); efs_destroyfile("greetings.html"); efs_destroyfile("dspchip.jpg"); efs_destroyfile("getTicks.cgi"); } Rebuild, load and run. Point browser to the IP address. 32 Debug Information • Start System Analyzer. • Analyze the application (right-click in the Live Session window) – Open Execution Graph Note: Hwi and Swi logging was not enabled, so you don’t see them. Hwi/Swi logging does impact performance (~2-3%). 33 Debug Information Open Task Load (both Graph and Summary) 34 Let’s Make a WebServer! Steps • Create and build new “empty” project • Loading and debugging the empty project • Add in Networking stack • Add HTTP pages • Add in a DHCP Server 35 Add in a DHCP Server Let’s add in a DHCP Server into the application. 1. Move your PC and target to a private network. 2. Add DHCP Server into your application’s configuration 36 Add in a DHCP Server 3. “Add” an instance and configure DHCP Server as needed. 37 Add in a DHCP Server 4. Give the target a static IP address. Select IP and configure accordingly 38 Add in a DHCP Server 5. Rebuild, load and run. Have you PC attempt to get an IP address now. 6. Future DHCP Server enhancements • • • Configurable lease time (currently 7 days) Configurable Gateway (currently Gateway IP address is the target’s IP address) API/ROV to display requests that have been serviced 39 Extra Material • Using a different Software product • printf vs System_printf • CPU Load vs Task Load • USB Logging 40 Creating an Empty Project The “Order” page shows the order that the software packages are used (both for configuration and linking). 41 Using a different product version. If you wanted to use a different sub-product version, select it on the RTSC page. Then on the “Order” page, move it up above the MCU SDK’s sub-product entry (see next slide). 42 Using a different product version. When building now, the 1.1.00.04 UIA will be used instead of the MCU SDK’s UIA. 43 printf vs System_printf The biggest question we have is “Why don’t I get output to the console” First some background… System_printf: This function behaves much like the ANSI C Standard printf but does not support the full range of format strings specified by the C Standard. In addition, several non-standard format specifiers are recognized. You can plug in different implementations into the System module. SysMin: Small implementation that stores output in an internal buffer. It is flushed to stdout when a System_flush is called or when the program exits. SysStd: Basically sits on top of printf and other RTS functions. SysFlex: Let’s the customer plug in different functions. MCU SDK ships a UART implementation. Please refer to the MCU SDK User Guide for more details 44 CPU Load vs Task Load Within the SYS/BIOS Idle Task, there is a list of functions that are executed. These N functions are repeated executed while in Idle. The execution of all N functions is 1 idle loop. One of the function is placed there by the Load module. For a time period (default 500ms) the number of loops completed by the Idle Task is counted. While this the counting is going on, the minimal time for a loop is also determined. The product of the number of times through the loop and the minimal time is an approximation for the time spent in Idle. The CPU Load is determined in this manner. An individual Task Load is determined by looking at the context switches. The Task Load is a more accurate measure. So 100 - %Load of Idle is more accurate than %CPULoad. However, there is more of a performance overhead to manage Task Load. Note: LoggerIdle plugs in an Idle function that messes up CPULoad, but not Task Load. 45 USB Logging Some of the examples (demo and uartconsole) already show how to log via the USB peripheral instead of stop-mode. 46