This project is about controlling an FM radio chips by using an Arduino and some optional components like an LCD display, rotary encoder, a LCD+Keyboard shield and Ethernet Shield to build a standalone radio. It comes with several examples for different configurations.
I like listening to the radio while working at home and prefer music and information over the typical advertising+movies+series mix that is on the tv. So this project was started and ended (up to now) with this implementation.
The WebRadio example is the most complex example I implemented for the Radio Library and is indeed the code tht now runs in the radio next to my working place.
Inside this boy there is a Arduino MEGA with an Ethernet Shield and the SI4705 board from ELV including an amplifier for the speakers. The software also has a lot of technologies that work together:
The code for driving the radio chip, the LCD m the rotary encoder and the serial interface is essential the same as in the LCDRadio project. Rellay new to this example is the web server implementation that covers all kind of http communication. Soe the main explaination for this example really goes to the web server implementation.
This sketch is the largest I wrote for the Arduino and it doesn't fit into the program memory and RAM of an Arduino UNO so I had to use an Arduino MEGA. Especially RAM helps to speed up the web server.
When implementing a complex sketch that handles mutliple IO devices it is a good idea to implement a separate loop function for every IO component. These specific loop functions can implement the proper functionality for this IO device and should return as fast as possible.
In the global loop() functions all the sepcific loop functions are called in a row to give every component the chance to run:
/// Constantly look for the things, that have to be done. void loop() { unsigned long now = millis(); loopWebServer(now); /// Look for incomming webserver requests and answer them... loopButtons(now); /// Check for changed signals on the buttons and rotary encoder. loopSerial(now); /// Check for serial input commands and trigger command execution. loopRadio(now); /// Check for new radio data. loopLCD(now); /// Check for new LCD data to be displayed. } // loop()
There is a basic WebServer example comming with the library for the Ethernet Shield. It's very simple and shows the basic structure for waiting for new server requests and how to answer them by sending data back to the client. However there are some things that have to be changed for a good performing webserver.
When writing network applications it is really important to look into the network layer and check the results. I did that with the Wireshark tool that can sniff all packets on the network. So here are some things I discovered and like to tell you (not to do).
After sending a http answer back to the client it is important to stop the communication on the TCP level by closing the client connection using the stop() function. This helps in cleaning up the incomming requests. In some cases the browser side doesn't close the connection by itself and because Arduino only can answer to one client at a time this trick helped to fix a lot of hanging requests. This is a http 1.0 compatible behaviour.
In multithreading web servers (IIS, Apache Tomcat, ...) in contrast it makes more sense to keep the connection and reuse it.
Some WebServer implementations you can find code like this:
client.write(file.read()); // send web page to client
Here the content of the file is read from the SD card one byte at a time. There is a huge overhead in that function just to find the next byte available that has to be executed every time. A better approach is to use another function that passes a buffer:
len = file.read(buffer, sizeof(buffer)) client.write(buffer, len);
It’s up to 10 times faster to work with a buffer.
It seems so easy defining strings in the PROGMEM space to save memory from the ram. Here is an example that works:
#define HTTPERR_404 F("HTTP/1.1 404 Not Found\r\n") _client.print(HTTPERR_404);
When looking at the packets of network data by using a sniffer you can see that all the characters of the string are sent out one by one in a separate package. This is because the characters are pulled out of the PROGMEM or Flash space one by one and immediately sent out causing a 1 char package on the net as you can see in the log of a network sniffer:
The 60 bytes packages only transfer one character payload. So it is necessary to avoid using F() strings as a parameter - but wait for the StringBuffer!
The main problem with the speed of an Arduino as a web server is to use optimized approaches for sending out data that is normally text. So I have built a helper class called StringBuffer that helps filling small text fragments into a larger string and sending out that string only at the end using only one call to the client.
Have a look at the functions this library offers. It’s not a library and you can add the StringBuffer.h file to your own sketch and use / extend the functions easily.
The first implementation of the “file not found” situation was:
// Response no and send not found html void respondHeaderNotFound() { client.print(HTTPERR_404); client.print(HTTP_GENERAL); client.print(HTTP_ENDHEAD); } // respondHeaderNotFound()
And this ended in 84 packets with on character using up to 60 bytes for the complete packet.
Very network consuming :-( and slow too :-((
By using the StringBuffer implementation it is possible to collect all the text and sending it out in one piece:
// Response no and send not found html void respondHeaderNotFound() { StringBuffer sout = StringBuffer(_writeBuffer, sizeof(_writeBuffer)); sout.append(HTTPERR_404); sout.append(HTTP_GENERAL); sout.append(HTTP_ENDHEAD); client.print(_writeBuffer); } // respondHeaderNotFound()
Now only 9 packets are sent out:
With one of my Ethernet shields I had the problem that the ethernet communication was unable to start. The problem lies in the reset mechanism and is described in the Arduio forum: http://forum.arduino.cc/index.php?topic=28175.msg208411#msg208411
The propose to add another 100nF capacitor to the reset line and ground.
After changing the Ethernet shield to a newer board revision the problem disappears so swapping the board may also solve the problem.
Imprint License This content is part of the http://www.mathertel.de/ web site.