Working on an IoT project.
Environment:
- Adafruit Feather M4 Express
- Adafruit MusicMaker FeatherWing (amplified, this is a VS1053 at heart)
- Silicognition PoE FeatherWing (electrically compatible with the Adafruit Ethernet FeatherWing)
- PlatformIO with Visual Studio Code on a Mac (all libraries are up-to-date)
The Problem:
I can get either the Ethernet to work or the MusicMaker to work, but not both at the same time.
There are no errors during initialization, they just don't work.
Specifically, the MusicMaker's sineTest()
method doesn't generate any sound if Ethernet has been initialized. Likewise, the Ethernet client's connect()
method is unable to connect if the MusicMaker is initialized. Both methods work fine if the other device hasn't been initialized.
Feather Pins:
Purpose Pin
-------------------------- ---
VS1053 Chip select 6
VS1053 Data/command select 10
VS1053 Data request 9
SD Card Chip select 5
Ethernet Chip select 14
Note that I'm not currently using the SD card and haven't called its begin()
method.
Note also that the Ethernet chip select is on a non-standard pin because the default conflicts with the VS1053 data/command select.
Also, the PoE FeatherWing has an EEPROM on it with a unique MAC address and reading this works fine.
One other note: I thought that I had this combination working in an earlier prototype a few months ago, but that may have been with the Feather 32U4.
TIA.
The prototype code:
#include <Arduino.h>
#include <Adafruit_VS1053.h>
#include <Ethernet.h>
#include <IPAddress.h>
#include <SPI.h>
#include <Wire.h>
//
// Pins for the Ethernet and MusicMaker FeatherWings.
// NOTE: These definitions assume a Feather M4 Express with a PoE FeatherWing
// modified to have its chip select on pin 14 because the default conflicts
// with the MusicMaker's DCS pin.
//
#define VS1053_CS 6 // VS1053 chip select pin (output)
#define VS1053_DCS 10 // VS1053 Data/command select pin (output)
#define SD_CARD_CS 5 // Card chip select pin
#define VS1053_DREQ 9 // VS1053 Data request, ideally an Interrupt pin
#define ETHERNET_CS 14 // Ethernet chip select pin. Note that this is custom because the default conflicts with VS1053_DCS.
//
// PoE FeatherWing.
//
#define MAC_EEPROM_I2C_ADDRESS 0x50 // I2C address of the 24AA02E48 EEPROM chip that contains our MAC address (on the PoE FeatherWing).
#define MAC_EEPROM_REGISTER 0xFA // Register within the 24AA02E48 that contains the first byte of our MAC address.
#define HANG while( true ){ delay( 10 ); }
IPAddress host = { 172, 24, 110, 1 };
uint16_t port = 8000;
const char* path = "/sounds/DingDong.mp3";
Adafruit_VS1053 player{ Adafruit_VS1053( -1, VS1053_CS, VS1053_DCS, VS1053_DREQ ) };
EthernetClient client;
uint8_t mp3Buf[VS1053_DATABUFFERLEN];
uint32_t contentLength;
byte macAddress[6]{};
uint32_t beginRequest();
void readMacAddress( byte* );
void setup()
{
Serial.begin( 115200 );
while( !Serial ){ delay( 10 ); }
Serial.println( "MusicMaker/Ethernet Prototype" );
Wire.begin();
Ethernet.init( ETHERNET_CS );
readMacAddress( macAddress );
if( !Ethernet.begin( macAddress ))
{
Serial.printf( "Unable to initialize the network; hardware status is %d\n",
Ethernet.hardwareStatus());
HANG;
}
Serial.print( "Mac address: " );
for( int ii = 0; ii < 6; ii++ )
{
if( macAddress[ii] < 16 ) { Serial.print( '0' ); }
Serial.print( macAddress[ii], HEX );
if( ii < 5 ) { Serial.print( ':' ); }
}
Serial.println();
Serial.print( "DNS IP: " ); Serial.println( Ethernet.dnsServerIP());
Serial.print( "Local IP: " ); Serial.println( Ethernet.localIP());
Serial.print( "Gateway IP: " ); Serial.println( Ethernet.gatewayIP());
Serial.print( "Subnet mask: " ); Serial.println( Ethernet.subnetMask());
if( !player.begin())
{
Serial.println( "Unable to initialize the MusicMaker" );
HANG;
}
Serial.println( "Initialized the MusicMaker" );
player.setVolume( 40, 40 );
player.sineTest( 0x44, 1000 ); // 1KHz tone for one second.
contentLength = beginRequest();
Serial.printf( "HTTP content length: %d\n", contentLength );
}
void loop()
{
if( player.readyForData())
{
if( client.available() > 0 )
{
uint8_t bytesRead = client.read( mp3Buf, VS1053_DATABUFFERLEN );
if( bytesRead > 0 )
{
player.playData( mp3Buf, bytesRead );
contentLength -= bytesRead;
if( contentLength <= 0 )
{
Serial.println( "That should be all of our sound" );
}
}
}
}
}
uint32_t beginRequest()
{
if( !client.connect( host, port ))
{
Serial.print( "Unable to connect to " );
Serial.print( host ); Serial.print( ':' ); Serial.println( port );
HANG;
}
Serial.print( "GET " ); Serial.print( path ); Serial.print( " HTTP/1.1\r\n" );
Serial.print( "Host: " ); Serial.print( host ); Serial.print( "\r\n" );
Serial.print( "Connection: close\r\n\r\n" );
client.print( "GET " ); client.print( path ); client.print( " HTTP/1.1\r\n" );
client.print( "Host: " ); client.print( host ); client.print( "\r\n" );
client.print( "Connection: close\r\n\r\n" );
char http[] = "HTTP/";
client.find( http ); // Skip over the HTTP/ part of the header.
client.parseFloat( SKIP_WHITESPACE ); // Skip over the HTTP version number.
int httpStatus = client.parseInt( SKIP_WHITESPACE );
if( httpStatus < 200 || httpStatus > 299 )
{
Serial.printf( "GET request failed; HTTP status is %d\n", httpStatus );
client.stop();
HANG;
}
char lengthHeader[] = "Content-Length:";
client.find( lengthHeader );
int contentLength = client.parseInt();
char endOfHeaders[] = "\r\n\r\n";
if( !client.find( endOfHeaders ))
{
Serial.println( "Invalid HTTP response (missing trailing line endings)" );
client.stop();
HANG;
}
return contentLength;
}
void readMacAddress( byte* addr )
{
Wire.beginTransmission( MAC_EEPROM_I2C_ADDRESS );
Wire.write( MAC_EEPROM_REGISTER );
int failed = Wire.endTransmission();
if( failed )
{
Serial.printf( "Unable to retrieve MAC address; endTransmission returned %d\n", failed );
HANG;
}
byte* b = addr;
int bytesRead = Wire.requestFrom( MAC_EEPROM_I2C_ADDRESS, 6 );
if( bytesRead < 6 )
{
Serial.printf( "Unable to retrieve MAC address; fewer than six bytes\n" );
HANG;
}
while( Wire.available())
{
*b++ = Wire.read();
}
}
Turned out to be a hardware problem.
The PoE FeatherWing's default CS pin conflicts with the MusicMaker's DCS pin. The PoE CS pin can be changed by cutting a trace on the back of the board and bodging a wire from a pad to whatever pin you want to use instead. I had done that.
Turns out though, that I hadn't cut it completely. This was causing the Ethernet library to pull the MusicMaker DCS low every time it pulled the Ethernet CS low.
I finally figured it out when I hooked up a logic analyzer to all of the CS pins and the SPI pins. It was very clear that MusicMaker DCS was going low with Ethernet CS.
Yay Logic Analyzer.