Project Description

1. Introduction
After finishing the serial port version of my Arduino audio streaming project, I verified that the Arduino can easily handle a 20KHz sample rate and that the eight-bit resolution from the on-chip A/D converter doesn’t sound too bad. The next logical step if you happen to have an Ethernet shield laying around (and who doesn’t?) is to try and stream the audio signals over a local network.
I haven’t been able to find any easy way to stream audio data to a browser, much less from the Arduino, so I opted to use an ethernet version of the python audio playback/scope script. I was also able to use the same microphone amplifier circuit.

2. The Arduino Firmware
There are similarities between the serial port version and the ethernet version, but the main hurdle is controlling the Wiznet W5100 chip on the Ethernet Shield using embedded C. There are application notes on the Wiznet website but they are challenging to say the least. A more basic tutorial can be found at http://www.ermicro.com/blog/?p=1773, which is well worth a look. My firmware bears a suspicious resemblance to his, except that I’ve split the SPI and W5100 functions into separate modules.

1. The SPI Interface
The W5100 chip is accessed via the Atmega328’s built-in Serial Peripheral Interface (SPI), which are output via Port B. Initialization, shown below, configures Port B, and enables the SPI functions.

Once the SPI has been initialized, data can written to or read from a given on-chip address by sending a READ or WRITE op-code to the device as shown below:

3. W5100 Functions
A more detailed description of the functions can be found by examining my hopefully well-documented source files in the link below. I’ve kept some of the code from the ’emicro’ web-site for future use, but the following is a summary of the routines used in this application:

W5100_Init(void)
– sets the device MAC address, gateway IP address, subnet mask, and desired IP address. These addresses are usually determined in the DHCP process, but since I’m using a stand-alone Python Script, I need a more permanent IP address. What can I say? Its also easier and for a small network, shouldn’t present a problem.

void close(uint8_t socket) – marks the socket as closed

void disconnect(uint8_t socket) – sends a termination request to the remote peer

uint8_t socket(uint8_t sock, uint8_t eth_protocol, uint16_t tcp_port)
– initializes and opens a socket. Sockets are initialized with the selected protocol (TCP, UDP etc.) and port number (4096), then opened. Returns 1 if successful, 0 otherwise.

uint8_t listen(uint8_t sock)
– sets the W5100 to wait for a connection request from a remote peer. Returns 1 if the command was successful, 0 otherwise.

uint16_t send(uint8_t sock, uint8_t *buf, uint16_t buflen)
– transmits a buffer of bytes at address buf, and length buflen (512 bytes) over a socket sock. returns a 1 if the operation was successful, 0 otherwise.

4. Serial Port Functions
The serial port functions in stdio_setup are identical to the functions in the serial streaming application, except that the baud rate has been reduced to 19200 baud, and a DEBUG flag has been added to stdio_setup.h. The DEBUG flag has been added to allow debugging messages to be printed using the printf function during the development of the firmware. When in use, the DEBUG flag should be off, since print adds an unacceptable delay to the program, resulting in unintelligible audio.

5. The main() Module
The main module consists of two primary functions, data acquisition from the A/D converter, and control and data transfer to the W5100 chip and hence the network.
Data acquisition is almost identical to the method used in the serial port application. Timer 0 is configured to generate an interrupt after ever 50 usec, and the interrupt service routine copies data from the A/D converter to a buffer. In this application, two 512 byte buffers are used. While the first buffer is writing data to the W5100, the second buffer is being filled with data from the A/D converters. When the second buffer is full, a flag is raised, and the data is copied to the W5100, while the first buffer is filled with A/D data.
The data acquisition interrupt service routine is shown below:

The main() function initializes the various peripherals, then enters the data transfer loop which consists of a state machine which listens for connections from remote clients. The state machine monitors the status of a socket in the W5100’s internal status registers as detailed in the datasheet. When the socket is opened, it spends its time listening for new connections. When a new connection is established, data is transferred to the W5100 using the double buffering scheme outlined above:
The data transfer loop is shown below:

The complete Atmel Studio project and source code files can be found here: audioserv.zip

3. The Python Script
The Python script ethScope.py is also similar to the serial port version (Insert Link). The main difference being that this ethernet version has to establish a TCP connection with the Arduino server when the Run button is pressed, and the connection has to be closed when the Stop button is pressed.
The modified start_stop() function is shown below:

As shown, when Run is pressed, the function clears the queues, starts the audio stream if necessary, establishes the network connection, and starts the data acquisition thread. When Stop is pressed, the function stops the audio stream if necessary, suspends the data acquisition thread, and terminates the network connection.

The open_connection() function is shown below:

As shown, the function looks for the Arduino server at IP address 192.168.2.10 at port 4096. If no connection is found, the con_err flag is raised, an error message is generated, and the title displays the connection status. Otherwise, the script displays the address and port number in the title.
The data acquisition thread continuously checks the thread and network connection status then reads 512 block bytes from the network and combines them, four at a time, into a 2048 array for the GUI and audio playback queues. If the Stop button has been pressed, the network connection is terminated by closing the socket, then creating a socket for the next connection.
The data acquisition function is shown below, and the complete script can be found here: ethScope.zip

4. Conclusions
Getting a basic data acquisition/streaming network application is a good beginning for remote instrumentation and monitoring systems. In addition to custom network scripts and programs, it should be possible to stream data directly to a browser (with or without special plug-ins).

If youtube can do it so can I.

1. The Arduino IDE Version
One of the disadvantages of writing programs in traditional embedded C is that its difficult to use some of the Arduino C++ libraries such as, in this case, the Ethernet library. This gave me an incentive to try and port the ‘C’ version of my audio streaming server to the Arduino IDE, and adapt the Python script accordingly. The source code for the Arduino sketch and Python script can be found here  Arduino IDE Code.

1. The Arduino Program
As shown in the sketch, the Arduino code follows the structure of the ‘C’ version pretty closely. Since the Arduino uses Timer 0 for timekeeping, I’ve used Timer 1 to drive the A/D converter. Timer 1 is a 16 bit counter/timer with just enough differences from Timer 0 to make life interesting.

As shown, the first few lines are pretty straightforward. The Ethernet data is set up with at MAC address, IP address, a server at port 4096, and a potential cilent. The data buffers are set up as shown, and I’ve reduced the size from 512 to 256 bytes to save internal RAM space.

1.1 Timer1 Set-up
The most significant change in the code is the Timer 1 initialization. As in the ‘C’ version, a 20 KHz clock is used to re-trigger the A/D converter after a conversion has completed. To implement this, the Clear Timer on Compare Match (CTC) mode is used. In this mode, the counter counts to a certain value and toggles the bit which starts the A/D conversion. In addition to being a 16-bit timer, Timer 1 differs from Timer 0 in another respect. There are two Output Compare Registers (OCR1A and OCR1B) which toggle bits OC1A and OC1B respectively. The difficulty is that the A/D converter can only be triggered by OC1B on Timer1. In CTC mode, the Timer/Counter register (TCNT1) only resets depending on the value of OCR1A. As a result, both registers have to be used. OCR1A to maintain the CTC mode, and OCR1B to initiate the A/D conversion.
This is shown in the following code snippet:

The toggle frequency of OC1A and OC1B is given by the following formula:

f = fclk/(2*N*(1+OCR1A)

For the Arduino, fclk = 16MHz. N is the prescaler value which is selected as 8. A toggle rate of 20KHz is needed which means a square wave frequency of 10Kz, resulting in an OCR1a value of 99.

1.2 ADC Routines
The ADC initialization routine is the same as the ‘C’ version. The only difference is that OC1B is selected as the trigger source. Similarly, the ADC conversion complete interrupt service routine is the same as the ‘C’ version.

1.3 The Main Loop
The structure of the main loop is similar to the ‘C’ version, but since I’m using the Ethernet library, it’s somewhat simplified. The following code snippet, shows the main loop:

The loop starts by waiting for a remote client to make a connection. In the ‘C’ version, this was handled by socket functions. In the Ethernet library, a client must send data when initiating a connection. If the client disconnects and there is still data in the receive buffer, the Arduino will still maintain the connection, so the receive buffer must be cleared.
The loop then checks ‘flag’ to see if there is a data buffer available. If a buffer is available, ‘buffsel’ is used to select the buffer not currently being filled by the ADC ISR. The selected buffer is then sent to the client via the server.write() function. The socket functions in the ‘C’ version timed out when the client connection was lost. The server.write() function doesn’t seem to have this capability and will hang unless a proper socket disconnect command is sent from the client. This required a slight change to the Python GUI.

2. The Python GUI
The modified Python-based GUI is very similar to the ‘C’ version except for some minor (but necessary) changes.

2.1 The Socket Connection
As mentioned, the Arduino Ethernet library requires data following a socket connection. This is shown in the following code snippet:

The actual data sent doesn’t matter, since the Arduino program clears the receive buffer.

3.2 Graceful Exit
Since the Arduino server.write() function doesn’t detect a client connection loss, a means of disconnecting the program when exiting (if necessary) was added:

As shown, when the program is exiting, the status is checked and if necessary, the audio stream and socket connection are terminated.

3.3 Buffer Size
As mentioned in the Arduino code, the data buffers were changed from 512 to 256 bytes. This is reflected in the following line:

 

For more information:

http://dbc-projects.net/arduino-audio-server/