Repository navigation
Expand file tree
/
Copy pathPixelRadio.cpp
More file actions
368 lines (311 loc) · 20 KB
/
Copy pathPixelRadio.cpp
File metadata and controls
368 lines (311 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*
File: PixelRadio.cpp
Project: PixelRadio, an RBDS/RDS FM Transmitter (QN8027 Digital FM IC)
Version: 1.1.2
Creation: Dec-16-2021
Revised: Oct-24-2022
Project Leader: T. Black (thomastech)
Contributors: thomastech, dkulp
(c) copyright T. Black 2021-2022.
Licensed under GNU GPL 3.0 and later, under this license absolutely no warranty is given.
This Code was formatted with the uncrustify extension.
Revision History:
V0.0, Dec-16-2021: Started Project.
V0.1, Jan-20-2022: Updated to ESP32UI PR#149 (Jan-20-2022 build). Resolved ongoing ESPUI Issues.
V0.2, Jan-28-2022: Migrated Hardware, ICStation FM Transmitter Board changed to basic QN8027 I2C Module.
V0.3, Feb-04-2022: Updated platformio/espressif32 3.4.0 to 3.5.0.
V1.0B, Feb-28-2022: Non-public beta release by thomastech.
V1.0, Apr-05-2022: First Public Release.
V1.1.0, Jun-13-2022: Updated espressif32 platform to 4.4.0, was 3.5.0. Core now 6.0.2, was 5.2.5
PlatformIO Package = framework-arduinoespressif32 3.20003.0 (2.0.3).
Updated ArduinoJson to 6.19.4, was 6.19.1
Updated LittleFS to 2.0.0 (built-in), was 1.0.6.
Changed platformio.ini board type to ttgo-t1, was esp32dev.
Added PTY (Program Type) command to HTTP, MQTT, and Serial Controllers (dkulp submission).
Added programmable Local Controller PTY code to Web UI (on Local RDS Tab).
Added RF Carrier On/Off to the Controller API.
Fixed 49 day System Run Time hang.
Fixed Web UI's displayed IP Address so it updates on reconnects.
Added test for broken QN8027 Oscillator (post failure to serial log).
Updated test tone routine for compatibility with latest espressif32 + removed blocking code.
Corrected millis var type (found some that were int32, must be uint32).
Added Percent (%) Decoding to HTTP API's URL.
Performed misc code cleanup.
Updated Readme documentation, added photos for external WiFi antenna modification.
-> Validation completed on Jun-17-2022, public release approved.
V1.1.1, Jun-30-2022: For backwards compatibility, HTTP url commands "psn" and "rtm" can use %20 by itself to clear text.
Revised Documentation, added info about HTTP Text clearing, creating PI Codes, Falcon Player.
V1.1.2, Oct-18-2022: Updated espressif32 platform to Ver 5.2.0 (was 4.4.0). Core now 6.1.4, was 6.0.2.
Updated ESPAsyncWebServer to Ver 3.0.0 (was 2.1.0).
Updated ESPUI library to PR #199, release Oct-2022. Stored locally in project lib folder.
Changed all millis() related vars to unsigned long (was uint32).
Minor edits to readme doc files.
-> Validation completed on Oct-18-2022, public release approved.
Notes:
1. This "Arduino" project must be compiled with VSCode / Platformio. Do not use the Arduino IDE.
2. The target CPU is a LilyGO "TTGO T8" (V1.7.1) ESP32 with 4MB Flash (minimum).
Early version of the TTGO-T8 used the CP2104 USB UART. Latest versions use the CH9102F. WIN10 will automatically
install the driver.
Important: The TTGO-T8 slide switch must be set "ON" (position nearest the RST switch). Otherwise USB is disabled.
3. This project's QN8027 Arduino Library has been modified for use with PixelRadio. The original library version can be
found here:
https://github.com/ManojBhakarPCM/Arduino-QN8027-with-Full-RDS-support
4. Flash Tool Memory Map
0x1000 bootloader_dio_40m.bin
0x8000 partitions.bin
0xe000 boot_app0.bin
0x10000 pixelradio.bin
*/
// ************************************************************************************************
#include <Arduino.h>
#include <ArduinoOTA.h>
#include <ArduinoLog.h>
#include <DNSServer.h>
#include <WiFi.h>
#include "config.h"
#include "credentials.h"
#include "PixelRadio.h"
#include "language.h"
#include "QN8027Radio.h"
// ************************************************************************************************
// Global Section
//
QN8027Radio radio = QN8027Radio(); // FM Transmitter Module (QN8027 radio)
// Global System Vars
bool activeTextHttpFlg = false; // HTTP Controller Is Sending RadioText if true.
bool activeTextLocalFlg = false; // Local Controller Is Sending RadioText if true.
bool activeTextMqttFlg = false; // MQTT Controller Is Sending RadioText if true.
bool activeTextSerialFlg = false; // Serial Controller Is Sending RadioText if true.
bool mqttOnlineFlg = false; // MQTT is online if true.
bool newAutoRfFlg = false; // new RF Auto Off Setting Avail Semaphore.
bool newCarrierFlg = false; // New Carrier Settings Avail Semaphore.
bool newDigGainFlg = false; // New Digital Audio Gain Setting Avail Semaphore.
bool newGpio19Flg = false; // New GPIO Pin 19 State.
bool newGpio23Flg = false; // New GPIO Pin 23 State.
bool newGpio33Flg = false; // New GPIO Pin 33 State.
bool newFreqFlg = false; // New Radio Frequency Setting Avail Semaphore.
bool newInpImpFlg = false; // New Input Impedance Setting Avail Semaphore.
bool newAudioModeFlg = false; // New Mono/Stereo Audio Setting Avail Semaphore.
bool newMuteFlg = false; // New Audio Mute Setting Avail Semaphore.
bool newPreEmphFlg = false; // New Radio Pre-Emphasis Setting Avail Semaphore.
bool newRfPowerFlg = false; // New RfPower Setting Avail Semaphore.
bool newVgaGainFlg = false; // New Analog VGA Gain Setting Avail Semaphore.
bool rebootFlg = false; // Reboot System if true;
bool stopHttpFlg = false; // HTTP Controller Stop Command was sent if true.
bool stopMqttFlg = false; // MQTT Controller Stop Command was sent if true.
bool stopSerialFlg = false; // Serial Controller Stop Command was sent if true.
bool testModeFlg = false; // Audio Test Tone Mode if true. Do NOT save in config file.
bool textSerialFlg = false; // Serial supplied RDS Text Avail, #1 Priority.
bool textHttpFlg = false; // HTTP supplied RDS Text Avail, #2 Priority.
bool textMqttFlg = false; // MQTT supplied RDS Text Avail, #3 Priority.
uint8_t fmRadioTestCode = FM_TEST_OK; // FM Radio Module Test Result Code.
uint16_t mqttPort = MQTT_PORT_DEF; // mqttPort is a Fixed Value. Does not change.
uint16_t rdsHttpPiCode = RDS_PI_CODE_DEF; // HTTP Controller PI Code, can be changed by HTTP Command.
uint16_t rdsMqttPiCode = RDS_PI_CODE_DEF; // MQTT Controller PI Code, can be changed by MQTT Command.
uint16_t rdsSerialPiCode = RDS_PI_CODE_DEF; // Serial Controller PI Code, can be changed by Serial Command.
uint8_t rdsHttpPtyCode = RDS_PTY_CODE_DEF; // HTTP Controller PTY Code, can be changed by HTTP Command.
uint8_t rdsMqttPtyCode = RDS_PTY_CODE_DEF; // MQTT Controller PTY Code, can be changed by MQTT Command.
uint8_t rdsSerialPtyCode = RDS_PTY_CODE_DEF; // Serial Controller PTY Code, can be changed by Serial Command.
unsigned long rdsHttpMsgTime = RDS_DSP_TM_DEF; // HTTP Controller's Message Time Can be Changed by HTTP Command.
unsigned long rdsLocalMsgTime = RDS_DSP_TM_DEF; // Local Controller's Message Time Can be Changed by Web UI.
unsigned long rdsMsgTime = RDS_DSP_TM_DEF; // Global (Master) RDS Message Time. Set by RDS Controllers.
unsigned long rdsMqttMsgTime = RDS_DSP_TM_DEF; // MQTT Controller's Message Time Can be Changed by MQTT Command.
unsigned long rdsSerialMsgTime = RDS_DSP_TM_DEF; // Serial Controller's Message Time Can be Changed by Serial Command.
float vbatVolts = 0.0f; // ESP32's Onboard "VBAT" Voltage. Typically 5V.
float paVolts = 0.0f; // RF Power Amp's Power Supply Voltage. Typically 9V.
String gpio19CtrlStr = ""; // GPIO-19 State if Changed by Serial/MQTT/HTTP Controller.
String gpio23CtrlStr = ""; // GPIO-23 State if Changed by Serial/MQTT/HTTP Controller.
String gpio33CtrlStr = ""; // GPIO-33 State if Changed by Serial/MQTT/HTTP Controller.
String ipAddrStr = ""; // DHCP IP Address;
String rdsHttpPsnStr = RDS_PSN_DEF_STR; // HTTP Supplied Program Service Name.
String rdsMqttPsnStr = RDS_PSN_DEF_STR; // MQTT Supplied Program Service Name.
String rdsSerialPsnStr = RDS_PSN_DEF_STR; // Serial Supplied Program Service Name.
String rdsSerialTextStr = ""; // RDS RadioText for Serial Controller.
String rdsHttpTextStr = ""; // RDS RadioText for HTTP Controller.
String rdsMqttTextStr = ""; // RDS RadioText for MQTT Controller.
String rdsTextMsgStr = ""; // Current RDS RadioText Message.
IPAddress hotSpotIP = HOTSPOT_IP_DEF;
IPAddress mqttIP = MQTT_IP_DEF;
IPAddress staticIP = IP_ADDR_DEF;
IPAddress subNet = SUBNET_MASK_DEF;
IPAddress wifiDNS = WIFI_ADDR_DEF;
IPAddress wifiGateway = WIFI_ADDR_DEF;
// ************************************************************************************************
// Configuration Vars (Can be saved to LittleFS and SD Card)
bool apFallBackFlg = AP_FALLBACK_DEF_FLG; // Control, Switch to AP mode if STA fails.
bool ctrlLocalFlg = CTRL_LOCAL_DEF_FLG; // Control, Permit Local Control if true.
bool ctrlHttpFlg = CTRL_HTTP_DEF_FLG; // Control, Permit HTTP Control if true.
bool ctrlMqttFlg = CTRL_MQTT_DEF_FLG; // Control, Permit MQTT if true.
// bool ctrlSerialFlg = CTRL_SERIAL_DEF_FLG; // Replaced by ctrlSerialFlg() function.
bool muteFlg = RADIO_MUTE_DEF_FLG; // Control, Mute audio if true.
bool rfAutoFlg = RF_AUTO_OFF_DEF_FLG; // Control, Turn Off RF carrier if no audio for 60Sec. false=Never turn off.
bool rdsText1EnbFlg = RDS_TEXTX_DEF_FLG; // Control, Disable local RDS Text Msg #1 if false.
bool rdsText2EnbFlg = RDS_TEXTX_DEF_FLG; // Control, Disable local RDS Text Msg #1 if false.
bool rdsText3EnbFlg = RDS_TEXTX_DEF_FLG; // Control, Disable local RDS Text Msg #1 if false.
bool rfCarrierFlg = RF_CARRIER_DEF_FLG; // Control, Turn off RF if false.
bool stereoEnbFlg = STEREO_ENB_DEF_FLG; // Control, Enable Stereo FM if true (false = Mono).
bool wifiDhcpFlg = CTRL_DHCP_DEF_FLG; // Control, Use DHCP if true, else static IP.
bool WiFiRebootFlg = WIFI_REBOOT_DEF_FLG; // Control, Reboot if all connections fail.
uint8_t analogVol = (atoi(ANA_VOL_DEF_STR)); // Control. Unused, for future expansion.
uint8_t usbVol = (atoi(USB_VOL_DEF_STR)); // Control. Unused, for future expansion.
uint8_t rdsLocalPtyCode = RDS_PTY_CODE_DEF; // Control. Local PTY Code, default for all controllers.
uint16_t rdsLocalPiCode = RDS_PI_CODE_DEF; // Control. Local PI Code, default for all controllers.
uint32_t baudRate = ESP_BAUD_DEF; // Control.
uint16_t fmFreqX10 = FM_FREQ_DEF_X10; // Control. FM MHz Frequency X 10 (881 - 1079).
String apNameStr = AP_NAME_DEF_STR; // Control.
String apIpAddrStr = IpAddressToString(HOTSPOT_IP_DEF); // Control.
String ctrlSerialStr = SERIAL_DEF_STR; // Control.
String preEmphasisStr = PRE_EMPH_DEF_STR; // Control.
String digitalGainStr = DIG_GAIN_DEF_STR; // Control.
String gpio19BootStr = GPIO_DEF_STR; // Control.
String gpio23BootStr = GPIO_DEF_STR; // Control.
String gpio33BootStr = GPIO_DEF_STR; // Control.
String inpImpedStr = INP_IMP_DEF_STR; // Control.
String logLevelStr = DIAG_LOG_DEF_STR; // Control, Serial Log Level.
String mdnsNameStr = MDNS_NAME_DEF_STR; // Control.
String mqttIpStr = IpAddressToString(MQTT_IP_DEF); // Control.
String mqttNameStr = MQTT_NAME_DEF_STR; // Control.
String mqttPwStr = MQTT_PW_STR; // Control.
String mqttUserStr = MQTT_ID_STR; // Control.
String rdsTextMsg1Str = RDS_TEXT1_DEF_STR; // Control.
String rdsTextMsg2Str = RDS_TEXT2_DEF_STR; // Control.
String rdsTextMsg3Str = RDS_TEXT3_DEF_STR; // Control.
String rdsLocalPsnStr = RDS_PSN_DEF_STR; // Control, Program Service Name.
String rfPowerStr = RF_PWR_DEF_STR; // Control.
String staticIpStr = "";
String subNetStr = subNet.toString(); // Control.
String staNameStr = STA_NAME_DEF_STR; // Control.
String userNameStr = LOGIN_USER_NAME_STR; // Control.
String userPassStr = LOGIN_USER_PW_STR; // Control.
String vgaGainStr = VGA_GAIN_DEF_STR; // Control.
String wifiDnsStr = ""; // Control.
String wifiGatewayStr = ""; // Control.
String wifiSSIDStr = SSID_NM_STR; // Control, Private WiFi Credentials.
String wifiWpaKeyStr = WPA_KEY_STR; // Control, Private WiFi Credentials.
// *********************************************************************************************
void setup()
{
bool successFlg = true;
char logBuff[80];
// enableCore1WDT();
pinMode(ON_AIR_PIN, OUTPUT); // "ON AIR" Sign Driver Output.
pinMode(MISO_PIN, INPUT_PULLUP); // MISO Requires Internal Pull-up.
pinMode(SCL_PIN, INPUT_PULLUP); // I2C Clock Pin.
pinMode(SDA_PIN, INPUT); // I2C Data Pin. Do NOT Enable Internal Pullup.
pinMode(MUX_PIN, OUTPUT); // Audio MUX Control (Line-In:Tone-In), Output.
pinMode(TONE_PIN, OUTPUT); // PWM Audio Test Tone, Output.
pinMode(SD_CS_PIN, OUTPUT); // SD Card Chip Select, Output.
digitalWrite(MUX_PIN, TONE_OFF); // Init Audio Mux, Enable Audio Line-In Jack, Music LED On.
digitalWrite(SD_CS_PIN, HIGH);
// Initialize Tone Generator.
toneInit();
// Initialize i2c.
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(I2C_FREQ_HZ); // 100KHz i2c speed.
pinMode(SCL_PIN, INPUT_PULLUP); // I2C Clock Pin.
// delay(3000); // DEBUG ONLY, wait for Platformio's monitor terminal.
// Initialize USB Serial.
Serial.begin(baudRate, SERIAL_8N1); // Open Serial-0 Port to Log System Messages.
// Serial1.begin(baudRate, SERIAL_8N1, SER1_RXD, SER1_TXD); // Optional Serial-1 Port.
// Serial1.println("\r\n\r\n");
// Serial1.println("COM1 NOW ALIVE");
while (!Serial && !Serial.available()) {} // Wait for ESP32 Device Serial Port to be available.
Serial.println("\r\n\r\n");
Serial.flush();
// Initialize the System Serial Log.
initSerialLog(true); // Initally Set to verbose log level. Will use config file setting at end of setup.
// Let's Start System Initialization
sprintf(logBuff, "PixelRadio FM Transmitter %s", AUTHOR_STR);
Log.infoln(logBuff);
sprintf(logBuff, "Version %s, %s", VERSION_STR, BLD_DATE_STR);
Log.infoln(logBuff);
Log.infoln("System is Starting ...");
// Initialize EEPROM Emulation.
// initEprom();
// Setup ADC
initVdcAdc(); // Initialize the Bat Voltage ADC.
initVdcBuffer(); // Initialize the two Power Supply Measurement Buffers.
Log.infoln("Initialized VBAT and RF_VDC ADCs.");
// Setup the File System.
littlefsInit();
instalLogoImageFile();
// Restore System Settings from File System.
restoreConfiguration(LITTLEFS_MODE, BACKUP_FILE_NAME);
resetControllerRdsValues(); // Must be called after restoreConfiguration().
setGpioBootPins(); // Must be called after restoreConfiguration().
if (checkEmergencyCredentials(CRED_FILE_NAME)) { // Check for Emergency WiFi Credential File on SD Card.
saveConfiguration(LITTLEFS_MODE, BACKUP_FILE_NAME); // Save restored credentials to file system.
}
digitalWrite(MUX_PIN, TONE_ON); // Turn off Music (Mux) LED.
if (wifiConnect()) { // Connected to Web Server.
#ifdef OTA_ENB
otaInit(); // Init OTA services.
#endif // ifdef OTA_ENB
#ifdef MQTT_ENB
mqttInit(); // Init MQTT services.
#endif // ifdef MQTT_ENB
}
digitalWrite(MUX_PIN, TONE_OFF); // Turn on Music (Mux) LED, restore Line-In to external audio.
// Startup the Serial Controller (serial parsing engine).
initSerialControl();
// Startup the I2C Devices).
i2cScanner(); // Scan the i2c bus and report all devices.
fmRadioTestCode = initRadioChip(); // If QN8027 fails we will warn user on UI homeTab.
Log.infoln("FM Radio RDS/RBDS Started.");
// Startup the Web GUI. DO THIS LAST!
Log.infoln("Initializing Web UI ...");
startGUI();
Log.infoln("-> Web UI Loaded.");
// End of System Initialization. Report pass/fail message to Serial Log.
if (successFlg && (fmRadioTestCode == FM_TEST_OK)) {
Log.infoln("PixelRadio System Init Complete, Success!");
}
else {
Log.fatalln("PixelRadio System Init Failed. Please Review Serial Log.");
}
Log.infoln("Changing Log Level to %s", logLevelStr.c_str());
initSerialLog(false);
Serial.flush();
}
// *********************************************************************************************
// Main Loop.
void loop()
{
// Background tasks
serialCommands(); // Process USB Serial Controller Commands.
processDnsServer(); // AP ESPUI DNS Server
processRDS(); // Send any available RadioText.
processMeasurements(); // Measure the two system voltages.
updateUiRSSI(); // Update the WiFi Signal Strength on UI homeTab & wifiTab.
updateUiFreeMemory(); // Update the Memory value on UI diagTab.
updateUiAudioLevel(); // Update the Audio Level value on UI diagtab.
updateUiDiagTimer(); // Upddate the Elapsed Timer on UI diagTab.
updateUiVolts(); // Update the two system voltages on UI diagTab.
updateRadioSettings(); // Update the QN8027 device registers.
updateGpioBootPins(); // Update the User Programmable GPIO Pins.
updateTestTones(false); // Update the Test Tone, false=Don't Reset Tone Sequence.
updateOnAirSign(); // Update the Optional "On Air" 12V LED Sign.
wifiReconnect(); // Reconnect to WiFi if not connected to router.
rebootSystem(); // Check to see if Reboot has been requested.
#ifdef MQTT_ENB
mqttReconnect(false);
processMQTT();
#endif // ifdef MQTT_ENB
#ifdef HTTP_ENB
processWebClient(); // Process Any Available HTTP RDS commands.
#endif // ifdef HTTP_ENB
#ifdef OTA_ENB
ArduinoOTA.handle(); // OTA Service.
#endif // ifdef OTA_ENB
}
// *********************************************************************************************
/*
// Hardware interrupts are not used in this project. This isr is a placeholder for future use.
// Warning: It is not possible to execute delay() or yield() from an ISR, or do blocking operations,
// or operations that disable the interrupts. Code MUST be short & sweet, such as set a flag then exit.
// Function protype must use ICACHE_RAM_ATTR type.
IRAM_ATTR void isr() // interrupt service routine
{
}
*/
// EOF