boKontDABApp.ino
////////////////////////////////////////////////////////////
// DAB Shield Example App
// AVIT Research Ltd
// v0.1 11/05/2017 - initial release
// v0.2 09/10/2017 - support for M0 etc.
// v0.3 17/10/2017 - Added Max Services to prevent overflow (Library only)
// v0.4 20/11/2017 - Added Version Info
// v0.5 23/11/2017 - Added More 'Getting Started' Debugging
// v0.6 23/11/2017 - support for Due etc.
// v0.7 27/11/2017 - Added Get Time support
// v0.8 06/12/2017 - Added FM/RDS Functionality
// v0.9 05/09/2019 - Enhanced FM functionality (seek/scan)
// v1.0 23/09/2020 - Added ESP32 D1 R32 Support
// v1.1 11/12/2020 - Added DAB Status, Mono and Mute
// v1.2 01/11/2021 - Added DAB Service Type, Dab/Dab+
///////////////////////////////////////////////////////////
// Changed for use with datalink: Kurt Nielsen kurtn.nypost@hotmail.dk
#include <SPI.h>
#include <DABShield.h>
//In order to compile for the UNO, all strings are put into flash (PROGRAM MEMORY)
const char pty_0[] PROGMEM = "None";
const char pty_1[] PROGMEM = "News";
const char pty_2[] PROGMEM = "Current affairs";
const char pty_3[] PROGMEM = "Information";
const char pty_4[] PROGMEM = "Sport";
const char pty_5[] PROGMEM = "Education";
const char pty_6[] PROGMEM = "Drama";
const char pty_7[] PROGMEM = "Culture";
const char pty_8[] PROGMEM = "Science";
const char pty_9[] PROGMEM = "Varied";
const char pty_10[] PROGMEM = "Pop music";
const char pty_11[] PROGMEM = "Rock music";
const char pty_12[] PROGMEM = "Easy listening music";
const char pty_13[] PROGMEM = "Light classical";
const char pty_14[] PROGMEM = "Serious classical";
const char pty_15[] PROGMEM = "Other music";
const char pty_16[] PROGMEM = "Weather";
const char pty_17[] PROGMEM = "Finance";
const char pty_18[] PROGMEM = "Children’s programmes";
const char pty_19[] PROGMEM = "Social Affairs";
const char pty_20[] PROGMEM = "Religion";
const char pty_21[] PROGMEM = "Phone In";
const char pty_22[] PROGMEM = "Travel";
const char pty_23[] PROGMEM = "Leisure";
const char pty_24[] PROGMEM = "Jazz music";
const char pty_25[] PROGMEM = "Country music";
const char pty_26[] PROGMEM = "National music";
const char pty_27[] PROGMEM = "Oldies music";
const char pty_28[] PROGMEM = "Folk music";
const char pty_29[] PROGMEM = "Documentary";
const char pty_30[] PROGMEM = "Alarm test";
const char pty_31[] PROGMEM = "Alarm";
const
char *const pty[] PROGMEM =
{pty_0,pty_1,pty_2,pty_3,pty_4,pty_5,pty_6,pty_7,pty_8,pty_9,pty_10,pty_11,pty_12,pty_13,pty_14,pty_15,pty_16,pty_17,pty_18,pty_19,pty_20,pty_21,pty_22,pty_23,pty_24,pty_25,pty_26,pty_27,pty_28,pty_29,pty_30,pty_31};
const char mode_0[] PROGMEM = "Dual";
const char mode_1[] PROGMEM = "Mono";
const char mode_2[] PROGMEM = "Stereo";
const char mode_3[] PROGMEM = "Joint Stereo";
const char *const audiomode[] PROGMEM = {mode_0,mode_1,mode_2,mode_3};
int res = 0;
int i = 0;
const byte in = 3;
const byte interruptPin = 3;
int flag = 0;
int count = 0;
int p_count = 1;
volatile bool readInput = false;
volatile bool startFlag = true;
volatile unsigned long startMillis;
volatile unsigned long endMillis;
volatile unsigned long diffMillis;
volatile unsigned long currentMillis;
uint8_t freq_indexT = 0;
uint8_t freq_indexT1 = 26;
uint8_t freq_indexT2 = 30;
//presets
uint8_t pro1[3] = {26,5};//DR1
uint8_t pro2[3] = {26,3};//DR2
uint8_t pro3[3] = {26,4};//DR3
uint8_t pro4[3] = {26,7};//DR4KBH
uint8_t pro5[3] = {26,12};//DR5KBH
uint8_t pro6[3] = {26,1};//DR6
uint8_t pro7[3] = {26,11};//DR8
uint8_t pro8[3] = {30,2};//Classic Rock
uint8_t pro9[3] = {30,5};//Radio Vinyl
//#define DAB_SPI_BITBANG
//#define ANALOG_VOLUME
#ifdef ARDUINO_ARCH_SAMD
#define Serial SerialUSB
//Comment out the following line if using the ICSP connector (and modified DABShield).
#define DAB_SPI_BITBANG
#endif
#ifdef ARDUINO_ARCH_SAM
#define Serial SerialUSB
//Comment out the following line if using the ICSP connector (and modified DABShield).
#define DAB_SPI_BITBANG
#endif
DAB Dab;
//SPI Ports for BIT
#if defined(ARDUINO_ARCH_ESP32)
const byte slaveSelectPin = 12;
#else
const byte slaveSelectPin = 8;
#endif
#ifdef DAB_SPI_BITBANG
#if defined(ARDUINO_ARCH_ESP32)
const byte SCKPin = 18;
const byte MISOPin = 19;
const byte MOSIPin = 23;
#else
const byte SCKPin = 13;
const byte MISOPin = 12;
const byte MOSIPin = 11;
#endif
#endif
bool dabmode = true;
DABTime dabtime;
uint8_t vol = 63;
uint8_t service = 0;
uint8_t freq = 0;
byte rxindex = 0;
char rxdata[32];
void setup() {
//Intitialise the terminal
Serial.begin(115200);
pinMode(in, INPUT);
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), inPut, FALLING);
startMillis = millis();
//while(!Serial);
Serial.print(F("
AVIT DAB 2017-20\n\n"));
//Enable SPI
#ifdef DAB_SPI_BITBANG
pinMode(slaveSelectPin, OUTPUT);
pinMode(SCKPin, OUTPUT);
pinMode(MOSIPin, OUTPUT);
pinMode(MISOPin, INPUT_PULLUP);
digitalWrite(slaveSelectPin, HIGH);
#else
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, HIGH);
SPI.begin();
#endif
Serial.print(F("Initialising....."));
//DAB Setup
Dab.setCallback(ServiceData);
Dab.begin();
if(Dab.error != 0)
{
Serial.print(F("ERROR: "));
Serial.print(Dab.error);
Serial.print(F("\nCheck DABShield is Connected and SPI Communications\n"));
}
else
{
Serial.print(F("done\n\n"));
Help_Menu();
Serial.print(F("DAB>"));
}
//Start af program power on.
/*uint8_t freq_index = 26;
Dab.tune(freq_index);
uint8_t index = 11;
Dab.set_service(index);*/
}
void tuneTemp()
{
uint8_t freq_index = 0;
freq_index = freq_indexT;
Dab.tune(freq_index);
}
void decode (int res)
{
uint8_t freq_index = 0;
uint8_t index = 0;
flag = 0;
if (res > 0 && res < 10)
{
p_count = res;
//delay (60);
flag = 1;
}
switch (res)
{
case 20 : {//start
p_count = 1;
flag = 1;
}
break;
case 23 : {
if (Dab.numberofservices > 0)//<< LEFT ARROW
{
if (service > 0)
{
service--;
}
else
{
//Serial.println(freq_index);
if (freq_indexT == freq_indexT1)
{
freq_indexT = freq_indexT2;
tuneTemp();
}
else
freq_indexT = freq_indexT1;
service = Dab.numberofservices;
}
tuneTemp();
Dab.set_service(service);
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
flag = 0;
}} break;
case 24 : {if (Dab.numberofservices > 0)//>> RIGHT ARROW
{
if (service < (Dab.numberofservices - 1))
{
service++;
}
else
{
Serial.println(freq_index);
if (freq_indexT == freq_indexT1)
{
freq_indexT = freq_indexT2;
tuneTemp();
}
else
freq_indexT = freq_indexT1;
service = 1;
}
tuneTemp();
Dab.set_service(service);
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
flag = 0;
}}
break;
case 37 : {//OFF
//mp3->stop();
flag = 0;
}
break;
case 10 : {p_count ++;//RIGHT ARROW
flag = 1;}
break;
case 57 : {p_count --;//LEFT ARROW
flag = 1;}
break;
case 12 : {p_count ++;//T RIGHT ARROW
flag = 1;}
break;
case 25 : {p_count --;//T LEFT ARROW
flag = 1;}
break;
default: {
//flag = 0;
//return 0;
}
}
if (flag == 1)
{
if (p_count > 9)
{
p_count = 1;
}
if (p_count < 1)
{
p_count = 9;
}
uint8_t freq_index = 0;
uint8_t index =
0;
switch (p_count)
{
case 1 : {freq_index = pro1[0]; index = pro1[1];}break;
case 2 : {freq_index = pro2[0]; index = pro2[1];}break;
case 3 : {freq_index = pro3[0]; index = pro3[1];}break;
case 4 : {freq_index = pro4[0]; index = pro4[1];}break;
case 5 : {freq_index = pro5[0]; index = pro5[1];}break;
case 6 : {freq_index = pro6[0]; index = pro6[1];}break;
case 7 : {freq_index = pro7[0]; index = pro7[1];}break;
case 8 : {freq_index = pro8[0]; index = pro8[1];}break;
case 9 : {freq_index = pro9[0]; index = pro9[1];}break;
default: {}
}
Dab.tune(freq_index);
Dab.set_service(index);
freq_indexT = freq_index;
}
}
void ServiceData(void)
{
if (dabmode == true)
{
Serial.print(Dab.ServiceData);
Serial.print(F("\n"));
}
else
{
char statusstring[72];
sprintf_P(statusstring, PSTR("%02d/%02d/%04d "), Dab.Days, Dab.Months, Dab.Year);
Serial.print(statusstring);
sprintf_P(statusstring, PSTR("%02d:%02d "),Dab.Hours, Dab.Minutes);
Serial.print(statusstring);
sprintf_P(statusstring, PSTR("%s "),Dab.ps);
Serial.print(statusstring);
sprintf_P(statusstring, PSTR("%s\n"),Dab.ServiceData);
Serial.print(statusstring);
}
}
void inPut()
{
readInput = true;
}
void readInputPin()
{
endMillis = millis();
diffMillis = endMillis - startMillis;
startMillis = millis();
if (diffMillis > 60)
{
res = 0;
delayMicroseconds (200);
for (i = 1; i < 7; i++)
{
delayMicroseconds (3100);
res = (res << 1) + (!digitalRead (in));
}
if (res == 62)
count = 1;
else
{
if ((res == 20 || res == 21 || res == 35 || res == 38) &&
(count == 0))
{
count = 1;
res = 20;
decode(res);
}
else if (res == 24 || res == 41)
{
res = 24;
decode(res);
}
else if (res == 23 || res == 40)
{
res = 23;
decode(res);
}
/*else if (res == 26 || res == 38)
{
res = 26;
decode(res);
}*/
else
{
decode(res);
count ++;
if (count > 1)
count = 0;
startFlag = true;
}
}
}
}
void loop() {
// put your main code here, to run repeatedly:
if (readInput)
{
readInput = false;
startFlag = false;
readInputPin();
}
#ifdef ANALOG_VOLUME
static int volumeADCFilter = -1;
//Analogue Volume control...
int volumeADC = analogRead(A1);
if (volumeADCFilter == -1)
volumeADCFilter = volumeADC;
else
volumeADCFilter += (volumeADC - volumeADCFilter) / 64;
byte vollimit = map(volumeADCFilter, 64, 1023, 0, 63);
if(vollimit != vol)
{
vol = vollimit;
Dab.vol(vol);
}
#endif
Dab.task();
if (Serial.available() > 0)
{
rxdata[rxindex] = Serial.read();
if (rxdata[rxindex] == '\r' || rxdata[rxindex] == '\n') //return or linefeed
{
Serial.print(F("\n"));
rxdata[rxindex] = '\0';
process_command(rxdata);
rxindex = 0;
}
else if (rxdata[rxindex] == '\b') //backspace
{
if (rxindex > 0)
{
Serial.print(F("\b \b"));
rxindex--;
}
}
else if (rxdata[rxindex] == '+')
{
if (vol < 63)
{
vol++;
Dab.vol(vol);
}
}
else if (rxdata[rxindex] == '-')
{
if (vol > 0)
{
vol--;
Dab.vol(vol);
}
}
else if (rxdata[rxindex] == '<')
{
if (Dab.numberofservices > 0)
{
if (service > 0)
{
service--;
}
else
{
service = Dab.numberofservices - 1;
}
Dab.set_service(service);
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
}
}
else if (rxdata[rxindex] == '>')
{
if (Dab.numberofservices > 0)
{
if (service < (Dab.numberofservices - 1))
{
service++;
}
else
{
service = 0;
}
Dab.set_service(service);
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
}
}
else //other char
{
Serial.print(rxdata[rxindex]);
rxindex++;
if (rxindex >= 32)
{
rxindex = 0;
}
}
}
}
void Help_Menu(void)
{
Serial.print(F("
HELP\n"));
Serial.print(F("PartNo = Si"));
Serial.print(Dab.PartNo);
Serial.print(F("\n"));
Serial.print(F("Firmware Version = "));
Serial.print(Dab.VerMajor);
Serial.print(F("."));
Serial.print(Dab.VerMinor);
Serial.print(F("."));
Serial.print(Dab.VerBuild);
Serial.print(F("\n"));
Serial.print(F("________________________________________________________\n\n"));
if (dabmode == true)
{
Serial.print(F("scan
- scans for valid DAB ensembles\n"));
Serial.print(F("tune
<n>
- tunes to frequency index\n"));
Serial.print(F("service
<ID>
- tunes to service ID\n"));
Serial.print(F("info
- displays the ensemble info\n"));
Serial.print(F("time
- displays the current DAB time\n"));
Serial.print(F("fm
- FM Mode\n"));
}
else
{
Serial.print(F("dab
- DAB Mode\n"));
Serial.print(F("tune
<n>
- tunes to frequency in kHz (87500 == 87.5MHz)\n"));
Serial.print(F("seek
<up/down>
- seeks to next station\n"));
Serial.print(F("scan
- lists available stations\n"));
}
Serial.print(F("volume
<n>
- set volume 0 - 63\n"));
Serial.print(F("mono
- set audio mode to mono\n"));
Serial.print(F("stereo
- set audio mode to stereo\n"));
Serial.print(F("mute <on/off/left/right> - mutes/unmutes audio\n"));
Serial.print(F("status
- displays audio/reception info\n"));
Serial.print(F("help
- displays this menu\n"));
Serial.print(F("________________________________________________________\n\n"));
}
void process_command(char *command)
{
char *cmd;
cmd = strtok(command, " \r");
if (strcmp_P(cmd, PSTR("tune")) == 0)
{
cmd = strtok(NULL, " \r");
if(dabmode == true)
{
freq = (int)strtol(cmd, NULL, 10);
service = 0;
Dab.tune(freq);
Ensemble_Info();
}
else
{
uint32_t freqkhz = strtol(cmd, NULL, 10);
if((freqkhz >= 87500) && (freqkhz <= 107900))
{
Dab.tune((uint16_t)(freqkhz/10));
FM_status();
}
else if((freqkhz >= 8750) && (freqkhz <= 10790))
{
Dab.tune((uint16_t)freqkhz);
FM_status();
}
else
{
Serial.print(F("Freq not in range\n"));
}
}
}
else if (strcmp_P(cmd, PSTR("service")) == 0)
{
cmd = strtok(NULL, " \r");
service = (uint8_t)strtol(cmd, NULL, 10);
Dab.set_service(service);
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
}
else if (strcmp_P(cmd, PSTR("volume")) == 0)
{
cmd = strtok(NULL, " \r");
vol = (uint8_t)strtol(cmd, NULL, 10);
Dab.vol(vol);
}
else if (strcmp_P(cmd, PSTR("info")) == 0)
{
Ensemble_Info();
}
else if (strcmp_P(cmd, PSTR("scan")) == 0)
{
if(dabmode == true)
{
DAB_scan();
}
else
{
FM_scan();
}
}
else if (strcmp(cmd, "fm") == 0)
{
Dab.begin(1);
dabmode = false;
Dab.tune((uint16_t)8750);
Help_Menu();
}
else if (strcmp(cmd, "dab") == 0)
{
Dab.begin(0);
dabmode = true;
Help_Menu();
}
else if (strcmp(cmd, "mono") == 0)
{
Dab.mono(true);
}
else if (strcmp(cmd, "stereo") == 0)
{
Dab.mono(false);
}
else if (strcmp(cmd, "mute") == 0)
{
cmd = strtok(NULL, " \r");
if(strcmp(cmd, "on") == 0)
{
Dab.mute(true, true);
}
else if(strcmp(cmd, "off") == 0)
{
Dab.mute(false, false);
}
else if(strcmp(cmd, "left") == 0)
{
Dab.mute(true, false);
}
else if(strcmp(cmd, "right") == 0)
{
Dab.mute(false, true);
}
}
else if (strcmp(cmd, "seek") == 0)
{
bool valid = false;
cmd = strtok(NULL, " \r");
if(strcmp(cmd, "up") == 0)
{
valid = Dab.seek(1, 1);
}
else if(strcmp(cmd, "down") == 0)
{
valid = Dab.seek(0, 1);
}
if(valid == true)
{
FM_status();
}
}
else if (strcmp(cmd, "status") == 0)
{
if(dabmode == true)
{
DAB_status();
}
else
{
FM_status();
}
}
else if (strcmp(cmd, "time") == 0)
{
char timestring[16];
Dab.time(&dabtime);
sprintf(timestring,"%02d/%02d/%02d ", dabtime.Days,dabtime.Months,dabtime.Year);
Serial.print(timestring);
sprintf(timestring,"%02d:%02d\n", dabtime.Hours,dabtime.Minutes);
Serial.print(timestring);
}
else if (strcmp(cmd, "help") == 0)
{
Help_Menu();
}
else if (strlen(command) == 0)
{
//no command
}
else
{
Serial.print(F("Unknown command\n"));
}
if(dabmode == true)
{
Serial.print(F("DAB>"));
}
else
{
Serial.print(F("FM>"));
}
}
void Ensemble_Info(void)
{
char freqstring[32];
uint8_t i;
Serial.print(F("\n\nEnsemble Freq "));
sprintf(freqstring, "%02d\t %03d.", Dab.freq_index, (uint16_t)(Dab.freq_khz(Dab.freq_index) / 1000));
Serial.print(freqstring);
sprintf(freqstring, "%03d MHz", (uint16_t)(Dab.freq_khz(Dab.freq_index) % 1000));
Serial.print(freqstring);
Serial.print(F("\n"));
Serial.print(Dab.Ensemble);
Serial.print(F("\n"));
Serial.print(F("\nServices: \n"));
Serial.print(F("ID\tName\n\n"));
for (i = 0; i < Dab.numberofservices; i++)
{
Serial.print(i);
Serial.print(F(":\t"));
Serial.print(Dab.service[i].Label);
Dab.status(Dab.service[i].ServiceID, Dab.service[i].CompID);
if(Dab.type == SERVICE_AUDIO)
{
Serial.print(F("\t dab"));
if(Dab.dabplus == true)
{
Serial.print(F("+"));
}
}
else if(Dab.type == SERVICE_DATA)
{
Serial.print(F("\t data"));
}
Serial.print(F("\n"));
}
Serial.print(F("\n"));
}
void DAB_scan(void)
{
uint8_t freq_index;
char freqstring[32];
for (freq_index = 0; freq_index < DAB_FREQS; freq_index++)
{
Serial.print(F("\nScanning Freq "));
sprintf(freqstring, "%02d\t %03d.", freq_index, (uint16_t)(Dab.freq_khz(freq_index) / 1000));
Serial.print(freqstring);
sprintf(freqstring, "%03d MHz", (uint16_t)(Dab.freq_khz(freq_index) % 1000));
Serial.print(freqstring);
Dab.tune(freq_index);
if(Dab.servicevalid() == true)
{
Ensemble_Info();
}
}
Serial.print(F("\n\n"));
}
void DAB_status(void)
{
char dabstring[32];
Dab.status();
Serial.print(Dab.service[service].Label);
Serial.print(F("\n"));
sprintf_P(dabstring,PSTR("PTY = %S (%d)\n"), pgm_read_word(&pty[Dab.pty]), Dab.pty);
Serial.print(dabstring);
sprintf(dabstring,"Bit Rate = %d kHz, ", Dab.bitrate);
Serial.print(dabstring);
sprintf(dabstring,"Sample Rate = %d Hz, ", Dab.samplerate);
Serial.print(dabstring);
sprintf_P(dabstring,PSTR("Audio Mode = %S (%d)\n"), pgm_read_word(&audiomode[Dab.mode]), Dab.mode);
Serial.print(dabstring);
sprintf_P(dabstring, PSTR("Serivce Mode = %s\n"), Dab.dabplus == true ? PSTR("dab+") : PSTR("dab"));
Serial.print(dabstring);
sprintf(dabstring,"RSSI = %d, SNR = %d, Quality = %d%, ", Dab.signalstrength, Dab.snr, Dab.quality);
Serial.print(dabstring);
Serial.print(F("\n"));
}
void FM_status(void)
{
char freqstring[32];
Dab.status();
sprintf(freqstring, "Freq = %3d.", (uint16_t)Dab.freq / 100);
Serial.print(freqstring);
sprintf(freqstring, "%1d MHz : ", (uint16_t)(Dab.freq % 100)/10);
Serial.print(freqstring);
sprintf(freqstring,"RSSI = %d, ",Dab.signalstrength);
Serial.print(freqstring);
sprintf(freqstring,"SNR = %d\n",Dab.snr);
Serial.print(freqstring);
}
void FM_scan(void)
{
uint16_t startfreq = Dab.freq;
Dab.vol(0);
Dab.tune((uint16_t)8750);
while(Dab.seek(1, 0) == true)
{
FM_status();
if(Dab.freq == 10790)
break;
}
Dab.tune(startfreq);
Dab.vol(vol);
}
#ifdef DAB_SPI_BITBANG
void DABSpiMsg(unsigned char *data, uint32_t len)
{
digitalWrite(SCKPin, LOW);
digitalWrite (slaveSelectPin, LOW);
for(uint32_t l=0; l<len; l++)
{
unsigned char spiByte = data[l];
for (uint32_t i = 0; i < 8; i++)
{
digitalWrite(MOSIPin, (spiByte & 0x80) ? HIGH : LOW);
delayMicroseconds(1);
digitalWrite(SCKPin, HIGH);
spiByte = (spiByte << 1) | digitalRead(MISOPin);
digitalWrite(SCKPin, LOW);
delayMicroseconds(1);
}
data[l] = spiByte;
}
digitalWrite (slaveSelectPin, HIGH);
}
#else
void DABSpiMsg(unsigned char *data, uint32_t len)
{
SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); //2MHz for starters...
digitalWrite (slaveSelectPin, LOW);
SPI.transfer(data, len);
digitalWrite (slaveSelectPin, HIGH);
SPI.endTransaction();
}
#endif
//Bemærk:
dette er eksperimentel software der er ingen garanti for brugbarhed, det
kan derimod sandsynligvis være skadeligt, kun til brug i Danmark.