Skip to content

Instantly share code, notes, and snippets.

@schreibfaul1
Last active May 13, 2020 21:25
Show Gist options
  • Save schreibfaul1/cd1aed62054ae1c616df468d59784385 to your computer and use it in GitHub Desktop.
Save schreibfaul1/cd1aed62054ae1c616df468d59784385 to your computer and use it in GitHub Desktop.
// AI-Thinker A1S Audioboard V2.2
// for default SPI GPIOs remove C58...C61 or use JTag GPIOs
// Display with Touchpad 320x240px, 3.3V relevant, controller: ILI9431
#include "Arduino.h"
#include "WiFi.h"
#include "SPI.h"
#include "SD.h"
#include "FS.h"
#include "Wire.h"
#include "Preferences.h"
#include "tft.h" // https://github.com/schreibfaul1/ESP32-TFT-Library-ILI9341-HX8347D
#include "AC101.h" // https://github.com/Yveaux/AC101
#include "Audio.h" // https://github.com/schreibfaul1/ESP32-audioI2S
// GPIOs for SD Card
#define SD_CS 13
// GPIOs for SPI
#define SPI_MOSI 15 //23
#define SPI_MISO 2 //19
#define SPI_SCK 14 //18
// GPIOs for TFT/TP
#define TFT_CS 22
#define TFT_DC 12
#define TP_CS 19 //21
#define TP_IRQ 4 //5
// I2S GPIOs, the names refer on AC101
#define I2S_DSIN 25
#define I2S_BCLK 27
#define I2S_LRC 26
#define I2S_MCLK 0
#define I2S_DOUT 35
// I2C GPIOs
#define IIC_CLK 32
#define IIC_DATA 33
// Amplifier enable
#define GPIO_PA_EN 21
// GPIOs for KEYS
#define KEY1 36
#define KEY2 13 // SD_CS
#define KEY3 19
#define KEY4 23
#define KEY5 18
#define KEY6 5
//String ssid = "xxxxxxxxx";
//String password = "xxxxxxxxx";
static AC101 ac;
Preferences pref;
TFT tft(0);
TP tp(TP_CS, TP_IRQ);
Audio audio;
String stations[] ={
"0n-80s.radionetz.de:8000/0n-70s.mp3",
"mediaserv30.live-streams.nl:8000/stream",
"www.surfmusic.de/m3u/100-5-das-hitradio,4529.m3u",
"stream.1a-webradio.de/deutsch/mp3-128/vtuner-1a",
"mp3.ffh.de/radioffh/hqlivestream.aac", // 128k aac
"www.antenne.de/webradio/antenne.m3u",
"listen.rusongs.ru/ru-mp3-128",
"edge.audio.3qsdn.com/senderkw-mp3",
"macslons-irish-pub-radio.com/media.asx",
"https://rbbmediapmdp-a.akamaihd.net/content/a8/0d/a80dd31a-f96f-496d-aaf4-ed43891dee5e/6ed01c6b-050f-4202-b95e-81c2bdccd592_6b575035-41f2-4e11-84f5-bd5cffded6f8.mp3",
};
//some global variables
uint8_t max_volume = 21;
uint8_t max_stations = 0; //will be set later
uint8_t cur_station = 0; //current station(nr), will be set later
uint8_t cur_volume = 0; //will be set from stored preferences
int8_t cur_btn =-1; //current button (, -1 means idle)
enum action{VOLUME_UP=0, VOLUME_DOWN=1, STATION_UP=2, STATION_DOWN=3};
enum staus {RELEASED=0, PRESSED=1};
struct _btns{
uint16_t x; //PosX
uint16_t y; //PosY
uint16_t w; //Width
uint16_t h; //Hight
uint8_t a; //Action
uint8_t s; //Status
};
typedef _btns btns;
btns btn[4];
//**************************************************************************************************
// G U I *
//**************************************************************************************************
void draw_button(btns b){
uint16_t color=TFT_BLACK;
uint8_t r=4, o=r*3;
if(b.s==RELEASED) color=TFT_GOLD;
if(b.s==PRESSED) color=TFT_DEEPSKYBLUE;
tft.drawRoundRect(b.x, b.y, b.w, b.h, r, color);
switch(b.a){
case VOLUME_UP:
tft.fillTriangle(b.x+b.w/2, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h-o, color); break;
case VOLUME_DOWN:
tft.fillTriangle(b.x+o, b.y+o, b.x+b.w/2, b.y+b.h-o, b.x+b.w-o, b.y+o, color); break;
case STATION_UP:
tft.fillTriangle(b.x+o, b.y+o, b.x+o, b.y+b.h-o, b.x+b.w-o, b.y+b.h/2, color); break;
case STATION_DOWN:
tft.fillTriangle(b.x+b.w-o, b.y+o, b.x+o, b.y+b.h/2, b.x+b.w-o, b.y+b.h-o, color); break;
}
}
void write_stationNr(uint8_t nr){
tft.fillRect(60, 195, 50, 40, TFT_BLACK);
String snr = String(nr);
if(snr.length()<2) snr = "0"+snr;
tft.setCursor(68, 195);
tft.setFont(Garamond34x42);
tft.setTextColor(TFT_YELLOW);
tft.print(snr);
}
void write_volume(uint8_t vol){
tft.fillRect(210, 195, 50, 40, TFT_BLACK);
String svol = String(vol);
if(svol.length()<2) svol = "0"+svol;
tft.setCursor(218, 195);
tft.setFont(Garamond34x42);
tft.setTextColor(TFT_YELLOW);
tft.print(svol);
}
void write_stationName(String sName){
tft.fillRect(0, 0, 320, 60, TFT_BLACK);
tft.setFont(Times_New_Roman34x27);
tft.setTextColor(TFT_CORNSILK);
tft.setCursor(20, 10);
tft.print(sName);
}
void write_streamTitle(String sTitle){
tft.fillRect(0, 60, 320, 130, TFT_BLACK);
tft.setFont(Times_New_Roman34x27);
tft.setTextColor(TFT_LIGHTBLUE);
tft.setCursor(20, 70);
tft.print(sTitle);
}
//**************************************************************************************************
// S E T U P *
//**************************************************************************************************
void setup()
{
Serial.begin(115200);
btn[0].x= 20; btn[0].y=195; btn[0].w=40; btn[0].h=40; btn[0].a=STATION_DOWN; btn[0].s=RELEASED;
btn[1].x=110; btn[1].y=195; btn[1].w=40; btn[1].h=40; btn[1].a=STATION_UP; btn[1].s=RELEASED;
btn[2].x=170; btn[2].y=195; btn[2].w=40; btn[2].h=40; btn[2].a=VOLUME_UP; btn[2].s=RELEASED;
btn[3].x=260; btn[3].y=195; btn[3].w=40; btn[3].h=40; btn[3].a=VOLUME_DOWN; btn[3].s=RELEASED;
max_stations= sizeof(stations)/sizeof(stations[0]); log_i("max stations %i", max_stations);
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
Serial.printf("Connect to AC101 codec... ");
while (not ac.begin(IIC_DATA, IIC_CLK))
{
Serial.printf("Failed!\n");
delay(1000);
}
Serial.printf("OK\n");
pref.begin("WebRadio", false); // instance of preferences for defaults (station, volume ...)
if(pref.getShort("volume", 1000) == 1000){ // if that: pref was never been initialized
pref.putShort("volume", 10);
pref.putShort("station", 0);
}
else{ // get the stored values
cur_station = pref.getShort("station");
cur_volume = pref.getShort("volume");
}
WiFi.mode(WIFI_STA);
while (WiFi.status() != WL_CONNECTED){
WiFi.begin(ssid.c_str(), password.c_str());
delay(1500);
}
log_i("Connect to %s", WiFi.SSID().c_str());
tft.begin(TFT_CS, TFT_DC, SPI_MOSI, SPI_MISO, SPI_SCK);
tft.setRotation(1);
tp.setRotation(3);
tft.setFont(Times_New_Roman21x17);
tft.fillScreen(TFT_BLACK);
audio.connecttohost(stations[cur_station]);
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++) draw_button(btn[i]);
ac.SetVolumeSpeaker(cur_volume * 1.5); // 0...31
ac.SetVolumeHeadphone(cur_volume *3); // 0...63
write_volume(cur_volume);
write_stationNr(cur_station);
// Enable amplifier
pinMode(GPIO_PA_EN, OUTPUT);
digitalWrite(GPIO_PA_EN, HIGH);
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DSIN);
audio.setVolume(cur_volume); // 0...21
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++) draw_button(btn[i]);
write_volume(cur_volume);
write_stationNr(cur_station);
}
//-----------------------------------------------------------------------
void loop()
{
audio.loop();
tp.loop();
}
//**************************************************************************************************
// E V E N T S *
//**************************************************************************************************
void audio_info(const char *info){
Serial.print("audio_info: "); Serial.println(info);
}
void audio_showstation(const char *info){
write_stationName(String(info));
}
void audio_showstreamtitle(const char *info){
String sinfo=String(info);
sinfo.replace("|", "\n");
write_streamTitle(sinfo);
}
void tp_pressed(uint16_t x, uint16_t y){
for(uint8_t i=0; i<(sizeof(btn)/sizeof(*btn)); i++){
if(x>btn[i].x && (x<btn[i].x+btn[i].w)){
if(y>btn[i].y && (y<btn[i].y+btn[i].h)){
cur_btn=i;
btn[cur_btn].s=PRESSED;
draw_button(btn[cur_btn]);
}
}
}
}
void tp_released(){
if(cur_btn !=-1){
btn[cur_btn].s=RELEASED;
draw_button(btn[cur_btn]);
switch(btn[cur_btn].a){
case VOLUME_UP: if(cur_volume<max_volume){
cur_volume++;
write_volume(cur_volume);
audio.setVolume(cur_volume);
ac.SetVolumeSpeaker(cur_volume * 1.5); // 0...31
ac.SetVolumeHeadphone(cur_volume *3); // 0...63
pref.putShort("volume", cur_volume);} // store the current volume in nvs
break;
case VOLUME_DOWN: if(cur_volume>0){
cur_volume-- ;
write_volume(cur_volume);
audio.setVolume(cur_volume);
ac.SetVolumeSpeaker(cur_volume * 1.5); // 0...31
ac.SetVolumeHeadphone(cur_volume *3); // 0...63
pref.putShort("volume", cur_volume);} // store the current volume in nvs
break;
case STATION_UP: if(cur_station<max_stations-1){
cur_station++;
write_stationNr(cur_station);
tft.fillRect(0, 0, 320, 195, TFT_BLACK);
audio.connecttohost(stations[cur_station]);
pref.putShort("station", cur_station);} // store the current station in nvs
break;
case STATION_DOWN:if(cur_station>0){
cur_station--;
write_stationNr(cur_station);
tft.fillRect(0, 0, 320, 195, TFT_BLACK);
audio.connecttohost(stations[cur_station]);
pref.putShort("station", cur_station);} // store the current station in nvs
break;
}
}
cur_btn=-1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment