Phillip Stevens
Published © CC BY-NC-SA

XBee Walkie Talkie

Simple application for analogue signals (audio) on the AVR Arduino platform, demonstrating the Goldilocks Analogue (1284p UNO Compatible).

AdvancedFull instructions provided10,944
XBee Walkie Talkie

Things used in this project

Hardware components

Goldilocks Analogue
Still as prototype currently, but functionality can be recreated with MCP4822 DAC, Microphone Amplifier, and Headphone Amplifier, together with Arduino Uno.
×1
MAX9744
×1
MAX9814
×1
MCP4921 DAC
×1
Arduino UNO & Genuino UNO
Arduino UNO & Genuino UNO
×1
Arduino Wireless Shield (Xbee)
×1

Story

Read more

Schematics

Xbee Walkie Talkie

It is not absolutely correct, in that it is using a MCP4725 DAC (I2C) and not a MCP4822 DAC (SPI), but Fritzing didn't have the right Adafruit breakout board.

Also, it is drawn in one direction only... (with the exception of the Rx and Tx being interconnected).
The XBee boards simply replace the two wires connecting the Rx and Tx. Any radio set that can carry sufficient data would work.

Goldilocks Analogue Prototype 3 Schematics

Schematics for the DAC output and the Headphone amplifier.
Microphone input amplifier will be added on prototype 4.

Code

Code

C/C++
void audioCodec_dsp( uint16_t * ch_A,  uint16_t * ch_B)
{
	int16_t xn;
	uint8_t cn;
	/*----- Audio Rx -----*/

	/* Get the next character from the ring buffer. */

	if( ringBuffer_IsEmpty( (ringBuffer_t*) &(xSerialPort.xRxedChars) ) )
	{
		cn = 0x80 ^ 0x55; // put A-Law nulled signal on the output.
	}
	else if (ringBuffer_GetCount( &(xSerialPort.xRxedChars) ) > (portSERIAL_BUFFER_RX>>1) ) // if the buffer is more than half full.
	{
		cn = ringBuffer_Pop( (ringBuffer_t*) &(xSerialPort.xRxedChars) ); // pop two samples to catch up, discard first one.
		cn = ringBuffer_Pop( (ringBuffer_t*) &(xSerialPort.xRxedChars) );
	}
	else
	{
		cn = ringBuffer_Pop( (ringBuffer_t*) &(xSerialPort.xRxedChars) ); // pop a sample
	}

	alaw_expand1(&cn, &xn);	// expand the A-Law compression

	*ch_A = *ch_B = (uint16_t)(xn + 0x7fff); // move the signal to positive values, put signal out on A & B channel.

	/*----- Audio Tx -----*/

	AudioCodec_ADC( &mod7_value.u16 );	// sample is 10bits left justified.

	xn = mod7_value.u16 - 0x7fe0;	// centre the sample to 0 by subtracting 1/2 10bit range.

	IIRFilter( &tx_filter, &xn);	// filter transmitted sample train

	alaw_compress1(&xn, &cn);	// compress using A-Law

	xSerialPutChar( &xSerialPort, cn);	// transmit the sample
}

Code

C/C++
ISR(TIMER0_COMPA_vect) __attribute__ ((hot, flatten));
ISR(TIMER0_COMPA_vect)
{
#if defined(DEBUG_PING)
  // start mark - check for start of interrupt - for debugging only (yellow trace)
  PORTD |= _BV(PORTD7); // Ping IO line.
#endif

  // MCP4822 data transfer routine
  // move data to the MCP4822 - done first for regularity (reduced jitter).
  DAC_out (ch_A_ptr, ch_B_ptr);

  // audio processing routine - do whatever processing on input is required - prepare output for next sample.
  // Fire the global audio handler which is a call-back function, if set.
  if (audioHandler!=NULL)
    audioHandler(ch_A_ptr, ch_B_ptr);

#if defined(DEBUG_PING)
  // end mark - check for end of interrupt - for debugging only (yellow trace)
  PORTD &= ~_BV(PORTD7);
#endif
}

Code

C/C++
void DAC_out(const uint16_t * ch_A, const uint16_t * ch_B)
{
  DAC_command_t write;

  if (ch_A != NULL)
  {
    write.value.u16 = (*ch_A) >> 4;
    write.value.u8[1] |= CH_A_OUT;
  }
  else // ch_A is NULL so we turn off the DAC
  {
    write.value.u8[1] = CH_A_OFF;
  }

  SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC.
  SPDR = write.value.u8[1]; // Begin transmission ch_A.
  while ( !(SPSR & _BV(SPIF)) );
  SPDR = write.value.u8[0]; // Continue transmission ch_A.

  if (ch_B != NULL) // start processing ch_B while we're doing the ch_A transmission
  {
    write.value.u16 = (*ch_B) >> 4;
    write.value.u8[1] |= CH_B_OUT;
  }
  else // ch_B is NULL so we turn off the DAC
  {
    write.value.u8[1] = CH_B_OFF;
  }

  while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_A.
  SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.

  SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // Pull SS low to select the Goldilocks Analogue DAC.
  SPDR = write.value.u8[1]; // Begin transmission ch_B.
  while ( !(SPSR & _BV(SPIF)) );
  SPDR = write.value.u8[0]; // Continue transmission ch_B.
  while ( !(SPSR & _BV(SPIF)) ); // check we've finished ch_B.
  SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // Pull SS high to deselect the Goldilocks Analogue DAC, and latch value into DAC.
}

AVRfreeRTOS at Sourceforge

Repository of the AVR port of freeRTOS, including the DAC.h and Analogue testing files used in this project. Please DON'T use the linked github repository. Go to sourceforge for latest code. https://sourceforge.net/projects/avrfreertos/

Credits

Phillip Stevens

Phillip Stevens

16 projects • 77 followers
You can flog a dead horse to water, but the grass is always greener on the flip side.

Comments