From ec368d618d672832eab5f2446a3f2701f2db4612 Mon Sep 17 00:00:00 2001 From: giddyhup Date: Sun, 21 Jun 2020 11:51:42 +0200 Subject: [PATCH 1/5] Added TM1637 support and more MQTT features --- Esp32_radio.ino | 125 +++++++++++++++++++++++++----------- Esp32_radio/Esp32_radio.ino | 125 +++++++++++++++++++++++++----------- Esp32_radio/TM1637.h | 99 ++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+), 74 deletions(-) create mode 100644 Esp32_radio/TM1637.h diff --git a/Esp32_radio.ino b/Esp32_radio.ino index 385b07e..298e7cc 100644 --- a/Esp32_radio.ino +++ b/Esp32_radio.ino @@ -162,9 +162,10 @@ #define TFTFILE "/Arduino/ESP32-Radio.tft" // Binary file name for update NEXTION image // // Define (just one) type of display. See documentation. -#define BLUETFT // Works also for RED TFT 128x160 +// #define BLUETFT // Works also for RED TFT 128x160 //#define OLED // 64x128 I2C OLED -//#define DUMMYTFT // Dummy display +// #define DUMMYTFT // Dummy display +#define TM1637 // 7 Segment Clock Element //#define LCD1602I2C // LCD 1602 display with I2C backpack //#define ILI9341 // ILI9341 240*320 //#define NEXTION // Nextion display. Uses UART 2 (pin 16 and 17) @@ -342,8 +343,9 @@ struct keyname_t // For keys in NVS //************************************************************************************************** enum display_t { T_UNDEFINED, T_BLUETFT, T_OLED, // Various types of display - T_DUMMYTFT, T_LCD1602I2C, T_ILI9341, - T_NEXTION } ; + T_DUMMYTFT, T_TM1637, T_LCD1602I2C, T_ILI9341, + T_NEXTION + } ; enum datamode_t { INIT = 1, HEADER = 2, DATA = 4, // State for datastream METADATA = 8, PLAYLISTINIT = 16, @@ -369,7 +371,7 @@ SemaphoreHandle_t SPIsem = NULL ; // For exclusive SPI us hw_timer_t* timer = NULL ; // For timer char timetxt[9] ; // Converted timeinfo char cmd[130] ; // Command from MQTT or Serial -uint8_t tmpbuff[6000] ; // Input buffer for mp3 or data stream +uint8_t tmpbuff[6000] ; // Input buffer for mp3 or data stream QueueHandle_t dataqueue ; // Queue for mp3 datastream QueueHandle_t spfqueue ; // Queue for special functions qdata_struct outchunk ; // Data to queue @@ -445,7 +447,9 @@ sv bool tripleclick = false ; // True if triple click sv bool longclick = false ; // True if longclick detected enum enc_menu_t { VOLUME, PRESET, TRACK } ; // State for rotary encoder menu enc_menu_t enc_menu_mode = VOLUME ; // Default is VOLUME mode - +String clockdotstr = "" ; // State of the segment clock dots +String clockbrightnesstr = "" ; // State of the segment clock brightness +String overallstate = "" ; // Overall state (to report startup etc.) // struct progpin_struct // For programmable input pins { @@ -453,7 +457,7 @@ struct progpin_struct // For programmable inp bool reserved ; // Reserved for connected devices bool avail ; // Pin is available for a command String command ; // Command to execute when activated - // Example: "uppreset=1" + // Example: "uppreset=1" bool cur ; // Current state, true = HIGH, false = LOW } ; @@ -550,7 +554,8 @@ touchpin_struct touchpin[] = // Touch pins and progr //************************************************************************************************** // ID's for the items to publish to MQTT. Is index in amqttpub[] enum { MQTT_IP, MQTT_ICYNAME, MQTT_STREAMTITLE, MQTT_NOWPLAYING, - MQTT_PRESET, MQTT_VOLUME, MQTT_PLAYING, MQTT_PLAYLISTPOS + MQTT_PRESET, MQTT_VOLUME, MQTT_PLAYING, MQTT_PLAYLISTPOS, + MQTT_CLOCKDOTS, MQTT_CLOCKBRIGHTNESS, MQTT_STATE } ; enum { MQSTRING, MQINT8, MQINT16 } ; // Type of variable to publish @@ -566,17 +571,20 @@ class mqttpubc // For MQTT publishing // Publication topics for MQTT. The topic will be pefixed by "PREFIX/", where PREFIX is replaced // by the the mqttprefix in the preferences. protected: - mqttpub_struct amqttpub[9] = // Definitions of various MQTT topic to publish + mqttpub_struct amqttpub[12] = // Definitions of various MQTT topic to publish { // Index is equal to enum above - { "ip", MQSTRING, &ipaddress, false }, // Definition for MQTT_IP - { "icy/name", MQSTRING, &icyname, false }, // Definition for MQTT_ICYNAME - { "icy/streamtitle", MQSTRING, &icystreamtitle, false }, // Definition for MQTT_STREAMTITLE - { "nowplaying", MQSTRING, &ipaddress, false }, // Definition for MQTT_NOWPLAYING - { "preset" , MQINT8, ¤tpreset, false }, // Definition for MQTT_PRESET - { "volume" , MQINT8, &ini_block.reqvol, false }, // Definition for MQTT_VOLUME - { "playing", MQINT8, &playingstat, false }, // Definition for MQTT_PLAYING - { "playlist/pos", MQINT16, &playlist_num, false }, // Definition for MQTT_PLAYLISTPOS - { NULL, 0, NULL, false } // End of definitions + { "ip", MQSTRING, &ipaddress, false }, // Definition for MQTT_IP + { "icy/name", MQSTRING, &icyname, false }, // Definition for MQTT_ICYNAME + { "icy/streamtitle", MQSTRING, &icystreamtitle, false }, // Definition for MQTT_STREAMTITLE + { "nowplaying", MQSTRING, &ipaddress, false }, // Definition for MQTT_NOWPLAYING + { "preset", MQINT8, ¤tpreset, false }, // Definition for MQTT_PRESET + { "volume", MQINT8, &ini_block.reqvol, false }, // Definition for MQTT_VOLUME + { "playing", MQINT8, &playingstat, false }, // Definition for MQTT_PLAYING + { "playlist/pos", MQINT16, &playlist_num, false }, // Definition for MQTT_PLAYLISTPOS + { "clock/dots", MQSTRING, &clockdotstr, false }, // Definition for MQTT_CLOCKDOTS + { "clock/brightness", MQINT8, &clockbrightnesstr, false }, // Definition for MQTT_CLOCKBRIGHTNESS + { "state", MQSTRING, &overallstate, false }, // Definition for MQTT_STATE + { NULL, 0, NULL, false } // End of definitions } ; public: void trigger ( uint8_t item ) ; // Trigger publishig for one item @@ -865,8 +873,9 @@ bool VS1053::testComm ( const char *header ) uint16_t r1, r2, cnt = 0 ; uint16_t delta = 300 ; // 3 for fast SPI const uint16_t vstype[] = { 1001, 1011, 1002, 1003, // Possible chip versions - 1053, 1033, 0000, 1103 } ; - + 1053, 1033, 0000, 1103 + } ; + dbgprint ( header ) ; // Show a header if ( !digitalRead ( dreq_pin ) ) { @@ -1063,7 +1072,7 @@ void VS1053::output_enable ( bool ena ) // Enable amplifier throu } -void VS1053::AdjustRate ( long ppm2 ) // Fine tune the data rate +void VS1053::AdjustRate ( long ppm2 ) // Fine tune the data rate { write_register ( SCI_WRAMADDR, 0x1e07 ) ; write_register ( SCI_WRAM, ppm2 ) ; @@ -1099,6 +1108,9 @@ VS1053* vs1053player ; #ifdef DUMMYTFT #include "Dummytft.h" // For Dummy display #endif +#ifdef TM1637 +#include "TM1637.h" // For TM1637 display +#endif #ifdef NEXTION #include "NEXTION.h" // For NEXTION display #endif @@ -2177,10 +2189,10 @@ bool connecttohost() { auth = nvsgetstr ( "basicauth" ) ; // Use basic authentication? if ( auth != "" ) // Should be user:passwd - { - auth = base64::encode ( auth.c_str() ) ; // Encode - auth = String ( "Authorization: Basic " ) + - auth + String ( "\r\n" ) ; + { + auth = base64::encode ( auth.c_str() ) ; // Encode + auth = String ( "Authorization: Basic " ) + + auth + String ( "\r\n" ) ; } } mp3client.print ( String ( "GET " ) + @@ -2430,7 +2442,7 @@ bool connectwifi() } tftlog ( pfs ) ; // Show IP delay ( 3000 ) ; // Allow user to read this - tftlog ( "\f" ) ; // Select new page if NEXTION + tftlog ( "\f" ) ; // Select new page if NEXTION return ( localAP == false ) ; // Return result of connection } @@ -2489,7 +2501,7 @@ bool do_nextion_update ( uint32_t clength ) } k = otaclient.read ( tmpbuff, k ) ; // Read a number of bytes from the stream dbgprint ( "TFT file, read %d bytes", k ) ; - nxtserial->write ( tmpbuff, k ) ; + nxtserial->write ( tmpbuff, k ) ; while ( !nxtserial->available() ) // Any input seen? { delay ( 20 ) ; @@ -2520,7 +2532,7 @@ bool do_nextion_update ( uint32_t clength ) bool do_software_update ( uint32_t clength ) { bool res = false ; // Update result - + if ( Update.begin ( clength ) ) // Update possible? { dbgprint ( "Begin OTA update, length is %d", @@ -2572,13 +2584,13 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char String line ; // Input header line String lstmod = "" ; // Last modified timestamp in NVS String newlstmod ; // Last modified from host - + updatereq = false ; // Clear update flag otastart() ; // Show something on screen stop_mp3client () ; // Stop input stream lstmod = nvsgetstr ( lstmodkey ) ; // Get current last modified timestamp dbgprint ( "Connecting to %s for %s", - updatehost, binfile ) ; + updatehost, binfile ) ; if ( !otaclient.connect ( updatehost, 80 ) ) // Connect to host { dbgprint ( "Connect to updatehost failed!" ) ; @@ -2610,7 +2622,7 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char break ; // Yes, get the OTA started } // Check if the HTTP Response is 200. Any other response is an error. - if ( line.startsWith ( "HTTP/1.1" ) ) // + if ( line.startsWith ( "HTTP/1.1" ) ) // { if ( line.indexOf ( " 200 " ) < 0 ) { @@ -2629,7 +2641,7 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char { dbgprint ( "No new version available" ) ; // No, show reason otaclient.flush() ; - return ; + return ; } if ( clength > 0 ) { @@ -2908,7 +2920,7 @@ String readprefs ( bool output ) String ( "/*******" ) ; } cmd = String ( "" ) ; // Do not analyze this - + } else if ( strstr ( key, "mqttpasswd" ) ) // Is it a MQTT password? { @@ -3100,7 +3112,7 @@ void scanserial2() dbgprint ( "NEXTION command seen %02X %s", cmd[0], cmd + 1 ) ; if ( cmd[0] == 0x70 ) // Button pressed? - { + { reply = analyzeCmd ( cmd + 1 ) ; // Analyze command and handle it dbgprint ( reply ) ; // Result for debugging } @@ -3538,6 +3550,10 @@ void setup() #if defined ( DUMMYTFT ) dbgprint ( dtyp, "DUMMYTFT" ) ; #endif +#if defined ( TM1637 ) + dbgprint ( dtyp, "TM1637" ) ; + display.setBrightness(TMBRIGHT); // Set clock brightness +#endif #if defined ( LCD1602I2C ) dbgprint ( dtyp, "LCD1602" ) ; #endif @@ -3660,7 +3676,7 @@ void setup() } } mk_lsan() ; // Make a list of acceptable networks - // in preferences. + // in preferences. WiFi.mode ( WIFI_STA ) ; // This ESP is a station WiFi.persistent ( false ) ; // Do not save SSID and password WiFi.disconnect() ; // After restart router could still @@ -3768,6 +3784,9 @@ void setup() NULL, // parameter of the task 1, // priority of the task &xspftask ) ; // Task handle to keep track of created task + +overallstate = "ESP32 Radio is up." ; +mqttpub.trigger ( MQTT_STATE ); } @@ -4631,7 +4650,7 @@ void loop() if ( updatereq ) // Software update requested? { if ( displaytype == T_NEXTION ) // NEXTION in use? - { + { update_software ( "lstmodn", // Yes, update NEXTION image from remote image UPDATEHOST, TFTFILE ) ; } @@ -5333,6 +5352,29 @@ const char* analyzeCmd ( const char* par, const char* val ) { muteflag = !muteflag ; // Request volume to zero/normal } + else if ( argument.indexOf ( "brightness" ) >= 0 ) // TM1637 brightness + { // set segment-display brightness + TMBRIGHT = ivalue; + display.setBrightness(ivalue); + sprintf ( reply, "Brightness is now %d", // Reply new brightness + ivalue ) ; + clockbrightnesstr = ivalue ; // MQTT payload + mqttpub.trigger ( MQTT_CLOCKBRIGHTNESS ) ; + } + else if ( argument.indexOf ( "dotson" ) >= 0 ) // TM1637 dots on + { + TMDOT = true; + clockdotstr = "on"; + mqttpub.trigger ( MQTT_CLOCKDOTS ) ; + } + else if ( argument.indexOf ( "dotsoff" ) >= 0 ) // TM1637 dots off + { + TMDOT = false; + clockdotstr = "off"; + mqttpub.trigger ( MQTT_CLOCKDOTS ) ; + + } + else if ( argument.indexOf ( "ir_" ) >= 0 ) // Ir setting? { // Do not handle here } @@ -5397,6 +5439,14 @@ const char* analyzeCmd ( const char* par, const char* val ) hostreq = true ; // Request UNSTOP } } + else if ( argument == "realresume" ) // (un)Stop requested? + { + hostreq = true ; // Request UNSTOP + } + else if ( argument == "realstop" ) // (un)Stop requested? + { + datamode = STOPREQD ; // Request STOP + } else if ( ( value.length() > 0 ) && ( ( argument == "mp3track" ) || // Select a track from SD card? ( argument == "station" ) ) ) // Station in the form address:port @@ -5625,10 +5675,11 @@ void displayinfo ( uint16_t inx ) //************************************************************************************************** void gettime() { + static int16_t delaycount = 0 ; // To reduce number of NTP requests static int16_t retrycount = 100 ; - if ( tft ) // TFT used? + if ( tft || TMSTATE ) // TFT or TM1637 used? { if ( timeinfo.tm_year ) // Legal time found? { diff --git a/Esp32_radio/Esp32_radio.ino b/Esp32_radio/Esp32_radio.ino index 385b07e..298e7cc 100644 --- a/Esp32_radio/Esp32_radio.ino +++ b/Esp32_radio/Esp32_radio.ino @@ -162,9 +162,10 @@ #define TFTFILE "/Arduino/ESP32-Radio.tft" // Binary file name for update NEXTION image // // Define (just one) type of display. See documentation. -#define BLUETFT // Works also for RED TFT 128x160 +// #define BLUETFT // Works also for RED TFT 128x160 //#define OLED // 64x128 I2C OLED -//#define DUMMYTFT // Dummy display +// #define DUMMYTFT // Dummy display +#define TM1637 // 7 Segment Clock Element //#define LCD1602I2C // LCD 1602 display with I2C backpack //#define ILI9341 // ILI9341 240*320 //#define NEXTION // Nextion display. Uses UART 2 (pin 16 and 17) @@ -342,8 +343,9 @@ struct keyname_t // For keys in NVS //************************************************************************************************** enum display_t { T_UNDEFINED, T_BLUETFT, T_OLED, // Various types of display - T_DUMMYTFT, T_LCD1602I2C, T_ILI9341, - T_NEXTION } ; + T_DUMMYTFT, T_TM1637, T_LCD1602I2C, T_ILI9341, + T_NEXTION + } ; enum datamode_t { INIT = 1, HEADER = 2, DATA = 4, // State for datastream METADATA = 8, PLAYLISTINIT = 16, @@ -369,7 +371,7 @@ SemaphoreHandle_t SPIsem = NULL ; // For exclusive SPI us hw_timer_t* timer = NULL ; // For timer char timetxt[9] ; // Converted timeinfo char cmd[130] ; // Command from MQTT or Serial -uint8_t tmpbuff[6000] ; // Input buffer for mp3 or data stream +uint8_t tmpbuff[6000] ; // Input buffer for mp3 or data stream QueueHandle_t dataqueue ; // Queue for mp3 datastream QueueHandle_t spfqueue ; // Queue for special functions qdata_struct outchunk ; // Data to queue @@ -445,7 +447,9 @@ sv bool tripleclick = false ; // True if triple click sv bool longclick = false ; // True if longclick detected enum enc_menu_t { VOLUME, PRESET, TRACK } ; // State for rotary encoder menu enc_menu_t enc_menu_mode = VOLUME ; // Default is VOLUME mode - +String clockdotstr = "" ; // State of the segment clock dots +String clockbrightnesstr = "" ; // State of the segment clock brightness +String overallstate = "" ; // Overall state (to report startup etc.) // struct progpin_struct // For programmable input pins { @@ -453,7 +457,7 @@ struct progpin_struct // For programmable inp bool reserved ; // Reserved for connected devices bool avail ; // Pin is available for a command String command ; // Command to execute when activated - // Example: "uppreset=1" + // Example: "uppreset=1" bool cur ; // Current state, true = HIGH, false = LOW } ; @@ -550,7 +554,8 @@ touchpin_struct touchpin[] = // Touch pins and progr //************************************************************************************************** // ID's for the items to publish to MQTT. Is index in amqttpub[] enum { MQTT_IP, MQTT_ICYNAME, MQTT_STREAMTITLE, MQTT_NOWPLAYING, - MQTT_PRESET, MQTT_VOLUME, MQTT_PLAYING, MQTT_PLAYLISTPOS + MQTT_PRESET, MQTT_VOLUME, MQTT_PLAYING, MQTT_PLAYLISTPOS, + MQTT_CLOCKDOTS, MQTT_CLOCKBRIGHTNESS, MQTT_STATE } ; enum { MQSTRING, MQINT8, MQINT16 } ; // Type of variable to publish @@ -566,17 +571,20 @@ class mqttpubc // For MQTT publishing // Publication topics for MQTT. The topic will be pefixed by "PREFIX/", where PREFIX is replaced // by the the mqttprefix in the preferences. protected: - mqttpub_struct amqttpub[9] = // Definitions of various MQTT topic to publish + mqttpub_struct amqttpub[12] = // Definitions of various MQTT topic to publish { // Index is equal to enum above - { "ip", MQSTRING, &ipaddress, false }, // Definition for MQTT_IP - { "icy/name", MQSTRING, &icyname, false }, // Definition for MQTT_ICYNAME - { "icy/streamtitle", MQSTRING, &icystreamtitle, false }, // Definition for MQTT_STREAMTITLE - { "nowplaying", MQSTRING, &ipaddress, false }, // Definition for MQTT_NOWPLAYING - { "preset" , MQINT8, ¤tpreset, false }, // Definition for MQTT_PRESET - { "volume" , MQINT8, &ini_block.reqvol, false }, // Definition for MQTT_VOLUME - { "playing", MQINT8, &playingstat, false }, // Definition for MQTT_PLAYING - { "playlist/pos", MQINT16, &playlist_num, false }, // Definition for MQTT_PLAYLISTPOS - { NULL, 0, NULL, false } // End of definitions + { "ip", MQSTRING, &ipaddress, false }, // Definition for MQTT_IP + { "icy/name", MQSTRING, &icyname, false }, // Definition for MQTT_ICYNAME + { "icy/streamtitle", MQSTRING, &icystreamtitle, false }, // Definition for MQTT_STREAMTITLE + { "nowplaying", MQSTRING, &ipaddress, false }, // Definition for MQTT_NOWPLAYING + { "preset", MQINT8, ¤tpreset, false }, // Definition for MQTT_PRESET + { "volume", MQINT8, &ini_block.reqvol, false }, // Definition for MQTT_VOLUME + { "playing", MQINT8, &playingstat, false }, // Definition for MQTT_PLAYING + { "playlist/pos", MQINT16, &playlist_num, false }, // Definition for MQTT_PLAYLISTPOS + { "clock/dots", MQSTRING, &clockdotstr, false }, // Definition for MQTT_CLOCKDOTS + { "clock/brightness", MQINT8, &clockbrightnesstr, false }, // Definition for MQTT_CLOCKBRIGHTNESS + { "state", MQSTRING, &overallstate, false }, // Definition for MQTT_STATE + { NULL, 0, NULL, false } // End of definitions } ; public: void trigger ( uint8_t item ) ; // Trigger publishig for one item @@ -865,8 +873,9 @@ bool VS1053::testComm ( const char *header ) uint16_t r1, r2, cnt = 0 ; uint16_t delta = 300 ; // 3 for fast SPI const uint16_t vstype[] = { 1001, 1011, 1002, 1003, // Possible chip versions - 1053, 1033, 0000, 1103 } ; - + 1053, 1033, 0000, 1103 + } ; + dbgprint ( header ) ; // Show a header if ( !digitalRead ( dreq_pin ) ) { @@ -1063,7 +1072,7 @@ void VS1053::output_enable ( bool ena ) // Enable amplifier throu } -void VS1053::AdjustRate ( long ppm2 ) // Fine tune the data rate +void VS1053::AdjustRate ( long ppm2 ) // Fine tune the data rate { write_register ( SCI_WRAMADDR, 0x1e07 ) ; write_register ( SCI_WRAM, ppm2 ) ; @@ -1099,6 +1108,9 @@ VS1053* vs1053player ; #ifdef DUMMYTFT #include "Dummytft.h" // For Dummy display #endif +#ifdef TM1637 +#include "TM1637.h" // For TM1637 display +#endif #ifdef NEXTION #include "NEXTION.h" // For NEXTION display #endif @@ -2177,10 +2189,10 @@ bool connecttohost() { auth = nvsgetstr ( "basicauth" ) ; // Use basic authentication? if ( auth != "" ) // Should be user:passwd - { - auth = base64::encode ( auth.c_str() ) ; // Encode - auth = String ( "Authorization: Basic " ) + - auth + String ( "\r\n" ) ; + { + auth = base64::encode ( auth.c_str() ) ; // Encode + auth = String ( "Authorization: Basic " ) + + auth + String ( "\r\n" ) ; } } mp3client.print ( String ( "GET " ) + @@ -2430,7 +2442,7 @@ bool connectwifi() } tftlog ( pfs ) ; // Show IP delay ( 3000 ) ; // Allow user to read this - tftlog ( "\f" ) ; // Select new page if NEXTION + tftlog ( "\f" ) ; // Select new page if NEXTION return ( localAP == false ) ; // Return result of connection } @@ -2489,7 +2501,7 @@ bool do_nextion_update ( uint32_t clength ) } k = otaclient.read ( tmpbuff, k ) ; // Read a number of bytes from the stream dbgprint ( "TFT file, read %d bytes", k ) ; - nxtserial->write ( tmpbuff, k ) ; + nxtserial->write ( tmpbuff, k ) ; while ( !nxtserial->available() ) // Any input seen? { delay ( 20 ) ; @@ -2520,7 +2532,7 @@ bool do_nextion_update ( uint32_t clength ) bool do_software_update ( uint32_t clength ) { bool res = false ; // Update result - + if ( Update.begin ( clength ) ) // Update possible? { dbgprint ( "Begin OTA update, length is %d", @@ -2572,13 +2584,13 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char String line ; // Input header line String lstmod = "" ; // Last modified timestamp in NVS String newlstmod ; // Last modified from host - + updatereq = false ; // Clear update flag otastart() ; // Show something on screen stop_mp3client () ; // Stop input stream lstmod = nvsgetstr ( lstmodkey ) ; // Get current last modified timestamp dbgprint ( "Connecting to %s for %s", - updatehost, binfile ) ; + updatehost, binfile ) ; if ( !otaclient.connect ( updatehost, 80 ) ) // Connect to host { dbgprint ( "Connect to updatehost failed!" ) ; @@ -2610,7 +2622,7 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char break ; // Yes, get the OTA started } // Check if the HTTP Response is 200. Any other response is an error. - if ( line.startsWith ( "HTTP/1.1" ) ) // + if ( line.startsWith ( "HTTP/1.1" ) ) // { if ( line.indexOf ( " 200 " ) < 0 ) { @@ -2629,7 +2641,7 @@ void update_software ( const char* lstmodkey, const char* updatehost, const char { dbgprint ( "No new version available" ) ; // No, show reason otaclient.flush() ; - return ; + return ; } if ( clength > 0 ) { @@ -2908,7 +2920,7 @@ String readprefs ( bool output ) String ( "/*******" ) ; } cmd = String ( "" ) ; // Do not analyze this - + } else if ( strstr ( key, "mqttpasswd" ) ) // Is it a MQTT password? { @@ -3100,7 +3112,7 @@ void scanserial2() dbgprint ( "NEXTION command seen %02X %s", cmd[0], cmd + 1 ) ; if ( cmd[0] == 0x70 ) // Button pressed? - { + { reply = analyzeCmd ( cmd + 1 ) ; // Analyze command and handle it dbgprint ( reply ) ; // Result for debugging } @@ -3538,6 +3550,10 @@ void setup() #if defined ( DUMMYTFT ) dbgprint ( dtyp, "DUMMYTFT" ) ; #endif +#if defined ( TM1637 ) + dbgprint ( dtyp, "TM1637" ) ; + display.setBrightness(TMBRIGHT); // Set clock brightness +#endif #if defined ( LCD1602I2C ) dbgprint ( dtyp, "LCD1602" ) ; #endif @@ -3660,7 +3676,7 @@ void setup() } } mk_lsan() ; // Make a list of acceptable networks - // in preferences. + // in preferences. WiFi.mode ( WIFI_STA ) ; // This ESP is a station WiFi.persistent ( false ) ; // Do not save SSID and password WiFi.disconnect() ; // After restart router could still @@ -3768,6 +3784,9 @@ void setup() NULL, // parameter of the task 1, // priority of the task &xspftask ) ; // Task handle to keep track of created task + +overallstate = "ESP32 Radio is up." ; +mqttpub.trigger ( MQTT_STATE ); } @@ -4631,7 +4650,7 @@ void loop() if ( updatereq ) // Software update requested? { if ( displaytype == T_NEXTION ) // NEXTION in use? - { + { update_software ( "lstmodn", // Yes, update NEXTION image from remote image UPDATEHOST, TFTFILE ) ; } @@ -5333,6 +5352,29 @@ const char* analyzeCmd ( const char* par, const char* val ) { muteflag = !muteflag ; // Request volume to zero/normal } + else if ( argument.indexOf ( "brightness" ) >= 0 ) // TM1637 brightness + { // set segment-display brightness + TMBRIGHT = ivalue; + display.setBrightness(ivalue); + sprintf ( reply, "Brightness is now %d", // Reply new brightness + ivalue ) ; + clockbrightnesstr = ivalue ; // MQTT payload + mqttpub.trigger ( MQTT_CLOCKBRIGHTNESS ) ; + } + else if ( argument.indexOf ( "dotson" ) >= 0 ) // TM1637 dots on + { + TMDOT = true; + clockdotstr = "on"; + mqttpub.trigger ( MQTT_CLOCKDOTS ) ; + } + else if ( argument.indexOf ( "dotsoff" ) >= 0 ) // TM1637 dots off + { + TMDOT = false; + clockdotstr = "off"; + mqttpub.trigger ( MQTT_CLOCKDOTS ) ; + + } + else if ( argument.indexOf ( "ir_" ) >= 0 ) // Ir setting? { // Do not handle here } @@ -5397,6 +5439,14 @@ const char* analyzeCmd ( const char* par, const char* val ) hostreq = true ; // Request UNSTOP } } + else if ( argument == "realresume" ) // (un)Stop requested? + { + hostreq = true ; // Request UNSTOP + } + else if ( argument == "realstop" ) // (un)Stop requested? + { + datamode = STOPREQD ; // Request STOP + } else if ( ( value.length() > 0 ) && ( ( argument == "mp3track" ) || // Select a track from SD card? ( argument == "station" ) ) ) // Station in the form address:port @@ -5625,10 +5675,11 @@ void displayinfo ( uint16_t inx ) //************************************************************************************************** void gettime() { + static int16_t delaycount = 0 ; // To reduce number of NTP requests static int16_t retrycount = 100 ; - if ( tft ) // TFT used? + if ( tft || TMSTATE ) // TFT or TM1637 used? { if ( timeinfo.tm_year ) // Legal time found? { diff --git a/Esp32_radio/TM1637.h b/Esp32_radio/TM1637.h new file mode 100644 index 0000000..224f81f --- /dev/null +++ b/Esp32_radio/TM1637.h @@ -0,0 +1,99 @@ +// TM1637.h +// Includes and variables for 7-segment display TM1637 + +#include +// https://github.com/avishorp/TM1637 + +// Module connection pins (Digital Pins) +#define TMCLK 25 +#define TMDIO 26 + +TM1637Display display(TMCLK, TMDIO); +uint8_t dispdata[4]; // array for TM1637 +int TMBRIGHT = 4; // default medium brightness (possible values: 0-7) +bool TMSTATE = true; // time will only be updated if this is set to true +bool TMCLOCK = true; // module could be active but we could use commands to update the display (not implemented) +bool TMDOT = false; // central colon is initially off, can be updated via command + +// Color definitions for the TFT screen (if used) +#define BLACK 0 +#define BLUE 1 +#define RED 1 +#define GREEN 1 +#define CYAN GREEN | BLUE +#define MAGENTA RED | BLUE +#define YELLOW RED | GREEN +#define WHITE RED | GREEN | BLUE + +// Various macro's to mimic the ST7735 version of display functions +#define dsp_setRotation() // Use standard landscape format +#define dsp_print(a) // Print a string +#define dsp_println(a) // Print string plus newline +#define dsp_setTextSize(a) // Set the text size +#define dsp_setTextColor(a) +#define dsp_setCursor(a,b) // Position the cursor +#define dsp_erase() // Clear the screen +#define dsp_getwidth() 0 // Get width of screen +#define dsp_getheight() 0 // Get height of screen +#define dsp_update() // Updates to the physical screen +#define dsp_usesSPI() false // Does not use SPI + +void* tft = NULL ; + +bool dsp_begin() +{ + return false ; +} + +void dsp_fillRect ( int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color ) +{} ; // Fill a rectange + + +#define TFTSECS 1 +scrseg_struct tftdata[TFTSECS] = // Screen divided in 3 segments + 1 overlay +{ // One text line is 8 pixels + { false, WHITE, 0, 8, "" }, // 1 top line +} ; + +//************************************************************************************************** +// D I S P L A Y B A T T E R Y * +//************************************************************************************************** +// Dummy routine for this type of display. * +//************************************************************************************************** +void displaybattery() +{ +} + + +//************************************************************************************************** +// D I S P L A Y V O L U M E * +//************************************************************************************************** +// Dummy routine for this type of display. * +//************************************************************************************************** +void displayvolume() +{ +} + + +//************************************************************************************************** +// D I S P L A Y T I M E * +//************************************************************************************************** +// Dummy routine for this type of display. * +//************************************************************************************************** +void displaytime ( const char* str, uint16_t color ) +{ + if ( TMCLOCK ) + { + dispdata[0] = display.encodeDigit(str[0]); + if ( TMDOT ) + { + dispdata[1] = display.encodeDigit(str[1]) | B10000000; + } else { + dispdata[1] = display.encodeDigit(str[1]); + } + + dispdata[2] = display.encodeDigit(str[3]); + dispdata[3] = display.encodeDigit(str[4]); + display.setSegments(dispdata); + } +} From 24d53cd1e1c910311e7035af1b7898feb56833d4 Mon Sep 17 00:00:00 2001 From: giddyhup Date: Tue, 7 Jul 2020 11:11:36 +0200 Subject: [PATCH 2/5] Comment new commands for TM1367 --- Esp32_radio/Esp32_radio.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Esp32_radio/Esp32_radio.ino b/Esp32_radio/Esp32_radio.ino index 298e7cc..b4c8a79 100644 --- a/Esp32_radio/Esp32_radio.ino +++ b/Esp32_radio/Esp32_radio.ino @@ -5273,6 +5273,11 @@ const char* analyzeCmd ( const char* str ) // reset // Restart the ESP32 * // bat0 = 2318 // ADC value for an empty battery * // bat100 = 2916 // ADC value for a fully charged battery * +// brightness = <0..7> // Brighnetss of the TM1637 digits * +// dotson // Turns the TM1637 colon on * +// dotsoff // Turns the TM1637 colon off * +// realstop // Stops playback (not a toggle) * +// realresume // Resumes playback (not a toggle) * // Commands marked with "*)" are sensible during initialization only * //************************************************************************************************** const char* analyzeCmd ( const char* par, const char* val ) From d7cd242869879e5efed11ce69702bf00b11e3a81 Mon Sep 17 00:00:00 2001 From: giddyhup Date: Thu, 16 Jul 2020 14:28:00 +0200 Subject: [PATCH 3/5] Fix clock brightness reporting --- Esp32_radio/Esp32_radio.ino | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Esp32_radio/Esp32_radio.ino b/Esp32_radio/Esp32_radio.ino index b4c8a79..34ffdc7 100644 --- a/Esp32_radio/Esp32_radio.ino +++ b/Esp32_radio/Esp32_radio.ino @@ -450,6 +450,7 @@ enc_menu_t enc_menu_mode = VOLUME ; // Default is VOLUME mo String clockdotstr = "" ; // State of the segment clock dots String clockbrightnesstr = "" ; // State of the segment clock brightness String overallstate = "" ; // Overall state (to report startup etc.) +bool stoppedstart = true; // Do not start playback on start // struct progpin_struct // For programmable input pins { @@ -582,7 +583,7 @@ class mqttpubc // For MQTT publishing { "playing", MQINT8, &playingstat, false }, // Definition for MQTT_PLAYING { "playlist/pos", MQINT16, &playlist_num, false }, // Definition for MQTT_PLAYLISTPOS { "clock/dots", MQSTRING, &clockdotstr, false }, // Definition for MQTT_CLOCKDOTS - { "clock/brightness", MQINT8, &clockbrightnesstr, false }, // Definition for MQTT_CLOCKBRIGHTNESS + { "clock/brightness", MQSTRING, &clockbrightnesstr, false }, // Definition for MQTT_CLOCKBRIGHTNESS { "state", MQSTRING, &overallstate, false }, // Definition for MQTT_STATE { NULL, 0, NULL, false } // End of definitions } ; @@ -4456,7 +4457,7 @@ void chk_enc() //************************************************************************************************** // M P 3 L O O P * //************************************************************************************************** -// Called from the mail loop() for the mp3 functions. * +// Called from the main loop() for the mp3 functions. * // A connection to an MP3 server is active and we are ready to receive data. * // Normally there is about 2 to 4 kB available in the data stream. This depends on the sender. * //************************************************************************************************** @@ -4646,7 +4647,9 @@ void mp3loop() //************************************************************************************************** void loop() { - mp3loop() ; // Do mp3 related actions + if ( ! stoppedstart ) { // Only play if stoppedstart is false + mp3loop() ; // Do mp3 related actions + } if ( updatereq ) // Software update requested? { if ( displaytype == T_NEXTION ) // NEXTION in use? @@ -4661,6 +4664,9 @@ void loop() if ( resetreq ) // Reset requested? { delay ( 1000 ) ; // Yes, wait some time + if ( mqtt_on ) { + mqttclient.disconnect(); // Disconnect nicely from MQTT broker + } ESP.restart() ; // Reboot } scanserial() ; // Handle serial input @@ -4668,7 +4674,9 @@ void loop() scandigital() ; // Scan digital inputs scanIR() ; // See if IR input ArduinoOTA.handle() ; // Check for OTA - mp3loop() ; // Do more mp3 related actions + if ( ! stoppedstart ) { // Only play if stoppedstart is false + mp3loop() ; // Do mp3 related actions + } handlehttpreply() ; cmdclient = cmdserver.available() ; // Check Input from client? if ( cmdclient ) // Client connected? @@ -5363,7 +5371,7 @@ const char* analyzeCmd ( const char* par, const char* val ) display.setBrightness(ivalue); sprintf ( reply, "Brightness is now %d", // Reply new brightness ivalue ) ; - clockbrightnesstr = ivalue ; // MQTT payload + clockbrightnesstr = String( ivalue ); // MQTT payload mqttpub.trigger ( MQTT_CLOCKBRIGHTNESS ) ; } else if ( argument.indexOf ( "dotson" ) >= 0 ) // TM1637 dots on @@ -5438,18 +5446,23 @@ const char* analyzeCmd ( const char* par, const char* val ) { datamode = STOPREQD ; // Request STOP + stoppedstart = true; } else { hostreq = true ; // Request UNSTOP + stoppedstart = true; + } } else if ( argument == "realresume" ) // (un)Stop requested? { + stoppedstart = false; hostreq = true ; // Request UNSTOP } else if ( argument == "realstop" ) // (un)Stop requested? { + stoppedstart = true; datamode = STOPREQD ; // Request STOP } else if ( ( value.length() > 0 ) && From a1522e52bc1e33cf5155be7b175bcd55a05ec6af Mon Sep 17 00:00:00 2001 From: giddyhup Date: Sun, 26 Jul 2020 11:52:53 +0200 Subject: [PATCH 4/5] Now publishing recived IR codes --- Esp32_radio/Esp32_radio.ino | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Esp32_radio/Esp32_radio.ino b/Esp32_radio/Esp32_radio.ino index 34ffdc7..d4c7338 100644 --- a/Esp32_radio/Esp32_radio.ino +++ b/Esp32_radio/Esp32_radio.ino @@ -450,6 +450,7 @@ enc_menu_t enc_menu_mode = VOLUME ; // Default is VOLUME mo String clockdotstr = "" ; // State of the segment clock dots String clockbrightnesstr = "" ; // State of the segment clock brightness String overallstate = "" ; // Overall state (to report startup etc.) +String irstr; // IR value as string bool stoppedstart = true; // Do not start playback on start // struct progpin_struct // For programmable input pins @@ -556,7 +557,7 @@ touchpin_struct touchpin[] = // Touch pins and progr // ID's for the items to publish to MQTT. Is index in amqttpub[] enum { MQTT_IP, MQTT_ICYNAME, MQTT_STREAMTITLE, MQTT_NOWPLAYING, MQTT_PRESET, MQTT_VOLUME, MQTT_PLAYING, MQTT_PLAYLISTPOS, - MQTT_CLOCKDOTS, MQTT_CLOCKBRIGHTNESS, MQTT_STATE + MQTT_CLOCKDOTS, MQTT_CLOCKBRIGHTNESS, MQTT_STATE, MQTT_IRVALUE } ; enum { MQSTRING, MQINT8, MQINT16 } ; // Type of variable to publish @@ -572,7 +573,7 @@ class mqttpubc // For MQTT publishing // Publication topics for MQTT. The topic will be pefixed by "PREFIX/", where PREFIX is replaced // by the the mqttprefix in the preferences. protected: - mqttpub_struct amqttpub[12] = // Definitions of various MQTT topic to publish + mqttpub_struct amqttpub[13] = // Definitions of various MQTT topic to publish { // Index is equal to enum above { "ip", MQSTRING, &ipaddress, false }, // Definition for MQTT_IP { "icy/name", MQSTRING, &icyname, false }, // Definition for MQTT_ICYNAME @@ -585,6 +586,7 @@ class mqttpubc // For MQTT publishing { "clock/dots", MQSTRING, &clockdotstr, false }, // Definition for MQTT_CLOCKDOTS { "clock/brightness", MQSTRING, &clockbrightnesstr, false }, // Definition for MQTT_CLOCKBRIGHTNESS { "state", MQSTRING, &overallstate, false }, // Definition for MQTT_STATE + { "irvalue", MQSTRING, &irstr, false }, // Definition for MQTT_IRCODE { NULL, 0, NULL, false } // End of definitions } ; public: @@ -3223,6 +3225,9 @@ void scanIR() if ( ir_value ) // Any input? { sprintf ( mykey, "ir_%04X", ir_value ) ; // Form key in preferences + //sprintf ( irstr, "%04X", ir_value ) ; + irstr = String(ir_value, HEX); + mqttpub.trigger ( MQTT_IRVALUE ) ; // publish received IR value if ( nvssearch ( mykey ) ) { val = nvsgetstr ( mykey ) ; // Get the contents From 99c6071cfafc59712ce637c9cdb950642948b5ca Mon Sep 17 00:00:00 2001 From: giddyhup Date: Sun, 26 Jul 2020 12:58:24 +0200 Subject: [PATCH 5/5] Described enhancements in ReadMe. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7d71271..038f207 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ See the review by Andreas Spiess at https://www.youtube.com/watch?v=hz65vfvbXMs. Look for documentation and printdesign in the doc directory. +This fork adds support for a TM1673 7-segment module so that the whole kit can act as a radio clock. Additionally, MQTT control possibilities have been expanded and received IR codes are also being published so that they can be acted on within ones home automation environment. Different to the original, playback is paused on boot and switching sources does not automatically start playback (you wouldn't want your radio clock to start blasting music after a power outage). + +The radio clock does not include the actual alarm function. Playback gets triggered via a received command ('realresume'). The logic, a timer node in Node RED or a cron job somewhere else control the radio. The segment display's dots can be controlled separately to indicate states (e.g. that the alarm is set). + +States are reported via MQTT so that the logic can make use of it (e.g. states can be stored in global variables in Node RED). + +Additions are commented in the code. + Features: - Can connect to thousands of Internet radio stations that broadcast MP3 or Ogg audio streams. - Can connect to a standalone mp3 file on a server.