Showing posts with label atmel. Show all posts
Showing posts with label atmel. Show all posts

Sunday

nRF24l01+ Library Roundup
Overview and Status of RF24 Arduino/Linux Comm. Stack

Six, seven years? Has it really been that long? According to GitHub that is in fact the case, as my first commits to my own fork of the RF24 library took place in early 2014. It all started by identifying existing limitations in the available libraries and working to achieve the highest level of performance and reliability possible. The main fork for RF24 at the time was written and maintained by ManiacBug, but he dropped off the scene shortly after publishing this very nicely designed library. Other users attempted to add support for various devices and address certain bugs, but nobody had really taken a thorough look at the capabilities of the hardware vs what was achievable at the time. Only through a lot of reading, testing and learning to program was I able to make those initial changes to begin work improving the RF24 core library. Looking back now, I am very grateful that I picked ManiacBugs code base as a place to get back into programming in C/C++, as it was well thought out and I discovered ways of doing things that I previously did not understand or know about.

And with that began a long and arduous journey into the internal workings of nrf24l01+, Arduino (AVR) and RPi (Linux) devices with the stated goal of optimizing the library to whatever extent possible. Once the RF24 library began to take shape, I began looking at the RF24Network library, another very nicely designed bit of code by ManiacBug, but it had its limitations and problems. Many issues were addressed, features and functionality added in order to push the nrf24 devices to the test in a multi-device scenario. It performed much better than expected using this OSI Layer 3 (network) library.

At that point, I began to really understand the OSI model, how the different layers actually work together and what they do down the last bit. Based on a number of conversations and user input, I came up with the idea for RF24Mesh, which is basically another layer on top of RF24Network -> RF24 that handles addressing, similar to DHCP, but able to verify connectivity and reconnect nodes at any point in the mesh. This allows nodes to move around and reconnect quickly as required while maintaining the mesh structure. The Network & Mesh layers use a 'master' node that acts like the gateway in a standard IP network, and it provides addressing and resolution for RF24Mesh.

Based on input and discussions with users and the performance of the radio modules when used in a multi-device mesh/network scenario along with the emergence of IoT and sensor networks, it seemed like the thing to do would be to work my way up the OSI model, so that is exactly what I did. Upon discovering the uIP stack for Arduino, I realized it would be possible to add ip4 support to some of the smallest devices like ATmega328 AVRs, and larger devices like RPi could just use their own IP stack and encryption etc. The uIP stack and related software was quite a challenge to understand and implement, as uIP is written in C and is designed to be as small and lightweight as possible as opposed to being human readable and easy to understand. The UIPEthernet library was also essential in helping me to understand and implement RF24Ethernet.

With that, an OSI RF24 comm. stack was established, with RF24, RF24Network, RF24Mesh, RF24Ethernet and RF24Gateway libraries all working together and inter-operational at any of the layers. This means that on a RF24Gateway/RF24Ethernet mesh network incorporating RPi and Arduino nodes, devices can choose to operate using OSI layer 2,3,4,5 and/or 7 (Data Link, Network, Transport, Session, Application). When using the higher layer libraries (RF24Gateway & RF24Ethernet) standard IP connectivity is established and devices can utilize standard encryption & authentication protocols (SSH, HTTPS etc.) to secure their traffic as per the OSI model.

The RF24 Communication Stack vs The OSI Model

With the associated libraries and enhanced functionality, the RF24 comm. stack is able to provide IoT and sensor network connectivity and capabilities to suit many different scenarios at a very low price point with much lower power consumption than WiFi networks. With the production of many devices like RPi, ESP8266 and ESP32 which support WiFi, users can construct and deploy IoT networks very easily, extending them as required.

Even now, it is really kind of cool to witness the system in action, using these little radios on tiny little computers we refer to as 'Arduinos'. The methodology and processes in place to manage a network of devices, fragmentation & reassembly, mitigate wireless data collisions and ensure delivery of data are quite effective, from the radio hardware and auto-ack functionality, all the way up to the TCP/IP and Mesh levels. With TCP/IP for example, using Raspberry Pi devices, the MTU is 1500-bytes, so each packet can require up to 63 sequential, successful data transmissions at the core RF24 layer, but this still works relatively well in real-world testing/usage.

It has been a lot of fun and a great learning experience so far. The creation of RF24Gateway really allowed the limits of the radios to be tested, and provided an excuse to play around with different network/IP configurations, routing scenarios and traffic handling at any level of the OSI model. A full and complete understanding of how devices operate on the internet or similar networks helps dramatically in troubleshooting, testing and development of systems and software that operate using these protocols.

Going forward, the development of the RF24 libraries has slowed significantly along with bug reports and issues. My focus has recently been on cleaning things up, finding and fixing bugs, mostly in the higher layers, and improving the user interface via examples and documentation. I had previously almost given up on the RF24Gateway and Ethernet layers as being too difficult to fix, but with the rise of more powerful MCUs and the potential for expanding their capabilities, it seems worthwhile to take another crack at working out bugs and issues.

 It would be nice to find another similar radio device with lower power consumption and/or more advanced capabilities and throughput to create more robust networks and mesh capabilities. The current design allows for speeds up to about 20KB/s over IP (RF24Gateway) using two Linux devices, but it would be nice to have a higher throughput to allow for more nodes, longer range and more advanced communication scenarios.

In conclusion I want to extend a big thank you to everybody who contributed ideas, analysis, information and programming skills along the way. An extra thank you to Avamander (GitHub) who has played a big part in ongoing maintenance, support and development!

Update: Aug 2020

Wow, I mentioned that I would be working on finding and fixing bugs due to the slowdown involving support, dev and maintenance etc, but did not expect to spend so much time, or that I would be able to identify so many issues, their root cause and a solution. Some pretty significant issues have been fixed throughout the RF24 stack, including 1 major bug in RF24, affecting all libraries, a number of bugs/issues affecting functionality and reliability in RF24Network and RF24Mesh, and a memory issue in RF24Ethernet affecting stability.
RF24Gateway has been updated with better handling of interrupts, a few corrections and the first release made, v1.0 due to all the issues that have been addressed. Again, a portion of these improvements are due to assistance, testing and input from a select few users. Users should notice a marked improvement in stability, functionality and the ability to recover from significant errors/hardware issues.

RF24 Communication Stack:
https://github.com/nRF24
https://nrf24.github.io/RF24Gateway
https://nrf24.github.io/RF24Ethernet



















Wednesday

RF24 Addressing: A review of NRF24L01+ radio addressing
Pipes, Addresses and Acknowledegments


There still seems to be a fair bit of questions, misconceptions and misunderstandings regarding the nature of RF24 radio addressing and how to set up communication scenarios. This is a review of RF24 pipes, addressing and acknowledgements that should provide any user with enough information to set up any supported configuration.



Acknowledgements (ACKs):

The radios support an automatic acknowledgement feature, enabled by default, in which the receiving radio switches into transmit mode after reception, and acknowledges reception of data.

1. A sends to B
2. B responds to A
3. A is now aware that B received the payload successfully

The acknowledgment process is very important to understand, because correct addressing is required for this feature to function properly.




Simplest Explanation/Rules of Addressing (Using ESB/Auto-Ack):

1. When using Auto-Ack, every radio can utilize a maximum of 6 unique addresses at once, which other radios can send data to.

2. Each address is assigned to a logical "pipe", and each radio has only 6 pipes that can be assigned addresses.

3. Since auto-ack implies a bi-directional communication channel, each address/pipe can only support communication to a single radio using acknowledgements. If two or more devices send data to a single pipe/address, auto-ack should be disabled to prevent ACK spamming and erroneous acknowledgements.
   Exceptions:
   a: Disabling Auto-Ack - This prevents excessive spamming of acknowledgements and errant acknowledgement.
   b: Timing of Transmissions - Using a token-ring style network or other method of preventing simultaneous transmissions would allow multiple radios to send data to the same pipe/address while maintaining the reliability of acknowledgements.

Note: At 1MBPS, a transmission can take up to 85ms or so, and overlapping transmissions are likely with any significant traffic

4. Per manufacturer design, the addresses assigned to pipes 1-5 must be the same, except for a single byte.



Basic Scenario - One to One Communication with AutoAck:

For purposes of standardization and to allow the system to be scaled, it is generally recommended to use two radio addresses. With auto-ack disabled, a single address can be used for all radios, or groups of radios.


Code:
Radio #1:
byte address_1[] = {0xCC,0xCC,0xCC,0xCC,0xCC};
radio.openReadingPipe(1,address_1);
Radio #2:
byte address_2[] = {0xC3,0xCC,0xCC,0xCC,0xCC};
radio.openReadingPipe(1,address_2);


Process - Radio #1 sending to Radio #2:

1. Radio #1 opens pipe 0 for writing (and internally, reading) using address of Radio #2 (CCCCCCCCC3)
2. Radio #2 receives the data on the first attempt, and (internally) opens a writing pipe using its own address (CCCCCCCCC3) and sends an acknowledgement back to Radio #1
3. Radio #1 receives the ACK, so does not engage in a series of timed retries





Basic Scenario - One to One or Many to Many Communication with Multicast:

With AutoAck disabled, radios can share the same address, and the system can still be scaled to support any number of devices.

Code:
byte address_1[] = {0xCC,0xCC,0xCC,0xCC,0xCC};
radio.setAutoAck(0);
radio.openReadingPipe(1,address_1);

In this situation, any radio can send a message to all other radios sharing the same address:

Code:

radio.stopListening();
radio.openWritingPipe(address_1);
radio.write(&data,sizeof(data));
radio.startListening();





Simple Mesh Scenario - Five-to-Five Communication with AutoAck:




In the above scenario any radio can send or receive data from any other radio in the mesh, thus creating a 'true mesh' of 5 nodes.

Radio #1 will always open a writing pipe using the address assigned to pipe#1 of the recipient.
Radio #2 will always open a writing pipe using the address assigned to pipe#2 of the recipient.
Radio #3 will always open a writing pipe using the address assigned to pipe#3 of the recipient.
... and so on

Process Confusion:
In this example of addressing, if Radio#2 and Radio#3 were to both open a writing pipe to Radio#1 using the same pipe/address, there would be confusion with acknowledgements. Radio#1 will respond to both devices with acknowledgements, since they have both opened a writing and reading pipe using the same address.




Complex Network/Mesh Scenario - Many-to-Many Communication with AutoAck:


As can be seen, this format of addressing is very similar to creating sub-nets with IPV4 addressing, except here we are using HEX instead of decimals, and there are 5 bytes per address instead of 4. Each radio is linked as shown in the image below, and each radio has a unique communication channel (pipe/address combination) to use when communicating with any of its parent or child nodes.

Due to the correlation of addresses and associated topology, each radio only needs to be aware of its parent and child nodes. Any traffic sent to a given node will either be a: Accepted b: Routed to parent c: Routed to a child

Address configuration, routing etc, can be managed very simply by masking and bit-shifting.





In the above example, pipe0 is NOT assigned a reading address, leaving it available to be used for broadcast or multicast. With AutoAck disabled on a single pipe, or by using selective acknowledgements, all radios or groups of radios can share an address, thus all radios will receive all messages sent to that address.

See the RF24Network Documentation and Source Code for more information regarding this type of configuration.



Summary:

In many low-throughput situations, the radios will appear to function correctly even with improper addressing, and may not cause any obvious problems, however, performance and functionality will most certainly be affected.

In any situation where reliability and acknowledged payloads are important, proper addressing and an understanding of the underlying processes behind acknowledged payloads is very important.



Tuesday

Arduino Audio: WAV Playback from SD Card  
*TMRpcm Library update*

Due to popular demand, the PCM audio library now supports 32khz sample rate audio for improved sound quality, and better performance at lower rates. Some of the notable improvements in this round of updates are documented below. See the Wiki on GitHub for usage information.

Multi Track Playback
Dual tracks can be played at once on separate pins. Limited to about 16-20kHz sample rate, see wiki page for updated usage and info.

SdFat Library Support
The library now optionally supports direct use of the SdFat library for lower program and memory usage and increased performance.

Metadata (ID3v2.3 and Info/List) Tag Support
The library now has a configurable option ( pcmConfig.h ) to handle and bypass metadata when reading audio files. It also provides limited support for retrieving song, artist and album information from audio files. Standard info (LIST) tags are supported as well as ID3 tags.

Easy WAV file generation and playback
One command to generate a standard WAV file and header, another to finalize it once data is written. Raw data from analog inputs or other sensors or information sources can be written to the file to generate digital audio that can be played on any device that supports WAV files, or easily converted to other standard formats.

Recording
Commands have been added to enable recording of WAV files to SD card. Performance is very much dependant on the write speed of SD card as well as the amount of SRAM used. Results seem to be ok with a class4 SD card.

Audio Formats and Options
The library is now capable of Stereo or pseudo 16-bit playback with a resistor ladder to a limited degree.
An option to operate using TIMER2 for compatility was added.
A function to start playback at a given number of seconds was added

Buffered SD Reads
After a bit of messing around with the standard SD library, I realized there was a buffered read that would improve the response quite a bit. It doesn't seem to be documented in the library reference, but the usage is as follows:

Normal Read:
byte buffer[32];
for(int i=0; i<32; i++){
   buffer[i] = File.read();
}

Buffered Read:
byte buffer[32];
int bytesRead = File.read((byte*)buffer,32);

*Update*PWM 'Pop' sounds on timer start/stop
*Edit* - There seems to be differences in how the timers are engaged on different boards. The library now autoselects between the ramping methods. See pcmConfig.h to manually select.
I suspected that the typical pwm 'popping' sound could be removed by creating an appropriate ramp, and was able to remove it via the following process on the Arduino Mega.

On Startup:
1. OCR1A and OCR1B are set for opposing PWM. If both are set at 0, one will be high and one will be low.
2. Before enabling the timer, OCR1A is set to 0, OCR1B set to maximum (Timer TOP value) so that there is no power flow on startup.
3. OCR1B is then ramped down to 0, and power is applied gradually
4. OCR1A and OCR1B can now be fed with the same value, without a popping noise

Between Tracks:
1. The OCR1A register always contains the value of the last sample from the previous track, unless disabled.
2. Read OCR1A, and read the first value of the next track.
3. Ramp the value of OCR1A from its current value, to the value of the next track 

On Disable:
1. Get the current value of OCR1A/OCR1B
2. Ramp OCR1B up to maximum (Timer TOP value) from the current value
3. Ramp OCR1A down to minimum (0) from the current value

On a Nano or Duemilanove board, the process is a bit different:

On Startup:
No ramp needed, handled by track transition ramp. There will be a pop the first time PWM is enabled, and no popping thereafter if disabled or enabled.

Between Tracks: 
Same as with Mega 

On Disable:
1. Get the current value of OCR1A/OCR1B
2. Ramp both OCR1A/OCR1B down to 0 from the current value

I applied a few more tweaks to bring the performance up a bit more, mainly converting 16bit variables to 8bit where possible, and improving on details of the playback process. The quality/oversampling option now defaults to 0. The update is available on GitHub:
Download

Monday

Arduino: High Speed (8Mhz) Signaling: NTSC TV Output



I recently purchased a cheap 7" color monitor with intentions of using it for FPV flying, but still have not received the transmitting equipment. Of course, in waiting, I began to wonder about interaction with an Arduino, and a few searches led me to the TVOut library, which achieves NTSC video output (B/W) using an Arduino. I downloaded the library, and the more I read through the code, the more I was intrigued by how exactly such fast and accurate timing was acheived.

To give an idea of the requirements, the horizonal sync signal occurs about 15750 cycles/second (every 63.5us), which is relatively easy for an Arduino to produce in itself. In between these signals however, the picture is drawn, which requires much faster signaling and more precise timing, into the Mhz+ range.

In addition to the TVOut library, I also found a simpler form of code here that uses the USART (Serial port) to form the video signal, using each bit in every byte sent to form individual pixels. The main problem I found with this code was timing. It uses sleep mode to help time the video display accurately, but there were still some issues with small video glitches that I could not resolve when replicating the code. It also uses manual writing of the hsync pulse, where the other method uses a timer generated signal.

The first thing to do was to understand the makeup of an NTSC signal. This page provided a straightforward explanation, along with the timing requirements. Then it was on to recreating a hybrid of the code mentioned above, in the simplest possible format. The first part was pretty straightforward, creating horizontal and vertical sync signals. An entire frame consists of 259 horizontal sync signals, and 3 vertical sync signals, which are simply inverted horizontal sync signals to make 262 lines. The following code will produce a sync signal on pin 12, which results in a blank black screen.


#define hLinesPerFrame  262
#define vSync 247
#define hSync 25

volatile unsigned int isrCounter = 0;

void setup(){
  pinMode(12,OUTPUT);
 

  //This sets up the timer to produce the horizontal sync pulses
  //ICR1 is the timer TOP value, OCR1B controls the hSync pulse width
  //OCR1A will control the timing of the video display
  ICR1 = 1019;

  OCR1A = 1;
  OCR1B = 30;
  TCCR1A =  _BV(WGM11) | _BV(COM1B1) | _BV(COM1B0);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A); 
}


ISR(TIMER1_COMPA_vect){
  ++isrCounter;

  switch(isrCounter){         
          case vSync: OCR1B = 1019-hSync; break;

          case vSync+3: OCR1B = hSync; break;
          case hLinesPerFrame:  isrCounter = 0; break;
        }
  }


void loop(){ 

}

So that part is easy enough, a few lines of code, a 1k resistor from pin 12, and we have a basic NTSC signal with vertical and horizontal sync. Actually creating video will be a bit more complicated, and I've chosen an Arduino Mega due to the amount of SRAM (8K) available, which is needed for higher resolutions.

What I found in experimenting with different methods for timing, is that the timing of interrupts on a standard Arduino board can vary quite a bit. Using sleep mode to idle the board prior to triggering works 98-99% of the time, but still leaves a few interupts coming a bit late, producing glitches in the video. The method used in the TVOut library appears to work the best, as it monitors the counter to trigger at exactly the same time every time. Of course, it is written in assembly, and I made a slight modification to exit immediatly if the wait time has been missed. My explanation may be a slight bit off technically, but should be pretty close:

static void inline wait_until(uint8_t time) {

__asm__ __volatile__ (
    "sub    %[time], %[tcnt1l]\n\t" //Subtract the low byte of TCNT1 from the wait time
    "brmi   102f\n\t"               //If result is negative, we missed it. Branch to 102

 "100:\n\t"                     
    "subi    %[time], 3\n\t"         //Subtract 3 from time
    "brcc    100b\n\t"               //Loop until we get a negative
    "subi    %[time], 0-3\n\t"       //Add 3 back to our value
    "breq    101f\n\t"               //If equal to 0, branch to 101 and delay 1/16 of a uS (No OPeration)
    "dec    %[time]\n\t"            //Decrement our value by 1
    "breq    102f\n\t"               //If equal to 0, branch to 102 and don't delay
    "rjmp    102f\n"                 //Else Jump to the end
 "101:\n\t"
    "nop\n"
 "102:\n"

     :                                 //Output variables (none)
     : [time] "a" (time),              //Input variables
       [tcnt1l] "a" (TCNT1L)

);
 

}

That takes care of the basic signaling, and provides accurate enough timing to begin looking at displaying the video. I chose to use the USART to draw the video, as it simplifies things quite a bit when compared to using functions written in assembly, and should be able to use a baud rate of 8Mhz in Master SPI mode according to the datasheet. The USART also includes a double-buffering capability, which seems to make it more suitable here than using SPI.




We can calculate our BAUD rate, and the required setting for the UBBRn register at maximum speed:

16Mhz / 2(UBRRn + 1)  -->  16 / 2(0 + 1)  -->  16 / 2 -->  BAUD: 8Mhz  UBRRn: 0

Now, to configure the USART, only two lines of code are required in setup() :

  UCSR1C = _BV(UMSEL01) | _BV(UMSEL00); //Set USART to Master SPI mode
  UBRR1 = 0;                            //Baud rate = 8Mhz



Then to create a buffer for the pixel data of 5400 bytes:
    int totalBytes = (400/8) * 108;   
  byte data[totalBytes];

Now, all there is to do is start drawing pixels at the correct time, which (simplified) is done with the following lines of code, which get added to the TIMER1_COMPA_vector interrupt shown above.

wait_until(153);                      //See the assembly function above

UCSR1B |= _BV(TXEN1);                 //Enable USART TX

for (int x =0; x < 400/8; x++){       //Load bytes until we have drawn 1 horizontal line:
  while ( !(UCSR1A & _BV(UDRE1))){ }  //Wait until the USART is ready for more data
  UDR1 = data[x+start];               //Load a byte
}

  
UCSR1B = 0 ;
                                             //Disable the USART TX




...And thats all there is to it. Of course, the last part of it requires a bit more code to control the screen position and count lines, etc, but those are the fundamentals of producing NTSC video with an Arduino. A complete sketch is posted below.

Notes:
a: The timing of the video display in relation to the horizontal sync pulse is very important. It specifically triggers before the h-sync pulse ends so as to have enough time to smooth out variations in triggering.
b: The loading of data into the buffer (UDR1) must be done as quickly as possible, and requires efficient code to work at 8Mhz.
c: Using the USART requires accurate screen positioning due to the pulse created whenever it is enabled. This creates a vertical line, which can be positioned off screen via the wait timing.
d: This code does not use the defined standard timing for parts of the signal, due to better results with slightly modified settings. (ie: 2uS horizontal pulse instead of 4.7uS)
e: The maximum resolution is mainly limited by the memory available, as 1 byte is required for every 8 pixels. (400 x 108 uses 5.4KB)

Summary:
For me, this was an interesting look at the maximum limitations of signaling using an Arduino, and gave me an opportunity to learn a little more about assembly code as well. I found a great tutorial here showing how to view your Arduino code in assembly, and it was a great help in beginning to understand the code behind the code.
All in all, I am pretty happy with the results, as the methods used above seem pretty comparable to the TVOut libary, but with a bit higher resolution. The main benefits of using the USART are simplified code, as the baud rate can be changed to scale the video horizontally, and feeding it data is very straightforward.

The code used to create the above video can be found here and borrows heavily from the code it is based on.






Saturday

Asynchronous WAV/PCM: Arduino Audio Library Update:

 This library allows asynchronous playback of WAV files using only an Arduino, SD module, and a speaker.

 A little while ago, I decided to create a library for simple wav file playback using an Arduino, since I couldn't find any that fit my needs

Logical Functionality: 

I posted two previous versions of this libary, one that used a buffer, and one that used interrupts to load the data. Each had its tradeoffs, and neither were perfect. The interrupt based version had noticeable sound quality issues, and the buffering version could not be easily stopped during playback, or the volume adjusted, etc.
 The problem as I understand it, is that a read from the SD card will actually read 512 bytes at a time, so the buffering interrupt would not always complete before the music interrupt was set to trigger next. Since only 1 interrupt will trigger at a time, timing was an issue and so created other issues.

Searching through the datasheet for some functionality that would allow me to do what I wanted, I stumbled across mention of 'nested' interrupts. It took a little bit of time to figure out exactly how to use them in this application, but here is a brief overview of how the timer and interrupts work together:

OVF: This is an interrupt overflow vector that is triggered everytime the timer 'overflows'. (every cycle) Here, it reads a byte from the buffer into OCR1A, and therefore changes the pwm duty every cycle (@16khz)

COMPB: This is an interrupt compare match vector that is triggered when compare match is made during the timing cycle (TCNT1 == ICR1). This interrupt vector is used to read data into the buffers. Can be interrupted by other interrupts via 'nested interrupts'.

a: Interrupt vectors enabled: OVF, COMPB
b: When COMPB is triggered, it disables itself, but leaves OVF enabled. Global interrupts are automatically disabled while an interrupt completes. To enable nested interrupts, global interrupts are enabled manually before reading from the SD card.
c: If ready to buffer data, it begins (OVF can now interrupt COMPB while it bufferrs data)
d: COMPB completes, and re-enables itself to trigger again while it waits to buffer more data

Thanks to nested interrupts, I finally have what I wanted, with the basic functionality one would expect. I think the code can still use a bit of tweaking though, since I haven't fully tested its limits.

Updated Features:
- Sound Quality/Distortion issues have been resolved
- Uses a single timer (timer1)
- Asynchronous (interrupt driven) playback and buffering allows other code to run while music plays 

iTunes Conversion: 
a: Click Edit > Preferences > Import Settings
b: Change the dropdown to WAV Encoder and Setting: Custom > 16.000kHz, 8-bit, Mono

c: Right click any file in iTunes, and select "Create WAV Version"

d: Copy file to SD card using computer

Function Usage:
tmrpcm.play("filename"); //plays a file
tmrpcm.speakerPin = 11; // set to 11 for Mega, 9 for Uno, Nano, etc
tmrpcm.volume(1); //raises or lowers the volume: 1 or -1
tmrpcm.disable(); //disables the timer on output pin and stops the music
tmrpcm.stopPlayback(); //stops the music, but leaves the timer running

Individual Files:

Library Package:
TMRpcm.zip (OLD)
(now hosted on GitHub here)

Updated: 
Added Functionality:
Automatic detection of sample rate (8000 - 22000Hz)
WAV format verification
Memory buffer 300 bytes
Phase/Frequency-Correct and Fast PWM modes
 
Added functions: 
tmrpcm.isPlaying();  //returns 1 if music playing, 0 if not
tmrpcm.pause();  //pauses/unpauses playback
tmrpcm.pwmMode = 1; //set to 1 for phase/frequency correct mode, 0 for fast pwm 
tmrpcm.volume(0); //CHANGED from prev version, now uses either a 1(up) or 0(down)

Tested with: Arduino Nano/328 and Mega2560 
TMRpcm.zip  (OLD)

(Current version on GitHub here)

Monday

WAV/PCM Library Update:

 After reading into the capabilities of the Arduino timers, it seemed possible to generate an audio signal from PCM/WAV data using a single timer. Reading through the documentation, and looking at examples like the Timer1/Timer3 libraries, I found that I could use OCRnA to control duty cycle, ICRn and prescale for frequency, and use an overflow interrupt to update the value of OCRnA according to the defined SAMPLE_RATE, all with one timer.

How it works:

16-bit timer 1 is used for compatibility with different Arduino boards, but 16-bit Timers 3, 4, or 5 could be used on a Mega also.

The timer is set to Phase and Frequency Correct Mode, and set to run at a defined sample rate. The settings for prescale (TCCR1B) and input capture (ICR1) are what determines the frequency of the PWM signal when timer 1 is used, and OCR1A controls the duty cycle.

 An interrupt is attached to trigger every time the timer hits bottom. (Generally 16000 c/s) At this rate, an interrupt is generated 16000 times per second, and a new value is set for the duty cycle(OCR1A), then a new value is buffered for the next cycle.

In short, a signal is generated at 16000hz. The length of time each cycle stays turned on is determined by the value read in from the WAV/PCM file, which is updated every cycle.

No buffering: I am not sure of read speeds for SD cards, but testing indicates slightly higher sample rates can be achieved with no modifications. The SD library appears to default to SPI_HALF_SPEED, but will leave that inquiry for another day...

Whats new:

a: This version is completely controlled by interrupts, allowing other functions to run while music is playing. 
b: There is no longer a requirement for a large memory buffer, bytes are loaded as required
c: Added function to raise/lower volume: tmrpcm.volume(1);
d: Due to interrupt-driven playback, ability to stop/start music at will is added 

Data Format: unsigned 8-bit pcm, 16khz sample rate

iTunes Conversion: 
a: Click Edit > Preferences > Import Settings
b: Change the dropdown to WAV Encoder and Setting: Custom > 16.000kHz, 8-bit, Mono

c: Right click any file in iTunes, and select "Create WAV Version"

d: Copy file to SD card using computer

Function Usage:

 TMRpcm tmrpcm;              //Declare new object
 tmrpcm.speakerPin = 11;    //set to 11 for Arduino Mega, 9 for Uno, Duemilanove, etc
 tmrpcm.volume(1);             // 1 to raise volume, 0 to lower volume
 tmrpcm.play("filename");     // plays an unsigned 8-bit wav file from SD card
 tmrpcm.stopPlayback();     //stops playback
 tmrpcm.playing();               //returns true during playback, false otherwise

Updated Files / Source: 

Notice: This version has audio quality issues. See newer blog post for updated version

Source:
Example: music.ino  //Plays music while blinking a LED via the loop function 

Library Package:
TMRpcm.zip (Current version here)

Tuesday


Arduino WAV Playback Direct from SD Card 

*TMRpcm Library beta released*

The Problem: 

I wanted to be able to play a variety of sound clips using the Arduino, but could only find examples or libraries using program memory or other such methods. There are music shields you can get, but no examples for playing raw files from an SD card that I could find.

The Solution - Build a Library:
  
My library is directly based on the code shown at arduino.cc/playground/Code/PCMAudio as well as the library shown at: hlt.media.mit.edu/?p=1963, both of which use PROGMEM.
This is also the first library I have written, so there may be a few items slightly off, especially at this point in developing it.

First off, I had no idea how to make this work, but since it could work from progmem, why not from an SD card? Some sort of buffering would be needed for sure but how much, how to implement, etc. were some of the issues that had to be figured out.

Since I had no idea what I was doing exactly or how I was going to do it, I attempted to convert the files/data from the above links into char or byte format and saving it directly to a file on the SD card with no spaces, commas, etc. and playing it using the same method. It actually worked!

Once I proved the concept, then it was a matter of finding the best/simplest way to format the data for playback. Checking into the format of WAV files, I realized that they can be saved into a format that I can read directly using an Arduino with an SD card. There is a small header at the beginning of the file, but then it is basically raw data. The data can be saved in an 8-bit format (0-255), which can be read into the Arduino, and written directly to the registers with no modification.

The next problem is that I have never written a library, and Arduino programming is somewhat new to me, although I have dabbled in various programming languages for "fun" over the years. Following the tutorials found online, I was able to turn my sketch into a library.

For me, the easiest method was to use iTunes to convert the wav files, but any PCM file in the correct format will work:


Click Edit > Preferences > Import Settings

Then change the dropdown to WAV Encoder and Setting: Custom > 16.000kHz, 8-bit, Mono

Now you can just right click any file in iTunes, and select Create WAV Version

Then just copy the file(s) to an SD card attached to an Arduino, and check out the library below, with included example sketch.


How it works:

Both the above example and library this was based on use PROGMEM to store the variables which drive the PCM signal. Since we are reading WAV files directly from SD, we can save as many as the SD card will allow, with general disregard for file size.

The library uses timers and interrupts to create a signal that runs at 16000 cycles/second. The signal is controlled by the variables we read in from a file. The file is read into a small buffer, and playback is started. While interrupts control the playback, the second buffer starts filling up with data, using the spare cpu cycles between interrupts, and resumes playback once the first buffer is 'emptied'. Then the first buffer starts loading data again, while the second is 'emptied' and so on. This allows a continuous stream of data to be available for playback. Testing seems to indicate a minmum requirement for about 400 bytes of total memory for a steady stream and/or reasonable sound quality. (soundBuff = 200)

How to load data: In order to load the needed data onto an SD card, the wave file must be in the correct format, or converted using iTunes and the instructions above. Basically, this can be done using any computer with an SD slot, using any method that outputs wav files in the correct format.

Conclusion: This is totally possible, and it now works! The sound quality is low, but for simple sound clips, this is reasonable.

Example:

File(s) are placed onto the root of the SD card, then the following sketch is run:

 _________________________________________________________________

#include <SD.h>                      // need to include the SD library
#define SD_ChipSelectPin 53  //example uses hardware SS pin 53 on Mega2560
#include <TMRpcm.h>           //  also need to include this library...

TMRpcm tmrpcm;   // create an object (tmrpcm) for use in this sketch

void setup(){
 

tmrpcm.speakerPin = 10;
tmrpcm.soundBuff = 500; //uses 1KB memory. Min setting is about 200 (400 bytes)
 
pinMode(10,OUTPUT); //speaker pin
Serial.begin(115200);
  if (!SD.begin(SD_ChipSelectPin)) {  //see if card present and initialized:
    Serial.println("SD fail");  return;   // don't do anything more if not
  }else{   Serial.println("SD ok");   }

  tmrpcm.play("music"); //file "temple" plays when arduino powers up, or reset
}

void loop(){
  
  if (Serial.available() ){
    if (Serial.read() == 'C'){ 
      tmrpcm.play("music"); //sending a C to serial port starts playback
    }
  }
}
_________________________________________________________________

Modify the filenames for tmrpcm.play() to match your file.

Function Usage:

play("myFile");
stopPlayback();

Files/Source:

Developed using Arduino IDE 1.0.1
This is more or less just proof-of-concept currently, and will only work on Arduino Megas currently.


Source Code (OLD):
   TMRpcm.h
   Example: music.ino

Original Library Package (OLD):
   TMRpcm.zip

Current version on GitHub (download)
See the Wiki for updated usage and info



RF24Ethernet w/lwIP - Reaching a Point of Long Term Stability

  RF24Ethernet w/lwIP - Reaching a Point of Long Term Stability Stable & Secure Communications with the RF24 Communication Stack  Since ...