Tuesday, February 1, 2011

Forwarding the serial port to the net

This section will discus reading the serial port and forwarding the content to a socket and vise versa in C++.  I will be using specific api from win32 so it will not build in linux.  If I ever see interest on getting it working on linux, I will write one for linux.  I will only cover briefly the socket information since there are so many in depth tutorials out there.

For starters we will need to create a new visual studio console application project.  This is pretty basic and could be different from version so I will assume you can do this on your own. I will be using visual studio 2010
Now that we have our applications, we need to include the following.
#include <stdio.h>
#include <winsock.h>
#include <Windows.h>
I read that some people have trouble with the linker depending on the order of the include.  This order worked for me.  I also put the includes in my precompiled header (stdafx.h).

we also need to include the lib file so the linker knows where to get the functions from.
simply add #pragma comment(lib, "Ws2_32.lib") after all your includes

Setting up the the socket
the first thing we need to add before using any winsoc, is WSAStartup.  It takes a version number as a parameter (we are using version 2) and a struct that will be use by its internals.
read more

WSAData wsaData;
if ( WSAStartup(MAKEWORD(2,0), &wsaData))
{
    printf("could not load WSAStratup");
    return -1;
}
Now we need to prepare our connection information.  We will use the sockaddr_in to store all the information needed for the socket.  There are a couple different types of sockaddr but for IPv4 sockaddr_in is the one we use. 
read more
The inet_addr function converts an IP string to a long used by winsock.
read more
and the htons converts the windows byte order to the network byte order as some system have different byte orders.
read more

SOCKET hSocket;
sockaddr_in addrInfo;
memset(&addrInfo,0,sizeof(sockaddr_in));

addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons(80);  /http port
addrInfo.sin_addr.S_un.S_addr = inet_addr("74.125.226.81");  //Google's IP adress


Now that we have all our information we need to create the socket and connect it.
we create the socket with the socket function. This function takes 3 parameters.
First we specify that the address is a IPv4 adress,  we what stream socket (goes in hand with tcp) as opposed to a data gram and we want to use TCP protocol.
read more
The connect takes the newly created socket and the addrInfo we created just 1 step before.

hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket==INVALID_SOCKET)
{
        EXIT("can't create Socket\n");
}
 if (connect(hSocket, (sockaddr*)(&addrInfo), sizeof(addrInfo))!=0)
 {
        EXIT("can't connect\n");
 }

Finaly I added this line so that when I read from the socket it does not block untill data is received
ioctlsocket(hSocket,FIONBIO,&timeout);

Setting up the Serial port
We will open the serialport using the function CreateFile.  The file that we will use is COM# where the # is the number of the comport.  For now I will use COM1.  We will also be using a struct called DCB.  This struct contains information about the serial port configurations that we will use.  Note that these settings must match with those used with you STM32 Discovery
read more
We will also use the function SetCommState.  This function simply applies the new settings.
read more

HANDLE hSerial;
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//Comm settings
DCB serialInfo = {0};
serialInfo.DCBlength=sizeof(serialInfo);
serialInfo.BaudRate=CBR_14400;
serialInfo.ByteSize=8;
serialInfo.StopBits=ONESTOPBIT;
serialInfo.Parity=NOPARITY;
SetCommState(hSerial, &serialInfo);

The Loop
Now we simply need to read from the serial port and send it to the net and then read from the net and send it to the serial port.  We will use ReadFile and WriteFile to read an write to the serial port. We will pass the following parameter. the handle to the Serial port, the pointer to the buffer, the size of the buffer or the content and the address to a variable that will contain the number of bytes sent or read.  we set the last parameter to null since it only used if you need to share access to the file and we don't.

We will be using recv and send to read and write to our socket.  These functions take similar parameters except instead of passing the adress to a variable to know how many byte were read or sent, it is return.

here is my loop
char* readbuffer = new char[READ_BUFFER_SIZE];
char* writebuffer = new char[BUFFER_2_SIZE];
DWORD readlen = 0, bytesLeft = READ_BUFFER_SIZE;
int writelen = 0;
do
{
    //reset the variables
    readlen = 0;
    writelen = 0;
   
    //read data from serial port
    ReadFile(hSerial,readbuffer,READ_BUFFER_SIZE,&readlen,0);
    //was there actual data read?
    if(readlen)
    {
        //send the bytes to the serial port
        bytesLeft = readlen;
        while(bytesLeft)
        {
            //did we send all the byte?
            bytesLeft -= send(hSocket,readbuffer+(readlen - bytesLeft),bytesLeft,0);
        }
    }
   
    //read the data from the net
    writelen = recv(hSocket,writebuffer,WRITE_BUFFER_SIZE,0);
   
    //did we receive any thing?
    if(writelen>0)
    {
        bytesLeft = writelen;
        while(bytesLeft)  //did we send every thing to the serial port?
        {
            DWORD byteSent = 0;
            //send to the serial port
            WriteFile(hSerial,writebuffer + (writelen - bytesLeft),bytesLeft,&byteSent,0);
            bytesLeft -= byteSent;
        }
    }
    else if(writelen < 0)
    {
        //we go an error we can ignore it if err == WSAEWOULDBLOCK
        long err = WSAGetLastError ();

    }
    if(writelen > 0 && readlen == 0)
    {
        Sleep(100);
    }       
}while(writelen); // the socket is closed

now if you run this and you run your STM32 Dsicovery,  what ever you send to serial will then be sent to (google in this example) the net.  Try doing a http get and get this page?

Next I will be posting on how to get HTTPS working on the stm32 Discover using this as your connection.
I will also discuss what else you can use to connect to the internet

No comments:

Post a Comment