0

UDP Server for CC3000
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

UDP Server for CC3000

by briggsm on Mon Jun 09, 2014 7:30 am

Just wanted to post a solution that I managed to get working, of a UDP Server for the CC3000 ("Server" meaning you can throw lots of UDP packets at the CC3000 & it will listen to & handle that data.) Got input from a few different sources, and just tried to put it together. It's probably not the best written code, but wanted to get it out there, & maybe others can help make it better.

For the record, with this basic code, doing nothing much other than listening for UDP packets, we can handle about 60 packets / second. Each packet has 45 bytes (42 header + 3 data).

It consists of 2 files: UDPServer.cpp & UDPServer.h

UDPServer.h
Code: Select all | TOGGLE FULL SIZE
#ifndef UDPSERVER_H
#define UDPSERVER_H

#include <Arduino.h>

#include "Adafruit_CC3000.h"
#include "utility/socket.h"

class UDPServer {

private:
   uint16_t _port;
   int _socket;
   
public:
   UDPServer(uint16_t port);
   
   bool begin();
   bool available();
   int readData(char *buffer, int bufferSize);
};

#endif  // UDPSERVER_H


UDPServer.cpp
Code: Select all | TOGGLE FULL SIZE
#include "UDPServer.h"

UDPServer::UDPServer(uint16_t port) {
   _port = port;
   _socket = -1;
}

bool UDPServer::begin() {
   //Serial.print("START of udpServer::Begin() _socket: "); Serial.println(_socket);
   // Open the socket if it isn't already open.
   if (_socket == -1) {
      // Create the UDP socket
      int soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (soc < 0) {
         Serial.println("socket() call failed");
         return false;
      }

      sockaddr_in address;
      memset(&address, 0, sizeof(address));
      address.sin_family = AF_INET;
      address.sin_port = htons(_port);
      address.sin_addr.s_addr = 0;  // 0 => auto use own ip address
      socklen_t len = sizeof(address);
      if (bind(soc, (sockaddr*) &address, sizeof(address)) < 0) {
         Serial.println("bind() call failed");
         return false;
      }
      
      _socket = soc;
   }

   Serial.print("END of udpServer::begin() _socket: "); Serial.println(_socket);

   return true;
}

bool UDPServer::available() {
   timeval timeout;
   timeout.tv_sec = 0;
   timeout.tv_usec = 5000;
   fd_set reads;
   FD_ZERO(&reads);
   FD_SET(_socket, &reads);
   select(_socket + 1, &reads, NULL, NULL, &timeout);
   if (!FD_ISSET(_socket, &reads)) {
      // No data to read.
      //Serial.println("No data to read.");
      return false;
   }
   
   return true;
}

int UDPServer::readData(char *buffer, int bufferSize) {
   // If there is data, then stores it into buffer &
   // returns the length of buffer. (-1 if none)
   
   if (available()) {  // Make sure data is really available

      int n = recv(_socket, buffer, bufferSize, 0);
      
      if (n < 1) {
         // Error getting data.
         Serial.println("Error getting data");
         return -1;
      }
      
      return n;
   }
   
   return -1;
}


And to make it work, add something similar to your .ino file (you'll also need all the usual CC3000 includes, instanciations, etc.):
Code: Select all | TOGGLE FULL SIZE
#include "UDPServer.h"
...
#define UDP_READ_BUFFER_SIZE 20
#define LISTEN_PORT_UDP 2811
UDPServer udpServer(LISTEN_PORT_UDP);
...
void setup() {
   udpServer.begin();
}

void loop() {
   if (udpServer.available()) {

      char buffer[UDP_READ_BUFFER_SIZE];
      int n = udpServer.readData(buffer, UDP_READ_BUFFER_SIZE);  // n contains # of bytes read into buffer
      Serial.print("n: "); Serial.println(n);

      for (int i = 0; i < n; ++i) {
         uint8_t c = buffer[i];
         Serial.print("c: "); Serial.println(c);
         // ... Do whatever you want with 'c' here ...
      }
   }
}

briggsm
 
Posts: 44
Joined: Thu Oct 24, 2013 11:17 am

Re: UDP Server for CC3000

by adafruit_support_mike on Mon Jun 09, 2014 11:52 pm

Nice!

We have regular questions about setting up a UDP server with the CC3000, and every bit of reference code helps. Thanks for posting yours! ;-)

adafruit_support_mike
 
Posts: 63074
Joined: Thu Feb 11, 2010 2:51 pm

Re: UDP Server for CC3000

by briggsm on Fri Jul 11, 2014 10:47 am

I've finally gotten my project in a state where I think it can be shared, and I created a tutorial (with video demos, pictures, and a "how-to"):

Tutorial: http://briggs-inc.com/blog/colorring/
ColorRing Library: https://github.com/briggsm/colorring
ColorRing CC Library: https://github.com/briggsm/colorringcc

The project, in a nutshell, is:
2 Adafruit NeoPixel LED strips, connected to Arduino board, controlled from a web browser, over Wi-Fi.
Use the web browser to create & send commands to the LED strips, as well as send real-time color data to the LED strips.

briggsm
 
Posts: 44
Joined: Thu Oct 24, 2013 11:17 am

Re: UDP Server for CC3000

by adafruit_support_mike on Fri Jul 11, 2014 6:20 pm

Whoa.. that is *WAY* cool!

adafruit_support_mike
 
Posts: 63074
Joined: Thu Feb 11, 2010 2:51 pm

Re: UDP Server for CC3000

by bduthie on Mon Jul 14, 2014 9:30 pm

I agree. This is really cool.

Way over the top for me, but it works great.

One question:

How could I retrieve the IP address and port of the sending unit?
bduthie
 
Posts: 9
Joined: Mon Jun 30, 2014 10:15 am

Re: UDP Server for CC3000

by adafruit_support_mike on Tue Jul 15, 2014 4:38 pm

bduthie wrote:How could I retrieve the IP address and port of the sending unit?

You'd have to do a lot of digging.

The socket interface exists to put an abstraction barrier between the network layer and the application layer. As far as the program is concerned, an IPv4 socket looks just like an IPv6 socket, which looks just like a Unix socket, which looks just like a Datalink socket, etc. They're all presented to the program as a 'socket descriptor', which is just an integer that the OS (or in this case, the CC3000) uses to keep track of the connection-level data structures.

If you were working with a TCP connection, you could pull the peer address from the 'accept()' function, but UDP goes through the 'select()' multiplexer. That just returns the descriptor of whatever socket has information to read. That goes to the 'recv()' function, which takes a pointer to a buffer among its arguments.

The Unix network suite contains functions that take a socket decriptor as input and return things like the peer address and port number, but the Adafruit_CC3000 library doesn't implement those.

adafruit_support_mike
 
Posts: 63074
Joined: Thu Feb 11, 2010 2:51 pm

Re: UDP Server for CC3000

by bduthie on Wed Jul 16, 2014 12:42 am

Did some looking, and actually made some minor tweaks to UDPServer.cpp to use recvfrom instead of recv, and it works like a champ.
I'll try to post my code changes tomorrow.
bduthie
 
Posts: 9
Joined: Mon Jun 30, 2014 10:15 am

Re: UDP Server for CC3000

by adafruit_support_mike on Wed Jul 16, 2014 2:58 pm

Ah.. good catch. I'd forgotten that 'recvfrom()' takes a sockaddr pointer.

adafruit_support_mike
 
Posts: 63074
Joined: Thu Feb 11, 2010 2:51 pm

Re: UDP Server for CC3000

by briggsm on Thu Aug 07, 2014 9:54 am

For anybody interested, I added another short demo video (2 min.). LEDs in plexiglass, and shows off some clock functionality:
https://www.youtube.com/watch?v=9pgfKWsUrCQ

briggsm
 
Posts: 44
Joined: Thu Oct 24, 2013 11:17 am

Re: UDP Server for CC3000

by Benpart on Wed May 20, 2015 3:25 am

Hello,

I was inspired by your code to make my own udp server.
The goal is to provide the address of the server via a reply to a broadcast . Here is the code :

adafruit_cc3000_server.h
Code: Select all | TOGGLE FULL SIZE
class UDPServer
{
   private:
      uint16_t _port;
      int _socket;

   public:
      UDPServer(uint16_t port);

      bool begin();
      bool Reboot();
      bool available();
      int readData(char *buffer, int bufferSize, sockaddr* sender);
      int writeData(char *buffer, int bufferSize, sockaddr* sender);
};



adafruit_cc3000_Server.cpp
Code: Select all | TOGGLE FULL SIZE
UDPServer::UDPServer(uint16_t port) {
   _port = port;
   _socket = -1;
}

bool UDPServer::Reboot()
{
   if (_socket != -1)
   {
      int closeInt = closesocket(_socket);
      if (closeInt == -1)
      {
         Serial.println("Reboot() call failed");
         return false;
      }
      _socket = -1;
   }
   delay(400);
   begin();
}

bool UDPServer::begin() {
   //Serial.print("START of udpServer::Begin() _socket: "); Serial.println(_socket);
   // Open the socket if it isn't already open.
   if (_socket == -1) {
      // Create the UDP socket
      int soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (soc < 0) {
         Serial.println("socket() call failed");
         return false;
      }

      sockaddr_in address;
      memset(&address, 0, sizeof(address));
      address.sin_family = AF_INET;
      address.sin_port = htons(_port);
      address.sin_addr.s_addr = 0;  // 0 => auto use own ip address
      socklen_t len = sizeof(address);

      if (bind(soc, (sockaddr*)&address, sizeof(address)) < 0) {
         Serial.println("bind() UDP call failed");
         return false;
      }
      else
         Serial.println("bind() UDP OK");

      _socket = soc;
   }

   //Serial.print("END of udpServer::begin() _socket: "); Serial.println(_socket);

   return true;
}

bool UDPServer::available() {
   timeval timeout;
   timeout.tv_sec = 0;
   timeout.tv_usec = 200;
   fd_set reads;
   FD_ZERO(&reads);
   FD_SET(_socket, &reads);
   select(_socket + 1, &reads, NULL, NULL, &timeout);
   if (!FD_ISSET(_socket, &reads)) {
      // No data to read.
      return false;
   }
   
   //Serial.print("END of udpServer::available() _socket: ");
   //Serial.println(_socket);
   
   return true;
}

int UDPServer::readData(char *buffer, int bufferSize, sockaddr* sender) {
   // If there is data, then stores it into buffer &
   // returns the length of buffer. (-1 if none)
   socklen_t sendsize = 0;
   int n = recvfrom(_socket, buffer, bufferSize, 0, sender, &sendsize);
   if (n < 1)
   {
      // Error getting data.
      //Serial.println("Error getting data");
      return -1;
   }

   return n;
}

int UDPServer::writeData(char *buffer, int bufferSize, sockaddr* sender)
{
   int n = sendto(_socket, buffer, bufferSize, 0, sender, sizeof(*sender));
   if (n < 1)
   {
      // Error getting data.
      //Serial.print("Error sending data: ");
      //Serial.println(n);
      return -1;
   }
   
   return n;
}


Usage (the server responds to a broadcast containing "where are you arduino") :

Code: Select all | TOGGLE FULL SIZE
void Setup()
{
udpServer.begin();
}

void checkUDP()
{
  if ( udpServer.available())
  {
      sockaddr_in sender;
      memset(&sender, 0, sizeof(sender));
     
      char bufferUDP[22] = {0};
      int n = udpServer.readData(bufferUDP, 22, (sockaddr*)&sender);  // n contains # of bytes read into buffer
     
      if(n > 0)
      {
        if(strncmp(bufferUDP, "where are you arduino", 22) == 0)
        {
          Serial.println(F("Receive Broadcast\r\n"));
          delay(100); 
          uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
   
          if (!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
          {
            udpServer.writeData("Unable to retrieve the IP Address", 33, (sockaddr*)&sender);
          }
          else
          {
            delay(100);
            char ipstring[14] = {0};
            char buf[10] = {0};
            itoa((uint8_t)(ipAddress >> 24),buf,10);
            strcat(ipstring, buf);
            strcat(ipstring, ".");
            buf[0] = 0;
            itoa((uint8_t)(ipAddress >> 16),buf,10);
            strcat(ipstring, buf);
            strcat(ipstring, ".");
            buf[0] = 0;
            itoa((uint8_t)(ipAddress >> 8),buf,10);
            strcat(ipstring, buf);
            strcat(ipstring, ".");
            buf[0] = 0;
            itoa((uint8_t)(ipAddress),buf,10);
            strcat(ipstring, buf);
            buf[0] = 0;
           
            char resp[32] = {0};
            strncpy(resp, "IP=", 3);
            strncat(resp, ipstring, sizeof(ipstring));
            strncat(resp, ",", 1);
            strncat(resp, "Name=", 5);
            strncat(resp, AppareilId, sizeof(AppareilId));
            udpServer.writeData(resp, strlen(resp), (sockaddr*)&sender);
          }
        }
        else
        {
          for (int i = 0; i < n; ++i)
          {
             Serial.print(bufferUDP[i]);
          }
          Serial.println();
        }
      }
   }
}

void loop()
{
  if(cc3000.checkConnected())
  {
    checkUDP();
    delay(50);
  }
  else
  {
    cc3000.reboot();
     //here the code to connect to AP
    while(udpServer.Reboot() == 0); 
  }
}


Certainly not the most beautiful code , but it works.
Enjoy. ;O))))

Ben

Benpart
 
Posts: 2
Joined: Wed May 20, 2015 3:06 am

Please be positive and constructive with your questions and comments.