This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C129ENCPDT: TI-RTOS: Server with cgi functions

Part Number: TM4C129ENCPDT

Hi,

I have a server with a cgi function. If the cgi function is called, I send an html code and the function ends with 1.

Now I want to send the html code within a task. Unfortunately it doesn't work. Could someone tell me why I am grateful for any help


static int mainPage_Update(SOCKET htmlSock, int ContentLength, char *pArgs )
{
	Mailbox_post(mailboxServerHandle, &htmlSock, BIOS_NO_WAIT);

	//sendMainPage(htmlSock);

	return 1;
}


void serverTask(void)
{
	SOCKET msg;

	while(1)
	{
		Mailbox_pend(mailboxServerHandle, &msg, BIOS_WAIT_FOREVER);
        sendMainPage(msg);
	}
}

  • Hi,

      Did you use efs_createfile to associate the C function entry-point with your CGI file. Please refer to section E.1.3 in the NDK API Guide. https://www.ti.com/lit/pdf/spru524

    Example: 

    efs_createfile("sample.cgi", 0, (unsigned char *)cgiSample);

    Please also refer to this post where Todd presented an example of creating HTTP server including the usage of CGI functions. 

    https://e2e.ti.com/support/microcontrollers/other/f/other-microcontrollers-forum/947315/faq-tm4c1294kcpdt-do-you-have-an-example-of-a-http-server-on-a-tivac-device-running-ti-rtos-sys-bios

  • Hello,

    Thank you for your answer. The .cgi function is of course executed and if I send my html text directly from the cgi function, everything works.

    Now I don't want to send my html text within the cgi function, but from a task. I have changed the http server example slightly so that you can understand my plan.

    /*
     * Copyright (c) 2015, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /*
     *  ======== empty.c ========
     */
    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/cfg/global.h>
    
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Mailbox.h>
    
    /* TI-RTOS Header files */
    
    #include <ti/drivers/GPIO.h>
    
    
    /* Board Header file */
    #include "Board.h"
    
    #include <ti/ndk/inc/netmain.h>
    #include "index_withCGI.h"
    #include "greetings.h"
    #include "chip.h"
    
    #define TASKSTACKSIZE   1024
    
    Task_Struct task0Struct;
    Char task0Stack[TASKSTACKSIZE];
    
    
    
    Void sendWebsite(SOCKET* s)
    {
        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>");
    }
    
    Int getTime(SOCKET s, int length)
    {
    	Mailbox_post(mailbox0, &s, BIOS_NO_WAIT);
        return (1);
    }
    
    Void AddWebFiles(Void)
    {
        //Note: both INDEX_SIZE and INDEX are defined in index.h
        efs_createfile("index.html", INDEX_SIZE, (UINT8 *)INDEX);
        efs_createfile("getTime.cgi", 0, (UINT8 *)&getTime);
        efs_createfile("greetings.html", GREETINGS_SIZE, (UINT8 *)GREETINGS);
        efs_createfile("chip.jpg", CHIP_SIZE, (UINT8 *)CHIP);
    }
    
    Void RemoveWebFiles(Void)
    {
        efs_destroyfile("index.html");
        efs_destroyfile("getTime.cgi");
        efs_destroyfile("greetings.html");
        efs_destroyfile("chip.jpg");
    }
    
    /*
     *  ======== heartBeatFxn ========
     *  Toggle the Board_LED0. The Task_sleep is determined by arg0 which
     *  is configured for the heartBeat Task instance.
     */
    Void heartBeatFxn(UArg arg0, UArg arg1)
    {
    	SOCKET msg;
    
        while (1)
        {
        	Mailbox_pend(mailbox0, &msg, BIOS_WAIT_FOREVER);
        	System_printf("msg: %d\n", msg);System_flush();
        	sendWebsite(msg);
        }
    }
    
    /*
     *  ======== main ========
     */
    int main(void)
    {
        Task_Params taskParams;
        /* Call board init functions */
        Board_initGeneral();
        Board_initEMAC();
        Board_initGPIO();
    
    
        /* Construct heartBeat Task  thread */
        Task_Params_init(&taskParams);
        taskParams.arg0 = 1000;
        taskParams.stackSize = TASKSTACKSIZE;
        taskParams.stack = &task0Stack;
        taskParams.priority = 1;
        Task_construct(&task0Struct, (Task_FuncPtr)heartBeatFxn, &taskParams, NULL);
    
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

    Unfortunately the browser does not recognize any data that has been sent.
    So how can I transfer the socket to the task and send html code within the task?

  • Hi,

      When you click the getTime.cgi on the browser, did the getTime() function unblock the mailbox0 handle? In another word, if you put a breakpoint at SendWebsite(), did it arrive there?

      I'm not an expert on the TI-RTOS efs_createfile. I'm just not sure if what you do is the right way to plug the CGI function using task. What if instead of calling sendWebsite() in the heartBeatFxn, you simply toggle a GPIO pin. Will the pin toggle? I just wanted to isolate the problem. 

      I also assume in your .cfg file you create the mailbox object with correct message size and number of messages. 

  • Hello,

    the mailbox has a size of 4 bytes.
    And I can confirm that the Mailbox_post function unlocks the Mailbox_pend and the task is running.

    The question arises whether it is possible to send the html page within the task?
    I can't get any further here.

  • Hi,

      Please refer to the NDK API user's guide for detail.  After some searching, this is what I can find that may explain calling the cgi function in a task that may not work like you do. http://software-dl.ti.com/simplelink/esd/simplelink_msp432e4_sdk/2.30.00.14/docs/ndk/NDK_API_Reference.html#e-web-programming-with-the-http-server

     

    E Web Programming with the HTTP Server

    The easiest way to get information from an embedded network device is through the web server. The HTTP server pulls files from the embedded file system (EFS) that is included in the NDK software package’s OS adaptation layer. These files can be compiled into the application, located on a network file system, a memory-based file system, or on a physical disk interfaced to the DSP. The NDK HTTP server accesses files through the EFS application interface, which can be ported to any file system desired. The server supports the HTTP/1.0 protocol.

    Common Gateway Interface (CGI) programs execute on a web server and process input from a user. They are useful as interfaces to services running on the device. Writing CGI programs for the NDK is relatively simple and only requires a few specific functions. A single CGI interface function can be written to support both HTTP POST requests and GET requests.

    The CGI program is built from a single C callable entry-point (or CGI function). Each CGI function is called on its own independent task thread. The task threads are created with a priority of OS_TASKPRINORM and a stack size of OS_TASKSTKHIGH. Note that consecutive calls to the same CGI function will not be on the same task thread. Thus, CGI functions cannot share sockets from one call to the next. In general, there is no persistent data in a CGI function.

    Also, file detection of CGI functions is done purely on the file extension. If the file ends with .cfg (case insensitive), then a POST or a GET of the file will result in a call to the CGI function mapped to that filename. A POST call to a non-CGI file is not allowed.

  • Hi,

      I have done more searching. I wonder if you add fdOpenSession() and fdCloseSession() to your task creation will make a difference. Please refer to section 3 of the TI-RTOS NDK API user's guide for details. https://software-dl.ti.com/simplelink/esd/simplelink_msp432e4_sdk/2.30.00.14/docs/ndk/NDK_API_Reference.html. Below is a snippet of the notes. Here you will see several ways to handle the file descriptor. 

    3 Sockets and Stream IO API

    This chapter describes the socket and file API functions.

    3.1 File Descriptor Environment

    In most embedded operating system environments, support for file descriptors varies greatly. In most cases, only the bare minimum functionality is provided, and trimmed down support functions are provided using the common reserved names (read(), write(), close(), etc.).

    As this stack supports the standard sockets interface functions, and these functions require file descriptor support, the stack provides its own small file system. This section describes the basic mechanics of the file system.

    3.1.1 Organization

    The basic building block of the stack code internally is an object handle. Internally to the stack, both sockets and pipes are addressed by object handles. However, at the application level, sockets and pipes are treated as file descriptors. The file descriptor contains additional state information allowing tasks to be blocked and unblocked based on socket activity.

    The stack API supports the use of file descriptors by adding a file descriptor layer of abstraction to the native operating environment. This layer implements the standard sockets and file IO functions. The stack works by associating a file descriptor session with each caller’s thread (or in this terminology, task). In this system, each task has its own file descriptor session. The file descriptor session is used when the task needs to block pending network activity.

    Note that although file descriptors can be used in classic functions like select(), in this implementation, they are still handles, not integers. For compatibility, network applications must use the NDK header files, and use INVALID_SOCKET for an error condition (not -1), and refrain from comparing sockets as <0 when checking for validity.

    3.1.2 Initializing the File System Environment

    To use the file system and socket functions provided by the stack, a task must first allocate a file descriptor table (called a file descriptor session). This is accomplished at the application layer by calling the file descriptor function fdOpenSession().

    When the task is finished using the file descriptor API, or when it is about to terminate, the function fdCloseSession() is called.

    3.1.2.1 When to Initialize the File Descriptor Environment

    For correct stack operation, a task thread must open a file descriptor session before calling any file descriptor related functions, and then close it when it is done.

    The simplest way to handle the file descriptor session is to call TaskCreate(), which handles opening and closing the file descriptor session internally.

    Another way to handle the file descriptor session is for the task to open a file session when it starts, and close the session when it completes. For example:

    Socket Task:

    void socket_task(int IPAddr, int TcpPort) 
    {
        SOCKET s; 
    
        // Open the file session
        fdOpenSession(TaskSelf());
    
        < socket application code > 
    
        // Close the file session 
        fdCloseSession(TaskSelf()); 
    }

    Another method is for the task that creates the socket task thread to open the file descriptor session for the child thread. Note that the parent task must guarantee that the child task’s file session is open before the child task executes. This is done via task priority or semaphore, but can complicate task creation. Therefore, it is not the ideal approach.

    It is also possible to allow a child task to open its own file session, but allow the parent task to monitor its children and eventually destroy them. Here, the parent task must close the file session of the child task threads it destroys. The child task then blocks when finished instead of terminating its own thread. The following example illustrates this concept:

    Child Socket Task:

    void child_socket_task(int IPAddr, int TcpPort) 
    {
        SOCKET s;
     
        // Open the file session 
        fdOpenSession(TaskSelf()); 
    
        < socket application code > 
    
        // We are done, but our parent thread will close 
        // our file session and destroy this task, so here 
        // we just yield. 
        TaskYield(); 
    }

    The parent task functions would look as follows:

    Parent Task Functions:

    void create_child_task() 
    { 
        // Create System Tasks 
    
        // Create a child task 
        hChildTask = TaskCreate(&child_socket_task, ?); 
    } 
    
    void destroy_child_task() 
    { 
        // First close the child's file session 
        // (This will close all open files) 
        fdSessionClose(hChildTask); 
    
        // Then destroy the task 
        TaskDestroy(hChildTask); 
    }

    3.1.2.2 Auto-Initializing the File Descriptor Environment

    For TI-RTOS Kernel users who configure their application using XGCONF or a *.cfg configuration file, the calls to fdOpenSession and fdCloseSession can be configured to be called automatically. This is achieved by setting the following configuration parameter:

    var Global = xdc.useModule('ti.ndk.config.Global.xdc');
    Global.autoOpenCloseFD = true;

    Setting this parameter to true causes calls to fdOpenSession and fdCloseSession to be made automatically in the TI-RTOS Kernel Task module’s create hook function and exit hook function, respectively.

    Note that the Global.autoOpenCloseFD parameter is only supported for dynamically-created Tasks created from within a Task context (that is, from within another running Task function). Tasks created statically in the configuration or dynamically in main() or a Hwi or Swi thread do not support this feature.