Untitled diff
495 lines
//******************************************************************************************
//******************************************************************************************
//* Esp_radio -- Webradio receiver for ESP8266, 1.8 color display and VS1053 MP3 module. *
//* Esp_radio -- Webradio receiver for ESP8266, 1.8 color display and VS1053 MP3 module. *
//* With ESP8266 running at 80 MHz, it is capable of handling up to 256 kb bitrate. *
//* With ESP8266 running at 80 MHz, it is capable of handling up to 256 kb bitrate. *
//* With ESP8266 running at 160 MHz, it is capable of handling up to 320 kb bitrate. *
//* With ESP8266 running at 160 MHz, it is capable of handling up to 320 kb bitrate. *
//******************************************************************************************
//******************************************************************************************
// ESP8266 libraries used:
// ESP8266 libraries used:
// - ESP8266WiFi
// - ESP8266WiFi - Part of ESP8266 Arduino default libraries.
// - SPI
// - SPI - Part of Arduino default libraries.
// - Adafruit_GFX
// - Adafruit_GFX - https://github.com/adafruit/Adafruit-GFX-Library
// - TFT_ILI9163C
// - TFT_ILI9163C - https://github.com/sumotoy/TFT_ILI9163C
// - ESPAsyncTCP
// - ESPAsyncTCP - https://github.com/me-no-dev/ESPAsyncTCP
// - ESPAsyncWebServer
// - ESPAsyncWebServer - https://github.com/me-no-dev/ESPAsyncWebServer
// - FS
// - FS - https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.2.0/ESP8266FS-0.2.0.zip
// - ArduinoOTA
// - ArduinoOTA Part of ESP8266 Arduino default libraries.
// - AsyncMqttClient
// - AsyncMqttClient https://github.com/marvinroger/async-mqtt-client
// - Adafruit TinyXML Fork https://github.com/adafruit/TinyXML
//
//
// A library for the VS1053 (for ESP8266) is not available (or not easy to find). Therefore
// A library for the VS1053 (for ESP8266) is not available (or not easy to find). Therefore
// a class for this module is derived from the maniacbug library and integrated in this sketch.
// a class for this module is derived from the maniacbug library and integrated in this sketch.
//
//
// See http://www.internet-radio.com for suitable stations. Add the stations of your choice
// See http://www.internet-radio.com for suitable stations. Add the stations of your choice
// to the .ini-file.
// to the .ini-file.
//
//
// Brief description of the program:
// Brief description of the program:
// First a suitable WiFi network is found and a connection is made.
// First a suitable WiFi network is found and a connection is made.
// Then a connection will be made to a shoutcast server. The server starts with some
// Then a connection will be made to a shoutcast server. The server starts with some
// info in the header in readable ascii, ending with a double CRLF, like:
// info in the header in readable ascii, ending with a double CRLF, like:
// icy-name:Classic Rock Florida - SHE Radio
// icy-name:Classic Rock Florida - SHE Radio
// icy-genre:Classic Rock 60s 70s 80s Oldies Miami South Florida
// icy-genre:Classic Rock 60s 70s 80s Oldies Miami South Florida
// icy-url:http://www.ClassicRockFLorida.com
// icy-url:http://www.ClassicRockFLorida.com
// content-type:audio/mpeg
// content-type:audio/mpeg
// icy-pub:1
// icy-pub:1
// icy-metaint:32768 - Metadata after 32768 bytes of MP3-data
// icy-metaint:32768 - Metadata after 32768 bytes of MP3-data
// icy-br:128 - in kb/sec (for Ogg this is like "icy-br=Quality 2"
// icy-br:128 - in kb/sec (for Ogg this is like "icy-br=Quality 2"
//
//
// After de double CRLF is received, the server starts sending mp3- or Ogg-data. For mp3, this
// After de double CRLF is received, the server starts sending mp3- or Ogg-data. For mp3, this
// data may contain metadata (non mp3) after every "metaint" mp3 bytes.
// data may contain metadata (non mp3) after every "metaint" mp3 bytes.
// The metadata is empty in most cases, but if any is available the content will be presented on the TFT.
// The metadata is empty in most cases, but if any is available the content will be presented on the TFT.
// Pushing the input button causes the player to select the next preset station present in the .ini file.
// Pushing the input button causes the player to select the next preset station present in the .ini file.
//
//
// The display used is a Chinese 1.8 color TFT module 128 x 160 pixels. The TFT_ILI9163C.h
// The display used is a Chinese 1.8 color TFT module 128 x 160 pixels. The TFT_ILI9163C.h
// file has been changed to reflect this particular module. TFT_ILI9163C.cpp has been
// file has been changed to reflect this particular module. TFT_ILI9163C.cpp has been
// changed to use the full screenwidth if rotated to mode "3". Now there is room for 26
// changed to use the full screenwidth if rotated to mode "3". Now there is room for 26
// characters per line and 16 lines. Software will work without installing the display.
// characters per line and 16 lines. Software will work without installing the display.
// If no TFT is used, you may use GPIO2 and GPIO15 as control buttons. See definition of "USETFT" below.
// If no TFT is used, you may use GPIO2 and GPIO15 as control buttons. See definition of "USETFT" below.
// Switches are than programmed as:
// Switches are than programmed as:
// GPIO2 : "Goto station 1"
// GPIO2 : "Goto station 1"
// GPIO0 : "Next station"
// GPIO0 : "Next station"
// GPIO15: "Previous station". Note that GPIO15 has to be LOW when starting the ESP8266.
// GPIO15: "Previous station". Note that GPIO15 has to be LOW when starting the ESP8266.
// The button for GPIO15 must therefore be connected to VCC (3.3V) instead of GND.
// The button for GPIO15 must therefore be connected to VCC (3.3V) instead of GND.
//
//
// For configuration of the WiFi network(s): see the global data section further on.
// For configuration of the WiFi network(s): see the global data section further on.
//
//
// The SPI interface for VS1053 and TFT uses hardware SPI.
// The SPI interface for VS1053 and TFT uses hardware SPI.
//
//
// Wiring:
// Wiring:
// NodeMCU GPIO Pin to program Wired to LCD Wired to VS1053 Wired to rest
// NodeMCU GPIO Pin to program Wired to LCD Wired to VS1053 Wired to rest
// ------- ------ -------------- --------------- ------------------- ---------------------
// ------- ------ -------------- --------------- ------------------- ---------------------
// D0 GPIO16 16 - pin 1 DCS -
// D0 GPIO16 16 - pin 1 DCS -
// D1 GPIO5 5 - pin 2 CS LED on nodeMCU
// D1 GPIO5 5 - pin 2 CS LED on nodeMCU
// D2 GPIO4 4 - pin 4 DREQ -
// D2 GPIO4 4 - pin 4 DREQ -
// D3 GPIO0 0 FLASH - - Control button "Next station"
// D3 GPIO0 0 FLASH - - Control button "Next station"
// D4 GPIO2 2 pin 3 (D/C) - (OR)Control button "Station 1"
// D4 GPIO2 2 pin 3 (D/C) - (OR)Control button "Station 1"
// D5 GPIO14 14 SCLK pin 5 (CLK) pin 5 SCK -
// D5 GPIO14 14 SCLK pin 5 (CLK) pin 5 SCK -
// D6 GPIO12 12 MISO - pin 7 MISO -
// D6 GPIO12 12 MISO - pin 7 MISO -
// D7 GPIO13 13 MOSI pin 4 (DIN) pin 6 MOSI -
// D7 GPIO13 13 MOSI pin 4 (DIN) pin 6 MOSI -
// D8 GPIO15 15 pin 2 (CS) - (OR)Control button "Previous station"
// D8 GPIO15 15 pin 2 (CS) - (OR)Control button "Previous station"
// D9 GPI03 3 RXD0 - - Reserved serial input
// D9 GPI03 3 RXD0 - - Reserved serial input
// D10 GPIO1 1 TXD0 - - Reserved serial output
// D10 GPIO1 1 TXD0 - - Reserved serial output
// ------- ------ -------------- --------------- ------------------- ---------------------
// ------- ------ -------------- --------------- ------------------- ---------------------
// GND - - pin 8 (GND) pin 8 GND Power supply
// GND - - pin 8 (GND) pin 8 GND Power supply
// VCC 3.3 - - pin 6 (VCC) - LDO 3.3 Volt
// VCC 3.3 - - pin 6 (VCC) - LDO 3.3 Volt
// VCC 5 V - - pin 7 (BL) pin 9 5V Power supply
// VCC 5 V - - pin 7 (BL) pin 9 5V Power supply
// RST - - pin 1 (RST) pin 3 RESET Reset circuit
// RST - - pin 1 (RST) pin 3 RESET Reset circuit
//
//
// The reset circuit is a circuit with 2 diodes to GPIO5 and GPIO16 and a resistor to ground
// The reset circuit is a circuit with 2 diodes to GPIO5 and GPIO16 and a resistor to ground
// (wired OR gate) because there was not a free GPIO output available for this function.
// (wired OR gate) because there was not a free GPIO output available for this function.
// This circuit is included in the documentation.
// This circuit is included in the documentation.
// Issues:
// Issues:
// Webserver produces error "LmacRxBlk:1" after some time. After that it will work very slow.
// Webserver produces error "LmacRxBlk:1" after some time. After that it will work very slow.
// The program will reset the ESP8266 in such a case. Now we have switched to async webserver,
// The program will reset the ESP8266 in such a case. Now we have switched to async webserver,
// the problem still exists, but the program will not crash anymore.
// the problem still exists, but the program will not crash anymore.
// Upload to ESP8266 not reliable.
// Upload to ESP8266 not reliable.
//
//
// 31-03-2016, ES: First set-up.
// 31-03-2016, ES: First set-up.
// 01-04-2016, ES: Detect missing VS1053 at start-up.
// 01-04-2016, ES: Detect missing VS1053 at start-up.
// 05-04-2016, ES: Added commands through http server on port 80.
// 05-04-2016, ES: Added commands through http server on port 80.
// 14-04-2016, ES: Added icon and switch preset on stream error.
// 14-04-2016, ES: Added icon and switch preset on stream error.
// 18-04-2016, ES: Added SPIFFS for webserver.
// 18-04-2016, ES: Added SPIFFS for webserver.
// 19-04-2016, ES: Added ringbuffer.
// 19-04-2016, ES: Added ringbuffer.
// 20-04-2016, ES: WiFi Passwords through SPIFFS files, enable OTA.
// 20-04-2016, ES: WiFi Passwords through SPIFFS files, enable OTA.
// 21-04-2016, ES: Switch to Async Webserver.
// 21-04-2016, ES: Switch to Async Webserver.
// 27-04-2016, ES: Save settings, so same volume and preset will be used after restart.
// 27-04-2016, ES: Save settings, so same volume and preset will be used after restart.
// 03-05-2016, ES: Add bass/treble settings (see also new index.html).
// 03-05-2016, ES: Add bass/treble settings (see also new index.html).
// 04-05-2016, ES: Allow stations like "skonto.ls.lv:8002/mp3".
// 04-05-2016, ES: Allow stations like "skonto.ls.lv:8002/mp3".
// 06-05-2016, ES: Allow hiddens WiFi station if this is the only .pw file.
// 06-05-2016, ES: Allow hiddens WiFi station if this is the only .pw file.
// 07-05-2016, ES: Added preset selection in webserver.
// 07-05-2016, ES: Added preset selection in webserver.
// 12-05-2016, ES: Added support for Ogg-encoder.
// 12-05-2016, ES: Added support for Ogg-encoder.
// 13-05-2016, ES: Better Ogg detection.
// 13-05-2016, ES: Better Ogg detection.
// 17-05-2016, ES: Analog input for commands, extra buttons if no TFT required.
// 17-05-2016, ES: Analog input for commands, extra buttons if no TFT required.
// 26-05-2016, ES: Fixed BUTTON3 bug (no TFT).
// 26-05-2016, ES: Fixed BUTTON3 bug (no TFT).
// 27-05-2016, ES: Fixed restore station at restart.
// 27-05-2016, ES: Fixed restore station at restart.
// 04-07-2016, ES: WiFi.disconnect clears old connection now (thanks to Juppit).
// 04-07-2016, ES: WiFi.disconnect clears old connection now (thanks to Juppit).
// 23-09-2016, ES: Added commands via MQTT and Serial input, Wifi set-up in AP mode.
// 23-09-2016, ES: Added commands via MQTT and Serial input, Wifi set-up in AP mode.
// 04-10-2016, ES: Configuration in .ini file. No more use of EEPROM and .pw files.
// 04-10-2016, ES: Configuration in .ini file. No more use of EEPROM and .pw files.
// 11-10-2016, ES: Allow stations that have no bitrate in header like icecast.err.ee/raadio2.mp3.
// 11-10-2016, ES: Allow stations that have no bitrate in header like icecast.err.ee/raadio2.mp3.
// 14-10-2016, ES: Updated for async-mqtt-client-master 0.5.0
// 14-10-2016, ES: Updated for async-mqtt-client-master 0.5.0
// 22-10-2016, ES: Correction mute/unmute.
// 22-10-2016, ES: Correction mute/unmute.
// 15-11-2016, ES: Support for .m3u playlists.
// 15-11-2016, ES: Support for .m3u playlists.
// 22-12-2016, ES: Support for localhost (play from SPIFFS).
// 22-12-2016, ES: Support for localhost (play from SPIFFS).
// 28-12-2016, ES: Implement "Resume" request.
// 28-12-2016, ES: Implement "Resume" request.
// 31-12-2016, ES: Allow ContentType "text/css".
// 31-12-2016, ES: Allow ContentType "text/css".
// 02-01-2017, ES: Webinterface in PROGMEM.
// 02-01-2017, ES: Webinterface in PROGMEM.
// 16-01-2017, ES: Correction playlists.
// 16-01-2017, ES: Correction playlists.
// 17-01-2017, ES: Bugfix config page and playlist.
// 17-01-2017, ES: Bugfix config page and playlist.
// 23-01-2017, ES: Bugfix playlist.
// 23-01-2017, ES: Bugfix playlist.
// 26-01-2017, ES: Check on wrong icy-metaint.
// 26-01-2017, ES: Check on wrong icy-metaint.
// 30-01-2017, ES: Allow chunked transfer encoding.
// 30-01-2017, ES: Allow chunked transfer encoding.
// 01-02-2017, ES: Bugfix file upload.
// 01-02-2017, ES: Bugfix file upload.
// 26-04-2017, ES: Better output webinterface on preset change.
// 26-04-2017, ES: Better output webinterface on preset change.
//
//
// Define the version number, also used for webserver as Last-Modified header:
// Define the version number, also used for webserver as Last-Modified header:
#define VERSION "Wed, 26 Apr 2017 08:45:00 GMT"
#define VERSION "Wed, 26 Apr 2017 08:45:00 GMT"
// TFT. Define USETFT if required.
// TFT. Define USETFT if required.
#define USETFT
#define USETFT
#include <ESP8266WiFi.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncWebServer.h>
#include <AsyncMqttClient.h>
#include <AsyncMqttClient.h>
#include <SPI.h>
#include <SPI.h>
#if defined ( USETFT )
#if defined ( USETFT )
#include <Adafruit_GFX.h>
#include <Adafruit_GFX.h>
#include <TFT_ILI9163C.h>
#include <TFT_ILI9163C.h>
#endif
#endif
#include <Ticker.h>
#include <Ticker.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <FS.h>
#include <FS.h>
#include <ArduinoOTA.h>
#include <ArduinoOTA.h>
#include <TinyXML.h>
extern "C"
extern "C"
{
{
#include "user_interface.h"
#include "user_interface.h"
}
}
// Definitions for 3 control switches on analog input
// Definitions for 3 control switches on analog input
// You can test the analog input values by holding down the switch and select /?analog=1
// You can test the analog input values by holding down the switch and select /?analog=1
// in the web interface. See schematics in the documentation.
// in the web interface. See schematics in the documentation.
// Switches are programmed as "Goto station 1", "Next station" and "Previous station" respectively.
// Switches are programmed as "Goto station 1", "Next station" and "Previous station" respectively.
// Set these values to 2000 if not used or tie analog input to ground.
// Set these values to 2000 if not used or tie analog input to ground.
#define NUMANA 3
#define NUMANA 3
//#define asw1 252
//#define asw1 252
//#define asw2 334
//#define asw2 334
//#define asw3 499
//#define asw3 499
#define asw1 2000
#define asw1 2000
#define asw2 2000
#define asw2 2000
#define asw3 2000
#define asw3 2000
//
//
// Color definitions for the TFT screen (if used)
// Color definitions for the TFT screen (if used)
#define BLACK 0x0000
#define BLACK 0x0000
#define BLUE 0xF800
#define BLUE 0xF800
#define RED 0x001F
#define RED 0x001F
#define GREEN 0x07E0
#define GREEN 0x07E0
#define CYAN GREEN | BLUE
#define CYAN GREEN | BLUE
#define MAGENTA RED | BLUE
#define MAGENTA RED | BLUE
#define YELLOW RED | GREEN
#define YELLOW RED | GREEN
#define WHITE BLUE | RED | GREEN
#define WHITE BLUE | RED | GREEN
// Digital I/O used
// Digital I/O used
// Pins for VS1053 module
// Pins for VS1053 module
#define VS1053_CS 5
#define VS1053_CS 5
#define VS1053_DCS 16
#define VS1053_DCS 16
#define VS1053_DREQ 4
#define VS1053_DREQ 4
// Pins CS and DC for TFT module (if used, see definition of "USETFT")
// Pins CS and DC for TFT module (if used, see definition of "USETFT")
#define TFT_CS 15
#define TFT_CS 15
#define TFT_DC 2
#define TFT_DC 2
// Control button (GPIO) for controlling station
// Control button (GPIO) for controlling station
#define BUTTON1 2
#define BUTTON1 2
#define BUTTON2 0
#define BUTTON2 0
#define BUTTON3 15
#define BUTTON3 15
// Ringbuffer for smooth playing. 20000 bytes is 160 Kbits, about 1.5 seconds at 128kb bitrate.
// Ringbuffer for smooth playing. 20000 bytes is 160 Kbits, about 1.5 seconds at 128kb bitrate.
#define RINGBFSIZ 20000
#define RINGBFSIZ 20000
// Debug buffer size
// Debug buffer size
#define DEBUG_BUFFER_SIZE 100
#define DEBUG_BUFFER_SIZE 100
// Name of the ini file
// Name of the ini file
#define INIFILENAME "/radio.ini"
#define INIFILENAME "/radio.ini"
// Access point name if connection to WiFi network fails. Also the hostname for WiFi and OTA.
// Access point name if connection to WiFi network fails. Also the hostname for WiFi and OTA.
// Not that the password of an AP must be at least as long as 8 characters.
// Not that the password of an AP must be at least as long as 8 characters.
// Also used for other naming.
// Also used for other naming.
#define NAME "Esp-radio"
#define NAME "Esp-radio"
// Maximum number of MQTT reconnects before give-up
// Maximum number of MQTT reconnects before give-up
#define MAXMQTTCONNECTS 20
#define MAXMQTTCONNECTS 20
//
//
//******************************************************************************************
//******************************************************************************************
// Forward declaration of various functions *
// Forward declaration of various functions *
//******************************************************************************************
//******************************************************************************************
void displayinfo ( const char* str, uint16_t pos, uint16_t height, uint16_t color ) ;
void displayinfo ( const char* str, uint16_t pos, uint16_t height, uint16_t color ) ;
void showstreamtitle ( const char* ml, bool full = false ) ;
void showstreamtitle ( const char* ml, bool full = false ) ;
void handlebyte ( uint8_t b, bool force = false ) ;
void handlebyte ( uint8_t b, bool force = false ) ;
void handlebyte_ch ( uint8_t b, bool force = false ) ;
void handlebyte_ch ( uint8_t b, bool force = false ) ;
void handleFS ( AsyncWebServerRequest* request ) ;
void handleFS ( AsyncWebServerRequest* request ) ;
void handleFSf ( AsyncWebServerRequest* request, const String& filename ) ;
void handleFSf ( AsyncWebServerRequest* request, const String& filename ) ;
void handleCmd ( AsyncWebServerRequest* request ) ;
void handleCmd ( AsyncWebServerRequest* request ) ;
void handleFileUpload ( AsyncWebServerRequest* request, String filename,
void handleFileUpload ( AsyncWebServerRequest* request, String filename,
size_t index, uint8_t* data, size_t len, bool final ) ;
size_t index, uint8_t* data, size_t len, bool final ) ;
char* dbgprint( const char* format, ... ) ;
char* dbgprint( const char* format, ... ) ;
char* analyzeCmd ( const char* str ) ;
char* analyzeCmd ( const char* str ) ;
char* analyzeCmd ( const char* par, const char* val ) ;
char* analyzeCmd ( const char* par, const char* val ) ;
String chomp ( String str ) ;
String chomp ( String str ) ;
void publishIP() ;
void publishIP() ;
//
//
//******************************************************************************************
//******************************************************************************************
// Global data section. *
// Global data section. *
//******************************************************************************************
//******************************************************************************************
// There is a block ini-data that contains some configuration. Configuration data is *
// There is a block ini-data that contains some configuration. Configuration data is *
// saved in the SPIFFS file radio.ini by the webinterface. On restart the new data will *
// saved in the SPIFFS file radio.ini by the webinterface. On restart the new data will *
// de read from this file. *
// de read from this file. *
// Items in ini_block can be changed by commands from webserver/MQTT/Serial. *
// Items in ini_block can be changed by commands from webserver/MQTT/Serial. *
//******************************************************************************************
//******************************************************************************************
struct ini_struct
struct ini_struct
{
{
String mqttbroker ; // The name of the MQTT broker server
String mqttbroker ; // The name of the MQTT broker server
uint16_t mqttport ; // Port, default 1883
uint16_t mqttport ; // Port, default 1883
String mqttuser ; // User for MQTT authentication
String mqttuser ; // User for MQTT authentication
String mqttpasswd ; // Password for MQTT authentication
String mqttpasswd ; // Password for MQTT authentication
String mqtttopic ; // Topic to suscribe to
String mqtttopic ; // Topic to suscribe to
String mqttpubtopic ; // Topic to pubtop (IP will be published)
String mqttpubtopic ; // Topic to pubtop (IP will be published)
uint8_t reqvol ; // Requested volume
uint8_t reqvol ; // Requested volume
uint8_t rtone[4] ; // Requested bass/treble settings
uint8_t rtone[4] ; // Requested bass/treble settings
int8_t newpreset ; // Requested preset
int8_t newpreset ; // Requested preset
String ssid ; // SSID of WiFi network to connect to
String ssid ; // SSID of WiFi network to connect to
String passwd ; // Password for WiFi network
String passwd ; // Password for WiFi network
} ;
} ;
enum datamode_t { INIT = 1, HEADER = 2, DATA = 4,
enum datamode_t { INIT = 1, HEADER = 2, DATA = 4,
METADATA = 8, PLAYLISTINIT = 16,
METADATA = 8, PLAYLISTINIT = 16,
PLAYLISTHEADER = 32, PLAYLISTDATA = 64,
PLAYLISTHEADER = 32, PLAYLISTDATA = 64,
STOPREQD = 128, STOPPED = 256
STOPREQD = 128, STOPPED = 256
} ; // State for datastream
} ; // State for datastream
// Global variables
// Global variables
int DEBUG = 1 ;
int DEBUG = 1 ;
ini_struct ini_block ; // Holds configurable data
ini_struct ini_block ; // Holds configurable data
WiFiClient mp3client ; // An instance of the mp3 client
WiFiClient mp3client ; // An instance of the mp3 client
AsyncWebServer cmdserver ( 80 ) ; // Instance of embedded webserver on port 80
AsyncWebServer cmdserver ( 80 ) ; // Instance of embedded webserver on port 80
AsyncMqttClient mqttclient ; // Client for MQTT subscriber
AsyncMqttClient mqttclient ; // Client for MQTT subscriber
IPAddress mqtt_server_IP ; // IP address of MQTT broker
IPAddress mqtt_server_IP ; // IP address of MQTT broker
char cmd[130] ; // Command from MQTT or Serial
char cmd[130] ; // Command from MQTT or Serial
#if defined ( USETFT )
#if defined ( USETFT )
TFT_ILI9163C tft = TFT_ILI9163C ( TFT_CS, TFT_DC ) ;
TFT_ILI9163C tft = TFT_ILI9163C ( TFT_CS, TFT_DC ) ;
#endif
#endif
Ticker tckr ; // For timing 100 msec
Ticker tckr ; // For timing 100 msec
uint32_t totalcount = 0 ; // Counter mp3 data
uint32_t totalcount = 0 ; // Counter mp3 data
datamode_t datamode ; // State of datastream
datamode_t datamode ; // State of datastream
int metacount ; // Number of bytes in metadata
int metacount ; // Number of bytes in metadata
int datacount ; // Counter databytes before metadata
int datacount ; // Counter databytes before metadata
String metaline ; // Readable line in metadata
String metaline ; // Readable line in metadata
String icystreamtitle ; // Streamtitle from metadata
String icystreamtitle ; // Streamtitle from metadata
String icyname ; // Icecast station name
String icyname ; // Icecast station name
int bitrate ; // Bitrate in kb/sec
int bitrate ; // Bitrate in kb/sec
int metaint = 0 ; // Number of databytes between metadata
int metaint = 0 ; // Number of databytes between metadata
int8_t currentpreset = -1 ; // Preset station playing
int8_t currentpreset = -1 ; // Preset station playing
String host ; // The URL to connect to or file to play
String host ; // The URL to connect to or file to play
String playlist ; // The URL of the specified playlist
String playlist ; // The URL of the specified playlist
bool hostreq = false ; // Request for new host
bool hostreq = false ; // Request for new host
bool reqtone = false ; // New tone setting requested
bool reqtone = false ; // New tone setting requested
bool muteflag = false ; // Mute output
bool muteflag = false ; // Mute output
uint8_t* ringbuf ; // Ringbuffer for VS1053
uint8_t* ringbuf ; // Ringbuffer for VS1053
uint16_t rbwindex = 0 ; // Fill pointer in ringbuffer
uint16_t rbwindex = 0 ; // Fill pointer in ringbuffer
uint16_t rbrindex = RINGBFSIZ - 1 ; // Emptypointer in ringbuffer
uint16_t rbrindex = RINGBFSIZ - 1 ; // Emptypointer in ringbuffer
uint16_t rcount = 0 ; // Number of bytes in ringbuffer
uint16_t rcount = 0 ; // Number of bytes in ringbuffer
uint16_t analogsw[NUMANA] = { asw1, asw2, asw3 } ; // 3 levels of analog input
uint16_t analogsw[NUMANA] = { asw1, asw2, asw3 } ; // 3 levels of analog input
uint16_t analogrest ; // Rest value of analog input
uint16_t analogrest ; // Rest value of analog input
bool resetreq = false ; // Request to reset the ESP8266
bool resetreq = false ; // Request to reset the ESP8266
bool NetworkFound ; // True if WiFi network connected
bool NetworkFound ; // True if WiFi network connected
String networks ; // Found networks
String networks ; // Found networks
String anetworks ; // Aceptable networks (present in .ini file)
String anetworks ; // Aceptable networks (present in .ini file)
String presetlist ; // List for webserver
String presetlist ; // List for webserver
uint8_t num_an ; // Number of acceptable networks in .ini file
uint8_t num_an ; // Number of acceptable networks in .ini file
String testfilename = "" ; // File to test (SPIFFS speed)
String testfilename = "" ; // File to test (SPIFFS speed)
uint16_t mqttcount = 0 ; // Counter MAXMQTTCONNECTS
uint16_t mqttcount = 0 ; // Counter MAXMQTTCONNECTS
int8_t playlist_num = 0 ; // Nonzero for selection from playlist
int8_t playlist_num = 0 ; // Nonzero for selection from playlist
File mp3file ; // File containing mp3 on SPIFFS
File mp3file ; // File containing mp3 on SPIFFS
bool localfile = false ; // Play from local mp3-file or not
bool localfile = false ; // Play from local mp3-file or not
bool chunked = false ; // Station provides chunked transfer
bool chunked = false ; // Station provides chunked transfer
int chunkcount = 0 ; // Counter for chunked transfer
int chunkcount = 0 ; // Counter for chunked transfer
// XML IHeartRadio Globals.
const char* ihrhost = "playerservices.streamtheworld.com"; // XML data source.
const char* apiVersion = "1.5"; // API Version of IHeartRadio.
const char* mountPoint = "CIMXFMAAC"; // Testing MountPoint (Station Callsign).
const char* lang = "en"; // Language.
const int xmlPort = 80; // XML Port.
uint8_t xmlbuffer[150]; // For XML decoding.
String xmlOpen; // Opening XML tag.
String xmlTag; // Current XML tag.
String xmlData; // Data inside tag.
String xmlDataLast; // Prevents duplicated tag data.
String stationServer(""); // Radio stream server.
String stationPort(""); // Radio stream port.
String stationMount(""); // Radio stream Callsign.
//******************************************************************************************
//******************************************************************************************
// End of global data section. *
// End of global data section. *
//******************************************************************************************
//******************************************************************************************
//******************************************************************************************
//******************************************************************************************
// Pages and CSS for the webinterface. *
// Pages and CSS for the webinterface. *
//******************************************************************************************
//******************************************************************************************
#include "about_html.h"
#include "about_html.h"
#include "config_html.h"
#include "config_html.h"
#include "index_html.h"
#include "index_html.h"
#include "radio_css.h"
#include "radio_css.h"
#include "favicon_ico.h"
#include "favicon_ico.h"
//
//
//******************************************************************************************
//******************************************************************************************
// VS1053 stuff. Based on maniacbug library. *
// VS1053 stuff. Based on maniacbug library. *
//******************************************************************************************
//******************************************************************************************
// VS1053 class definition. *
// VS1053 class definition. *
//******************************************************************************************
//******************************************************************************************
class VS1053
class VS1053
{
{
private:
private:
uint8_t cs_pin ; // Pin where CS line is connected
uint8_t cs_pin ; // Pin where CS line is connected
uint8_t dcs_pin ; // Pin where DCS line is connected
uint8_t dcs_pin ; // Pin where DCS line is connected
uint8_t dreq_pin ; // Pin where DREQ line is connected
uint8_t dreq_pin ; // Pin where DREQ line is connected
uint8_t curvol ; // Current volume setting 0..100%
uint8_t curvol ; // Current volume setting 0..100%
const uint8_t vs1053_chunk_size = 32 ;
const uint8_t vs1053_chunk_size = 32 ;
// SCI Register
// SCI Register
const uint8_t SCI_MODE = 0x0 ;
const uint8_t SCI_MODE = 0x0 ;
const uint8_t SCI_BASS = 0x2 ;
const uint8_t SCI_BASS = 0x2 ;
const uint8_t SCI_CLOCKF = 0x3 ;
const uint8_t SCI_CLOCKF = 0x3 ;
const uint8_t SCI_AUDATA = 0x5 ;
const uint8_t SCI_AUDATA = 0x5 ;
const uint8_t SCI_WRAM = 0x6 ;
const uint8_t SCI_WRAM = 0x6 ;
const uint8_t SCI_WRAMADDR = 0x7 ;
const uint8_t SCI_WRAMADDR = 0x7 ;
const uint8_t SCI_AIADDR = 0xA ;
const uint8_t SCI_AIADDR = 0xA ;
const uint8_t SCI_VOL = 0xB ;
const uint8_t SCI_VOL = 0xB ;
const uint8_t SCI_AICTRL0 = 0xC ;
const uint8_t SCI_AICTRL0 = 0xC ;
const uint8_t SCI_AICTRL1 = 0xD ;
const uint8_t SCI_AICTRL1 = 0xD ;
const uint8_t SCI_num_registers = 0xF ;
const uint8_t SCI_num_registers = 0xF ;
// SCI_MODE bits
// SCI_MODE bits
const uint8_t SM_SDINEW = 11 ; // Bitnumber in SCI_MODE always on
const uint8_t SM_SDINEW = 11 ; // Bitnumber in SCI_MODE always on
const uint8_t SM_RESET = 2 ; // Bitnumber in SCI_MODE soft reset
const uint8_t SM_RESET = 2 ; // Bitnumber in SCI_MODE soft reset
const uint8_t SM_CANCEL = 3 ; // Bitnumber in SCI_MODE cancel song
const uint8_t SM_CANCEL = 3 ; // Bitnumber in SCI_MODE cancel song
const uint8_t SM_TESTS = 5 ; // Bitnumber in SCI_MODE for tests
const uint8_t SM_TESTS = 5 ; // Bitnumber in SCI_MODE for tests
const uint8_t SM_LINE1 = 14 ; // Bitnumber in SCI_MODE for Line input
const uint8_t SM_LINE1 = 14 ; // Bitnumber in SCI_MODE for Line input
SPISettings VS1053_SPI ; // SPI settings for this slave
SPISettings VS1053_SPI ; // SPI settings for this slave
uint8_t endFillByte ; // Byte to send when stopping song
uint8_t endFillByte ; // Byte to send when stopping song
protected:
protected:
inline void await_data_request() const
inline void await_data_request() const
{
{
while ( !digitalRead ( dreq_pin ) )
while ( !digitalRead ( dreq_pin ) )
{
{
yield() ; // Very short delay
yield() ; // Very short delay
}
}
}
}
inline void control_mode_on() const
inline void control_mode_on() const
{
{
SPI.beginTransaction ( VS1053_SPI ) ; // Prevent other SPI users
SPI.beginTransaction ( VS1053_SPI ) ; // Prevent other SPI users
digitalWrite ( dcs_pin, HIGH ) ; // Bring slave in control mode
digitalWrite ( dcs_pin, HIGH ) ; // Bring slave in control mode
digitalWrite ( cs_pin, LOW ) ;
digitalWrite ( cs_pin, LOW ) ;
}
}
inline void control_mode_off() const
inline void control_mode_off() const
{
{
digitalWrite ( cs_pin, HIGH ) ; // End control mode
digitalWrite ( cs_pin, HIGH ) ; // End control mode
SPI.endTransaction() ; // Allow other SPI users
SPI.endTransaction() ; // Allow other SPI users
}
}
inline void data_mode_on() const
inline void data_mode_on() const
{
{
SPI.beginTransaction ( VS1053_SPI ) ; // Prevent other SPI users
SPI.beginTransaction ( VS1053_SPI ) ; // Prevent other SPI users
digitalWrite ( cs_pin, HIGH ) ; // Bring slave in data mode
digitalWrite ( cs_pin, HIGH ) ; // Bring slave in data mode
digitalWrite ( dcs_pin, LOW ) ;
digitalWrite ( dcs_pin, LOW ) ;
}
}
inline void data_mode_off() const
inline void data_mode_off() const
{
{
digitalWrite ( dcs_pin, HIGH ) ; // End data mode
digitalWrite ( dcs_pin, HIGH ) ; // End data mode
SPI.endTransaction() ; // Allow other SPI users
SPI.endTransaction() ; // Allow other SPI users
}
}
uint16_t read_register ( uint8_t _reg ) const ;
uint16_t read_register ( uint8_t _reg ) const ;
void write_register ( uint8_t _reg, uint16_t _value ) const ;
void write_register ( uint8_t _reg, uint16_t _value ) const ;
void sdi_send_buffer ( uint8_t* data, size_t len ) ;
void sdi_send_buffer ( uint8_t* data, size_t len ) ;
void sdi_send_fillers ( size_t length ) ;
void sdi_send_fillers ( size_t length ) ;
void wram_write ( uint16_t address, uint16_t data ) ;
void wram_write ( uint16_t address, uint16_t data ) ;
uint16_t wram_read ( uint16_t address ) ;
uint16_t wram_read ( uint16_t address ) ;
public:
public:
// Constructor. Only sets pin values. Doesn't touch the chip. Be sure to call begin()!
// Constructor. Only sets pin values. Doesn't touch the chip. Be sure to call begin()!
VS1053 ( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin ) ;
VS1053 ( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin ) ;
void begin() ; // Begin operation. Sets pins correctly,
void begin() ; // Begin operation. Sets pins correctly,
// and prepares SPI bus.
// and prepares SPI bus.
void startSong() ; // Prepare to start playing. Call this each
void startSong() ; // Prepare to start playing. Call this each
// time a new song starts.
// time a new song starts.
void playChunk ( uint8_t* data, size_t len ) ; // Play a chunk of data. Copies the data to
void playChunk ( uint8_t* data, size_t len ) ; // Play a chunk of data. Copies the data to
// the chip. Blocks until complete.
// the chip. Blocks until complete.
void stopSong() ; // Finish playing a song. Call this after
void stopSong() ; // Finish playing a song. Call this after
// the last playChunk call.
// the last playChunk call.
void setVolume ( uint8_t vol ) ; // Set the player volume.Level from 0-100,
void setVolume ( uint8_t vol ) ; // Set the player volume.Level from 0-100,
// higher is louder.
// higher is louder.
void setTone ( uint8_t* rtone ) ; // Set the player baas/treble, 4 nibbles for
void setTone ( uint8_t* rtone ) ; // Set the player baas/treble, 4 nibbles for
// treble gain/freq and bass gain/freq
// treble gain/freq and bass gain/freq
uint8_t getVolume() ; // Get the currenet volume setting.
uint8_t getVolume() ; // Get the currenet volume setting.
// higher is louder.
// higher is louder.
void printDetails ( const char *header ) ; // Print configuration details to serial output.
void printDetails ( const char *header ) ; // Print configuration details to serial output.
void softReset() ; // Do a soft reset
void softReset() ; // Do a soft reset
bool testComm ( const char *header ) ; // Test communication with module
bool testComm ( const char *header ) ; // Test communication with module
inline bool data_request() const
inline bool data_request() const
{
{
return ( digitalRead ( dreq_pin ) == HIGH ) ;
return ( digitalRead ( dreq_pin ) == HIGH ) ;
}
}
} ;
} ;
//******************************************************************************************
//******************************************************************************************
// VS1053 class implementation. *
// VS1053 class implementation. *
//******************************************************************************************
//******************************************************************************************
VS1053::VS1053 ( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin ) :
VS1053::VS1053 ( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin ) :
cs_pin(_cs_pin), dcs_pin(_dcs_pin), dreq_pin(_dreq_pin)
cs_pin(_cs_pin), dcs_pin(_dcs_pin), dreq_pin(_dreq_pin)
{
{
}
}
uint16_t VS1053::read_register ( uint8_t _reg ) const
uint16_t VS1053::read_register ( uint8_t _reg ) const
{
{
uint16_t result ;
uint16_t result ;
control_mode_on() ;
control_mode_on() ;
SPI.write ( 3 ) ; // Read operation
SPI.write ( 3 ) ; // Read operation
SPI.write ( _reg ) ; // Register to write (0..0xF)
SPI.write ( _reg ) ; // Register to write (0..0xF)
// Note: transfer16 does not seem to work
// Note: transfer16 does not seem to work
result = ( SPI.transfer ( 0xFF ) << 8 ) | // Read 16 bits data
result = ( SPI.transfer ( 0xFF ) << 8 ) | // Read 16 bits data
( SPI.transfer ( 0xFF ) ) ;
( SPI.transfer ( 0xFF ) ) ;
await_data_request() ; // Wait for DREQ to be HIGH again
await_data_request() ; // Wait for DREQ to be HIGH again
control_mode_off() ;
control_mode_off() ;
return result ;
return result ;
}
}
void VS1053::write_register ( uint8_t _reg, uint16_t _value ) const
void VS1053::write_register ( uint8_t _reg, uint16_t _value ) const
{
{
control_mode_on( );
control_mode_on( );
SPI.write ( 2 ) ; // Write operation
SPI.write ( 2 ) ; // Write operation
SPI.write ( _reg ) ; // Register to write (0..0xF)
SPI.write ( _reg ) ; // Register to write (0..0xF)
SPI.write16 ( _value ) ; // Send 16 bits data
SPI.write16 ( _value ) ; // Send 16 bits data
await_data_request() ;
await_data_request() ;
control_mode_off() ;
control_mode_off() ;
}
}
void VS1053::sdi_send_buffer ( uint8_t* data, size_t len )
void VS1053::sdi_send_buffer ( uint8_t* data, size_t len )
{
{
size_t chunk_length ; // Length of chunk 32 byte or shorter
size_t chunk_length ; // Length of chunk 32 byte or shorter
data_mode_on() ;
data_mode_on() ;
while ( len ) // More to do?
while ( len ) // More to do?
{
{
await_data_request() ; // Wait for space available
await_data_request() ; // Wait for space available
chunk_length = len ;
chunk_length = len ;
if ( len > vs1053_chunk_size )
if ( len > vs1053_chunk_size )
{
{
chunk_length = vs1053_chunk_size ;
chunk_length = vs1053_chunk_size ;
}
}
len -= chunk_length ;
len -= chunk_length ;
SPI.writeBytes ( data, chunk_length ) ;
SPI.writeBytes ( data, chunk_length ) ;
data += chunk_length ;
data += chunk_length ;
}
}
data_mode_off() ;
data_mode_off() ;
}
}
void VS1053::sdi_send_fillers ( size_t len )
void VS1053::sdi_send_fillers ( size_t len )
{
{
size_t chunk_length ; // Length of chunk 32 byte or shorter
size_t chunk_length ;
data_mode_on() ;
while ( len ) // More to do?
{
await_data_request() ; // Wait for space available
chunk_length = len ;
if ( len > vs1053_chunk_size )
{
chunk_length = vs1053_chunk_size ;
}
len -= chunk_length ;
while ( chunk_length-- )
{
SPI.write ( endFillByte ) ;
}
}
data_mode_off();
}
void VS1053::wram_write ( uint16_t address, uint16_t data )
{
write_register ( SCI_WRAMADDR, address ) ;
write_register ( SCI_WRAM, data ) ;
}
uint16_t VS1053::wram_read ( uint16_t address )
{
write_register ( SCI_WRAMADDR, address ) ; // Start reading from WRAM
return read_register ( SCI_WRAM ) ; // Read back result
}
bool VS1053::testComm ( const char *header )
{
// Test the communication with the VS1053 module. The result wille be returned.
// If DREQ is low, there is problably no VS1053 connected. Pull the line HIGH
// in order to prevent an endless loop waiting for this signal. The rest of the
// software will still work, but readbacks from VS1053 will fail.
int i ;