├── .gitattributes ├── .gitignore ├── README.md ├── YouTubeCounterDeepSleepV3 ├── ESP_Helpers.h └── YouTubeCounterDeepSleepV3.ino ├── YouTubeCounterV1 ├── IOTappStoryHelpers.h └── YouTubeCounterV1.ino ├── YoutubeCounterIOTappStoryV1.0 └── YoutubeCounterIOTappStoryV1.0.ino └── YoutubeCounterNeopixelV1.0 └── YoutubeCounterNeopixelV1.0.ino /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Youtube-Subscriber-Counter 2 | Youtube Subscriber Counter for ESP8266 3 | 4 | YoutubeCounterNeopixelV1.0 is for Neopixel display. Video: https://youtu.be/IIl5nDjfkjY 5 | 6 | YouTubeCounterV1 is IOTappStory compliant. 7 | ModeButton is D4 for Wemos boards. Change to D3 if you use NodeMCU boards 8 | Add your channel ID and your google API key 9 | 10 | 11 | -------------------------------------------------------------------------------- /YouTubeCounterDeepSleepV3/ESP_Helpers.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #define UPDATEINTERVAL 2 5 | #define RUNINTERVAL 2 6 | 7 | #define UPDATE_SERVER "192.168.0.200" 8 | #define UPDATE_URL "/iotappstore/iotappstorev20.php" 9 | 10 | #define SECOND_UPDATE_SERVER "iotappstore.org" 11 | #define SECOND_UPDATE_URL "/iotappstore/iotappstorev20.php" 12 | 13 | #define SSIDBASE 200 14 | #define PASSWORDBASE 220 15 | 16 | #define MAGICBYTE 85 17 | #define RTCMEMBEGIN 68 18 | 19 | char ssid[20]; 20 | char password[20]; 21 | 22 | String initSSID = mySSID; 23 | String initPassword = myPASSWORD; 24 | 25 | void handleTelnet() { 26 | if (TelnetServer.hasClient()) { 27 | // client is connected 28 | if (!Telnet || !Telnet.connected()) { 29 | if (Telnet) Telnet.stop(); // client disconnected 30 | Telnet = TelnetServer.available(); // ready for new client 31 | } else { 32 | TelnetServer.available().stop(); // have client, block new conections 33 | } 34 | } 35 | } 36 | 37 | void printRTCmem() { 38 | DEBUGPRINTLN1(""); 39 | DEBUGPRINTLN1("rtcMem "); 40 | DEBUGPRINT1("markerFlag "); 41 | DEBUGPRINTLN1(rtcMem.markerFlag); 42 | DEBUGPRINT1("runSpaces "); 43 | DEBUGPRINTLN1(rtcMem.runSpaces); 44 | DEBUGPRINT1("updateSpaces "); 45 | DEBUGPRINTLN1(rtcMem.updateSpaces); 46 | DEBUGPRINT1("lastSubscribers "); 47 | DEBUGPRINTLN1(rtcMem.lastSubscribers); 48 | } 49 | 50 | 51 | void readRTCmem() { 52 | system_rtc_mem_read(RTCMEMBEGIN, &rtcMem, sizeof(rtcMem)); 53 | if (rtcMem.markerFlag != MAGICBYTE || rtcMem.lastSubscribers < 0 ) { 54 | rtcMem.markerFlag = MAGICBYTE; 55 | rtcMem.lastSubscribers = 0; 56 | rtcMem.updateSpaces = 0; 57 | rtcMem.runSpaces = 0; 58 | system_rtc_mem_write(RTCMEMBEGIN, &rtcMem, sizeof(rtcMem)); 59 | } 60 | printRTCmem(); 61 | } 62 | 63 | void writeRTCmem() { 64 | rtcMem.markerFlag = MAGICBYTE; 65 | system_rtc_mem_write(RTCMEMBEGIN, &rtcMem, sizeof(rtcMem)); 66 | } 67 | 68 | bool readCredentials() { 69 | EEPROM.begin(512); 70 | if (EEPROM.read(SSIDBASE - 1) != 0x5) { 71 | Serial.println(EEPROM.read(SSIDBASE - 1), HEX); 72 | initSSID.toCharArray(ssid, initSSID.length() + 1); 73 | for (int ii = 0; ii <= initSSID.length(); ii++) EEPROM.write(SSIDBASE + ii, ssid[ii]); 74 | 75 | initPassword.toCharArray(password, initPassword.length() + 1); 76 | for (int ii = 0; ii <= initPassword.length(); ii++) EEPROM.write(PASSWORDBASE + ii, password[ii]); 77 | EEPROM.write(SSIDBASE - 1, 0x35); 78 | } 79 | int i = 0; 80 | do { 81 | ssid[i] = EEPROM.read(SSIDBASE + i); 82 | i++; 83 | } while (ssid[i - 1] > 0 && i < 20); 84 | 85 | if (i == 20) DEBUGPRINTLN1("ssid loaded"); 86 | i = 0; 87 | do { 88 | password[i] = EEPROM.read(PASSWORDBASE + i); 89 | i++; 90 | } while (password[i - 1] != 0 && i < 20); 91 | if (i == 20) DEBUGPRINTLN1("Pass loaded"); 92 | EEPROM.end(); 93 | } 94 | 95 | void printMacAddress() { 96 | byte mac[6]; 97 | WiFi.macAddress(mac); 98 | DEBUGPRINT1("MAC: "); 99 | for (int i = 0; i < 5; i++) { 100 | Serial.print(mac[i], HEX); 101 | Serial.print(":"); 102 | } 103 | Serial.println(mac[5], HEX); 104 | } 105 | 106 | 107 | bool iotUpdater(String server, String url, String firmware, bool immediately, bool debug) { 108 | bool retValue = true; 109 | 110 | DEBUGPRINTLN1(""); 111 | DEBUGPRINT1("updateSpaces "); 112 | DEBUGPRINTLN1(rtcMem.updateSpaces); 113 | 114 | if (rtcMem.updateSpaces >= UPDATEINTERVAL || immediately) { 115 | if (debug) { 116 | printMacAddress(); 117 | DEBUGPRINT1("IP = "); 118 | DEBUGPRINTLN1(WiFi.localIP()); 119 | DEBUGPRINT1("Update_server "); 120 | DEBUGPRINTLN1(server); 121 | DEBUGPRINT1("UPDATE_URL "); 122 | DEBUGPRINTLN1(url); 123 | DEBUGPRINT1("FIRMWARE_VERSION "); 124 | DEBUGPRINTLN1(firmware); 125 | DEBUGPRINTLN1("Updating..."); 126 | } 127 | t_httpUpdate_return ret = ESPhttpUpdate.update(server, 80, url, firmware); 128 | switch (ret) { 129 | case HTTP_UPDATE_FAILED: 130 | retValue = false; 131 | if (debug) Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 132 | DEBUGPRINTLN1(); 133 | break; 134 | 135 | case HTTP_UPDATE_NO_UPDATES: 136 | if (debug) DEBUGPRINTLN1("HTTP_UPDATE_NO_UPDATES"); 137 | break; 138 | 139 | case HTTP_UPDATE_OK: 140 | if (debug) DEBUGPRINTLN1("HTTP_UPDATE_OK"); 141 | break; 142 | } 143 | DEBUGPRINTLN1("Zero updateSpaces"); 144 | rtcMem.updateSpaces = 0; 145 | } else { 146 | rtcMem.updateSpaces = ++rtcMem.updateSpaces; // not yet an update 147 | } 148 | writeRTCmem(); 149 | return retValue; 150 | } 151 | 152 | 153 | -------------------------------------------------------------------------------- /YouTubeCounterDeepSleepV3/YouTubeCounterDeepSleepV3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define DEBUG 1 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #define FIRMWARE "SubscriberCounterV3" 14 | #define FIRMWARE_VERSION FIRMWARE"_000" 15 | 16 | 17 | extern "C" { 18 | #include "user_interface.h" // this is for the RTC memory read/write functions 19 | } 20 | #define BUZZER 5 21 | 22 | #define NUMBER_OF_DEVICES 5 23 | #define CS_PIN 2 24 | 25 | 26 | 27 | typedef struct { 28 | byte markerFlag; 29 | long lastSubscribers; 30 | int updateSpaces; 31 | int runSpaces; 32 | } rtcMemDef __attribute__((aligned(4))); 33 | rtcMemDef rtcMem; 34 | 35 | MAX7219_Dot_Matrix myDisplay (NUMBER_OF_DEVICES, CS_PIN); // 8 chips, and then specify the LOAD pin only 36 | 37 | // declare telnet server 38 | WiFiServer TelnetServer(23); 39 | WiFiClient Telnet; 40 | 41 | long subscribers, lastSubscribers; 42 | WiFiClient client; 43 | 44 | #include "ESP_Helpers.h" 45 | 46 | int getSubscribers() { 47 | const char* host = "api.thingspeak.com"; 48 | int subscribers = 0; 49 | 50 | // Establish connection to host 51 | Serial.printf("\n[Connecting to %s ... ", host); 52 | if (client.connect(host, 80)) 53 | { 54 | Serial.println("connected]"); 55 | 56 | // send GET request to host 57 | String url = "/apps/thinghttp/send_request?api_key=W7XYLLV4BOZHERBL"; 58 | DEBUGPRINTLN1("[Sending a request]"); 59 | client.print(String("GET ") + url + " HTTP/1.1\r\n" + 60 | "Host: " + host + "\r\n" + 61 | "Connection: close\r\n\r\n"); 62 | 63 | DEBUGPRINTLN1("[Response:]"); 64 | while (client.connected()) 65 | { 66 | if (client.available()) 67 | { 68 | String line = client.readStringUntil('\n'); 69 | int hi = line.indexOf("subscribers"); 70 | 71 | if (hi > 0) { 72 | String tt = line.substring(hi - 6, hi - 1); 73 | tt.replace(",", ""); 74 | subscribers = tt.toInt(); 75 | DEBUGPRINTLN1(subscribers); 76 | } 77 | } 78 | } 79 | client.stop(); 80 | DEBUGPRINTLN1("\n[Disconnected]"); 81 | } 82 | else 83 | { 84 | DEBUGPRINTLN1("connection failed!]"); 85 | client.stop(); 86 | } 87 | return subscribers; 88 | } 89 | 90 | 91 | void beep(int beepDuration) { 92 | digitalWrite(BUZZER, HIGH); 93 | delay(beepDuration); 94 | digitalWrite(BUZZER, LOW); 95 | delay(500); 96 | } 97 | 98 | void dispMatrix(String content) { 99 | char charBuf[50]; 100 | content.toCharArray(charBuf, 50); 101 | myDisplay.sendString(charBuf); 102 | } 103 | 104 | void display(long num) { 105 | int hi = analogRead(A0); 106 | float _intensity = 15 - (15.0 / (560.0 - 0) * hi); 107 | int intensity = (_intensity > 15.0) ? 15 : (int)_intensity; 108 | intensity = (_intensity < 2.0) ? 0 : (int)_intensity; 109 | // DEBUGPRINT1("Intensity "); 110 | // DEBUGPRINTLN1(intensity); 111 | // for (int i=0;i= RUNINTERVAL) { 138 | 139 | readCredentials(); 140 | WiFi.mode(WIFI_STA); 141 | WiFi.begin(mySSID, myPASSWORD); 142 | while (WiFi.status() != WL_CONNECTED) { 143 | delay(500); 144 | DEBUGPRINT1("."); 145 | if (wiTry >= 30) { 146 | ESP.reset(); 147 | } else { 148 | wiTry++; 149 | } 150 | } 151 | 152 | if (iotUpdater(UPDATE_SERVER, UPDATE_URL, FIRMWARE_VERSION, false, true) == 0) { 153 | iotUpdater(SECOND_UPDATE_SERVER, SECOND_UPDATE_URL, FIRMWARE_VERSION, true, true); 154 | } 155 | 156 | TelnetServer.begin(); 157 | TelnetServer.setNoDelay(true); 158 | 159 | subscribers = getSubscribers(); 160 | if (subscribers < rtcMem.lastSubscribers) subscribers = rtcMem.lastSubscribers; 161 | // subscribers = rtcMem.lastSubscribers + 1; 162 | 163 | DEBUGPRINT1("-----"); 164 | DEBUGPRINT1(rtcMem.lastSubscribers); 165 | DEBUGPRINT1(" "); 166 | DEBUGPRINTLN1(subscribers); 167 | if (subscribers > rtcMem.lastSubscribers) { 168 | beep(90); 169 | if (subscribers % 10 == 0) for (int ii = 0; ii < 1; ii++) beep(90); 170 | if (subscribers % 100 == 0) for (int ii = 0; ii < 3; ii++) beep(90); 171 | if (subscribers % 1000 == 0) for (int ii = 0; ii < 5; ii++) beep(90); 172 | if (subscribers == 10000) for (int ii = 0; ii < 100; ii++) beep(90); 173 | } 174 | display(subscribers); 175 | rtcMem.lastSubscribers = subscribers; 176 | rtcMem.runSpaces = 0; 177 | } else { 178 | rtcMem.runSpaces = ++rtcMem.runSpaces; 179 | writeRTCmem(); 180 | } 181 | writeRTCmem(); 182 | ESP.deepSleep(3000000); 183 | } 184 | 185 | void loop() { 186 | handleTelnet(); 187 | 188 | int hi = analogRead(A0); 189 | float _intensity = 15 - (15.0 / (560.0 - 20) * hi); 190 | int intensity = (_intensity > 15.0) ? 15 : (int)_intensity; 191 | intensity = (_intensity < 1.0) ? 1 : (int)_intensity; 192 | DEBUGPRINT1("A0: " ); 193 | DEBUGPRINT1(hi ); 194 | DEBUGPRINT1(" Intensity "); 195 | DEBUGPRINTLN1(intensity); 196 | 197 | Telnet.print("A0: " ); 198 | Telnet.print(hi ); 199 | Telnet.print(" Intensity "); 200 | Telnet.println(intensity); 201 | 202 | delay(100);; 203 | } 204 | -------------------------------------------------------------------------------- /YouTubeCounterV1/IOTappStoryHelpers.h: -------------------------------------------------------------------------------- 1 | void initialize() { // this function is called by IOTappstory() before return. Here, you put a safe startup configuration 2 | 3 | } 4 | 5 | 6 | void configESP() { 7 | Serial.begin(115200); 8 | for (int i = 0; i < 5; i++) DEBUG_PRINTLN(""); 9 | 10 | // ----------- PINS ---------------- 11 | #ifdef LEDgreen 12 | pinMode(LEDgreen, OUTPUT); 13 | digitalWrite(LEDgreen, LEDOFF); 14 | #endif 15 | #ifdef LEDred 16 | pinMode(LEDred, OUTPUT); 17 | digitalWrite(LEDred, LEDOFF); 18 | #endif 19 | 20 | 21 | // ------------- INTERRUPTS ---------------------------- 22 | blink.detach(); 23 | 24 | 25 | //------------- LED and DISPLAYS ------------------------ 26 | LEDswitch(GreenFastBlink); 27 | 28 | readFullConfiguration(); 29 | //connectNetwork(); 30 | 31 | DEBUG_PRINTLN("------------- Configuration Mode -------------------"); 32 | sendSysLogMessage(6, 1, config.boardName, FIRMWARE, 10, counter++, "------------- Configuration Mode -------------------"); 33 | 34 | 35 | initWiFiManager(); 36 | 37 | 38 | //--------------- LOOP ---------------------------------- 39 | 40 | while (1) { 41 | if (buttonChanged && buttonTime > 4000) espRestart('N', "Back to normal mode"); // long button press > 4sec 42 | yield(); 43 | loopWiFiManager(); 44 | } 45 | } 46 | 47 | 48 | void loopWiFiManager() { 49 | 50 | // additional fields 51 | 52 | 53 | // Standard 54 | WiFiManagerParameter p_boardName("boardName", "boardName", config.boardName, STRUCT_CHAR_ARRAY_SIZE); 55 | WiFiManagerParameter p_IOTappStory1("IOTappStory1", "IOTappStory1", config.IOTappStory1, STRUCT_CHAR_ARRAY_SIZE); 56 | WiFiManagerParameter p_IOTappStoryPHP1("IOTappStoryPHP1", "IOTappStoryPHP1", config.IOTappStoryPHP1, STRUCT_CHAR_ARRAY_SIZE); 57 | WiFiManagerParameter p_IOTappStory2("IOTappStory2", "IOTappStory2", config.IOTappStory2, STRUCT_CHAR_ARRAY_SIZE); 58 | WiFiManagerParameter p_IOTappStoryPHP2("IOTappStoryPHP2", "IOTappStoryPHP2", config.IOTappStoryPHP2, STRUCT_CHAR_ARRAY_SIZE); 59 | WiFiManagerParameter p_automaticUpdate("automaticUpdate", "Automatic Update", config.automaticUpdate, 2); 60 | 61 | // Just a quick hint 62 | WiFiManagerParameter p_hint("*Hint: if you want to reuse the currently active WiFi credentials, leave SSID and Password fields empty"); 63 | 64 | // Initialize WiFIManager 65 | WiFiManager wifiManager; 66 | wifiManager.addParameter(&p_hint); 67 | wifiManager.addParameter(&p_boardName); 68 | 69 | //add all parameters here 70 | WiFiManagerParameter p_YouTubeID("YouTubeID", "YouTubeID", config.YouTubeID, STRUCT_CHAR_ARRAY_SIZE); 71 | WiFiManagerParameter p_googleKey("googleKey", "googleKey", config.googleKey, STRUCT_CHAR_ARRAY_SIZE); 72 | 73 | // Standard 74 | 75 | /* 76 | you have to decide if you want to update your constants with Wi-Fi Manager or from JSON. If you want to use JSON, comment the next few lines and all 77 | other lines down in the code 78 | If you want to use WiFiManager to update, then comment the respective lines in readFullConfiguration() 79 | */ 80 | 81 | wifiManager.addParameter(&p_IOTappStory1); 82 | wifiManager.addParameter(&p_IOTappStoryPHP1); 83 | wifiManager.addParameter(&p_IOTappStory2); 84 | wifiManager.addParameter(&p_IOTappStoryPHP2); 85 | wifiManager.addParameter(&p_automaticUpdate); 86 | 87 | 88 | // Sets timeout in seconds until configuration portal gets turned off. 89 | // If not specified device will remain in configuration mode until 90 | // switched off via webserver or device is restarted. 91 | wifiManager.setConfigPortalTimeout(600); 92 | 93 | // It starts an access point 94 | // and goes into a blocking loop awaiting configuration. 95 | // Once the user leaves the portal with the exit button 96 | // processing will continue 97 | if (!wifiManager.startConfigPortal(config.boardName)) { 98 | DEBUG_PRINTLN("Not connected to WiFi but continuing anyway."); 99 | } else { 100 | // If you get here you have connected to the WiFi 101 | DEBUG_PRINTLN("Connected... :-)"); 102 | } 103 | // Getting posted form values and overriding local variables parameters 104 | // Config file is written 105 | 106 | //add all parameters here 107 | wifiManager.addParameter(&p_YouTubeID); 108 | wifiManager.addParameter(&p_googleKey); 109 | 110 | 111 | // Standard 112 | strcpy(config.boardName, p_boardName.getValue()); 113 | strcpy(config.IOTappStory1, p_IOTappStory1.getValue()); 114 | strcpy(config.IOTappStoryPHP1, p_IOTappStoryPHP1.getValue()); 115 | strcpy(config.IOTappStory2, p_IOTappStory2.getValue()); 116 | strcpy(config.IOTappStoryPHP2, p_IOTappStoryPHP2.getValue()); 117 | strcpy(config.automaticUpdate, p_automaticUpdate.getValue()); 118 | 119 | //additional fields 120 | strcpy(config.YouTubeID, p_YouTubeID.getValue()); 121 | strcpy(config.googleKey, p_googleKey.getValue()); 122 | 123 | writeConfig(); 124 | readFullConfiguration(); // read back to fill all variables 125 | 126 | LEDswitch(None); // Turn LED off as we are not in configuration mode. 127 | 128 | espRestart('N', "Configuration finished"); //Normal Operation 129 | 130 | } 131 | 132 | void JSONerror(String err) { 133 | DEBUG_PRINTLN(err); 134 | DEBUG_PRINTLN("Restoring default values"); 135 | writeConfig(); 136 | LEDswitch(RedFastBlink); 137 | 138 | } 139 | 140 | void readFullConfiguration() { 141 | readConfig(); // configuration in EEPROM 142 | if (SPIFFS.begin()) { 143 | File configFile = SPIFFS.open("/config.json", "r"); 144 | if (configFile) { 145 | size_t size = configFile.size(); 146 | if (size <= 1024) { 147 | // Allocate a buffer to store contents of the file. 148 | std::unique_ptr buf(new char[size]); 149 | 150 | configFile.readBytes(buf.get(), size); 151 | // DEBUG_PRINTLN(buf.get()); 152 | 153 | StaticJsonBuffer<1024> jsonBuffer; 154 | JsonObject& json = jsonBuffer.parseObject(buf.get()); 155 | 156 | if (json.success()) { 157 | // Fetch values. 158 | if (json.containsKey("magicBytes")) { 159 | 160 | DEBUG_PRINTLN("SPIFFS Configuration found"); 161 | strcpy(config.magicBytes, json["magicBytes"]); 162 | Serial.println("1"); 163 | strcpy(config.boardName, json["boardName"]); 164 | 165 | /* 166 | You have to decide if you want to update your constants with Wi-Fi Manager or from JSON. If you want to use WiFiManager, comment the next few lines 167 | If you want to use WiFiManager to update, then comment the respective lines in readFullConfiguration() 168 | */ 169 | 170 | strcpy(config.IOTappStory1, json["IOTappStory1"]); 171 | strcpy(config.IOTappStoryPHP1, json["IOTappStoryPHP1"]); 172 | strcpy(config.IOTappStory2, json["IOTappStory2"]); 173 | strcpy(config.IOTappStoryPHP2, json["IOTappStoryPHP2"]); 174 | strcpy(config.automaticUpdate, json["automaticUpdate"]); 175 | 176 | //additional fields 177 | 178 | 179 | // Print values. 180 | for (JsonObject::iterator it = json.begin(); it != json.end(); ++it) 181 | { 182 | DEBUG_PRINT(it->key); 183 | DEBUG_PRINT(": "); 184 | DEBUG_PRINTLN(it->value.asString()); 185 | } 186 | 187 | } else JSONerror("File Content wrong"); 188 | } else JSONerror(" No JSON Format"); 189 | } else JSONerror(" JSON File too long"); 190 | } else JSONerror("File not found"); 191 | } else JSONerror("SPIFFS Configurarion NOT FOUND!!!!"); 192 | 193 | Serial.println("Exit config"); 194 | } 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /YouTubeCounterV1/YouTubeCounterV1.ino: -------------------------------------------------------------------------------- 1 | /* This is an initial sketch to be used as a "blueprint" to create apps which can be used with IOTappstory.com infrastructure 2 | Your code can be filled wherever it is marked. 3 | 4 | 5 | Copyright (c) [2016] [Andreas Spiess] 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #define SKETCH "YouTubeCounter " 28 | #define VERSION "V1.0" 29 | #define FIRMWARE SKETCH VERSION 30 | 31 | #define SERIALDEBUG // Serial is used to present debugging messages 32 | #define REMOTEDEBUGGING // UDP is used to transfer debug messages 33 | 34 | #define LEDS_INVERSE // LEDS on = GND 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include //https://github.com/kentaylor/WiFiManager 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | 54 | #ifdef REMOTEDEBUGGING 55 | #include 56 | #endif 57 | 58 | extern "C" { 59 | #include "user_interface.h" // this is for the RTC memory read/write functions 60 | } 61 | 62 | //-------- Sketch Specific ------- 63 | 64 | 65 | 66 | // -------- PIN DEFINITIONS ------------------ 67 | #ifdef ARDUINO_ESP8266_ESP01 // Generic ESP's 68 | #define MODEBUTTON 4 69 | #define LEDgreen 13 70 | //#define LEDred 12 71 | #else 72 | #define MODEBUTTON D3 73 | #define LEDgreen D7 74 | //#define LEDred D6 75 | #endif 76 | 77 | // --- Sketch Specific ----- 78 | 79 | #ifdef ARDUINO_ESP8266_ESP01 // Generic ESP's 80 | #define BUZ_PIN 5 81 | #else 82 | #define BUZ_PIN D1 83 | #endif 84 | 85 | 86 | 87 | //---------- DEFINES for SKETCH ---------- 88 | #define STRUCT_CHAR_ARRAY_SIZE 50 // length of config variables 89 | #define MAX_WIFI_RETRIES 50 90 | #define RTCMEMBEGIN 68 91 | #define MAGICBYTE 85 92 | 93 | // --- Sketch Specific ----- 94 | // #define SERVICENAME "VIRGIN" // name of the MDNS service used in this group of ESPs 95 | #define NUMBER_OF_MATRIXES 5 96 | #define CS_PIN 2 97 | #define STARWARS "t112v127l12d2cg2d4cg2d4cc-cd2cg2d4cg2d4cc-c // General helpers for all IOTappStory sketches 194 | #include "IOTappStoryHelpers.h" // Sketch specific helpers for all IOTappStory sketches 195 | 196 | 197 | 198 | 199 | // ================================== SETUP ================================================ 200 | 201 | void setup() { 202 | Serial.begin(115200); 203 | for (int i = 0; i < 5; i++) DEBUG_PRINTLN(""); 204 | DEBUG_PRINTLN("Start "FIRMWARE); 205 | 206 | 207 | // ----------- PINS ---------------- 208 | pinMode(MODEBUTTON, INPUT_PULLUP); // MODEBUTTON as input for Config mode selection 209 | 210 | #ifdef LEDgreen 211 | pinMode(LEDgreen, OUTPUT); 212 | digitalWrite(LEDgreen, LEDOFF); 213 | #endif 214 | #ifdef LEDred 215 | pinMode(LEDred, OUTPUT); 216 | digitalWrite(LEDred, LEDOFF); 217 | #endif 218 | 219 | // --- Sketch Specific ----- 220 | 221 | pinMode(A0, INPUT); 222 | 223 | // ------------- INTERRUPTS ---------------------------- 224 | attachInterrupt(MODEBUTTON, ISRbuttonStateChanged, CHANGE); 225 | blink.detach(); 226 | 227 | 228 | //------------- LED and DISPLAYS ------------------------ 229 | LEDswitch(GreenBlink); 230 | 231 | 232 | // --------- BOOT STATISTICS ------------------------ 233 | // read and increase boot statistics (optional) 234 | readRTCmem(); 235 | rtcMem.bootTimes++; 236 | writeRTCmem(); 237 | printRTCmem(); 238 | 239 | 240 | //---------- SELECT BOARD MODE ----------------------------- 241 | 242 | system_rtc_mem_read(RTCMEMBEGIN + 100, &boardMode, 1); // Read the "boardMode" flag RTC memory to decide, if to go to config 243 | if (boardMode == 'C') configESP(); 244 | 245 | readFullConfiguration(); 246 | 247 | // --------- START WIFI -------------------------- 248 | 249 | connectNetwork(); 250 | 251 | sendSysLogMessage(2, 1, config.boardName, FIRMWARE, 10, counter++, "------------- Normal Mode -------------------"); 252 | 253 | if (atoi(config.automaticUpdate) == 1) IOTappStory(); 254 | 255 | 256 | 257 | // ----------- SPECIFIC SETUP CODE ---------------------------- 258 | 259 | // add a DNS service 260 | // MDNS.addService(SERVICENAME, "tcp", 8080); // just as an example 261 | myDisplay.begin(); 262 | myDisplay.setIntensity (15); 263 | dispMatrix(VERSION); 264 | delay(10000); 265 | 266 | // ----------- END SPECIFIC SETUP CODE ---------------------------- 267 | 268 | LEDswitch(None); 269 | pinMode(MODEBUTTON, INPUT_PULLUP); // MODEBUTTON as input for Config mode selection 270 | 271 | sendSysLogMessage(7, 1, config.boardName, FIRMWARE, 10, counter++, "Setup done"); 272 | } 273 | 274 | 275 | 276 | 277 | 278 | //======================= LOOP ========================= 279 | void loop() { 280 | //-------- IOTappStory Block --------------- 281 | yield(); 282 | handleModeButton(); // this routine handles the reaction of the Flash button. If short press: update of skethc, long press: Configuration 283 | 284 | // Normal blind (1 sec): Connecting to network 285 | // fast blink: Configuration mode. Please connect to ESP network 286 | // Slow Blink: IOTappStore Update in progress 287 | 288 | if (millis() - debugEntry > 5000) { // Non-Blocking second counter 289 | debugEntry = millis(); 290 | sendDebugMessage(); 291 | } 292 | 293 | 294 | //-------- Your Sketch --------------- 295 | 296 | display(subscribers); 297 | 298 | if ((millis() - loopEntry) > UPDATEINTERVAL) { 299 | 300 | Serial.println(UPDATEINTERVAL); 301 | loopEntry = millis(); 302 | subscribers = getSubscribers(); 303 | // subscribers = 9800; 304 | 305 | if (subscribers > lastSubscribers) { 306 | beepUp(); 307 | if (subscribers % 10 <= lastSubscribers % 10) for (int ii = 0; ii < 1; ii++) beepUp(); 308 | if (subscribers % 100 <= lastSubscribers % 100) starwars(); 309 | if (subscribers % 1000 <= lastSubscribers % 1000) for (int ii = 0; ii < 3; ii++) starwars(); 310 | } else if (subscribers < lastSubscribers) beepDown(); 311 | lastSubscribers = subscribers; 312 | } 313 | 314 | } 315 | //------------------------- END LOOP -------------------------------------------- 316 | 317 | void sendDebugMessage() { 318 | // ------- Syslog Message -------- 319 | 320 | /* severity: 2 critical, 6 info, 7 debug 321 | facility: 1 user level 322 | String hostName: Board Name 323 | app: FIRMWARE 324 | procID: unddefined 325 | msgID: counter 326 | message: Your message 327 | */ 328 | 329 | sysMessage = ""; 330 | long h1 = ESP.getFreeHeap(); 331 | sysMessage += " Subscribers "; 332 | sysMessage += subscribers; 333 | sysMessage += " Last Subscribers "; 334 | sysMessage += lastSubscribers; 335 | 336 | sysMessage += " Heap "; 337 | sysMessage += h1; 338 | sendSysLogMessage(6, 1, config.boardName, FIRMWARE, 10, counter++, sysMessage); 339 | } 340 | 341 | 342 | bool readRTCmem() { 343 | bool ret = true; 344 | system_rtc_mem_read(RTCMEMBEGIN, &rtcMem, sizeof(rtcMem)); 345 | if (rtcMem.markerFlag != MAGICBYTE) { 346 | rtcMem.markerFlag = MAGICBYTE; 347 | rtcMem.bootTimes = 0; 348 | system_rtc_mem_write(RTCMEMBEGIN, &rtcMem, sizeof(rtcMem)); 349 | ret = false; 350 | } 351 | return ret; 352 | } 353 | 354 | void printRTCmem() { 355 | DEBUG_PRINTLN(""); 356 | DEBUG_PRINTLN("rtcMem "); 357 | DEBUG_PRINT("markerFlag "); 358 | DEBUG_PRINTLN(rtcMem.markerFlag); 359 | DEBUG_PRINT("bootTimes "); 360 | DEBUG_PRINTLN(rtcMem.bootTimes); 361 | } 362 | 363 | 364 | int getSubscribers() { 365 | if (api.getChannelStatistics(CHANNEL_ID)) 366 | { 367 | subscribers = api.channelStats.subscriberCount; 368 | Serial.println("---------Stats---------"); 369 | Serial.print("Subscriber Count: "); 370 | Serial.println(subscribers); 371 | Serial.print("View Count: "); 372 | Serial.println(api.channelStats.viewCount); 373 | Serial.print("Comment Count: "); 374 | Serial.println(api.channelStats.commentCount); 375 | Serial.print("Video Count: "); 376 | Serial.println(api.channelStats.videoCount); 377 | Serial.println("------------------------"); 378 | } 379 | return subscribers; 380 | } 381 | 382 | 383 | void beepUp() { 384 | music.play("T80 L40 O4 CDEFGHAB>CDEFGHAB"); 385 | while (music.getIsPlaying() == 1) yield(); 386 | delay(500); 387 | } 388 | 389 | void beepDown() { 390 | music.play("T1000 L40 O5 BAGFEDC 15.0) ? 15 : (int)_hi; 406 | _intensity = (_hi < 2.0) ? 0 : (int)_hi; 407 | return _intensity; 408 | } 409 | 410 | void display(long num) { 411 | String _dispContent = String(num); 412 | 413 | int _intensity = get_intensity(); 414 | myDisplay.setIntensity (_intensity); 415 | 416 | if (_intensity == 0) _dispContent = " "; 417 | else if (_dispContent.length() < NUMBER_OF_MATRIXES) _dispContent = " " + _dispContent; 418 | 419 | dispMatrix(_dispContent); 420 | } 421 | 422 | void dispMatrix(String content) { 423 | char charBuf[50]; 424 | content.toCharArray(charBuf, 50); 425 | myDisplay.sendString(charBuf); 426 | } 427 | 428 | 429 | 430 | -------------------------------------------------------------------------------- /YoutubeCounterIOTappStoryV1.0/YoutubeCounterIOTappStoryV1.0.ino: -------------------------------------------------------------------------------- 1 | /* This is an initial sketch to be used as a "blueprint" to create apps which can be used with IOTappstory.com infrastructure 2 | Your code can be filled wherever it is marked. 3 | 4 | 5 | Copyright (c) [2016] [Andreas Spiess] 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #define SERIALDEBUG // Serial is used to present debugging messages 27 | //#define DISPLAY 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define SKETCH "YoutubeCounter " 37 | #define VERSION "V1.6" 38 | #define COMPDATE __DATE__ __TIME__ 39 | 40 | // ================================================ PIN DEFINITIONS ====================================== 41 | #ifdef ARDUINO_ESP8266_ESP01 // Generic ESP's 42 | #define MODEBUTTON 0 43 | #define BUZ_PIN 5 44 | #define CS_PIN 2 45 | #define LEDgreen 13 46 | //#define LEDred 12 47 | #else 48 | #define MODEBUTTON D3 49 | #define BUZ_PIN D1 50 | #define CS_PIN D4 51 | #define LEDgreen D7 52 | //#define LEDred D6 53 | #endif 54 | 55 | 56 | #define NUMBER_OF_DIGITS 5 57 | #define STARWARS "t112v127l12d2cg2d4cg2d4cc-cd2cg2d4cg2d4cc-c 68 | IOTAppStory IAS(SKETCH, VERSION, COMPDATE, MODEBUTTON); 69 | 70 | MAX7219_Dot_Matrix myDisplay (NUMBER_OF_DIGITS, CS_PIN); // # digits, and then specify the CS pin 71 | 72 | MusicEngine music(BUZ_PIN); 73 | 74 | WiFiClientSecure client; 75 | YoutubeApi api(API_KEY, client); 76 | SNTPtime NTPch("ch.pool.ntp.org"); 77 | 78 | // ================================================ SETUP ================================================ 79 | void setup() { 80 | IAS.serialdebug(true); 81 | Serial.println("Start"); 82 | myDisplay.begin(); 83 | myDisplay.setIntensity (15); 84 | 85 | IAS.preSetBoardname("YouTube"); 86 | IAS.preSetAutoUpdate(false); // automaticUpdate (true, false) 87 | IAS.preSetAutoConfig(true); // automaticConfig (true, false) 88 | IAS.preSetWifi(mySSID, myPASSWORD); 89 | 90 | IAS.onModeButtonShortPress([]() { 91 | Serial.println(" If mode button is released, I will enter in firmware update mode."); 92 | Serial.println("*------------------------------------------------------------------------ -*"); 93 | dispMatrix("UPD"); 94 | }); 95 | 96 | IAS.onModeButtonLongPress([]() { 97 | Serial.println(" If mode button is released, I will enter in configuration mode."); 98 | Serial.println("*------------------------------------------------------------------------ -*"); 99 | dispMatrix("UPD"); 100 | }); 101 | 102 | IAS.onModeButtonVeryLongPress([]() { 103 | Serial.println(" If mode button is released, I won't do anything."); 104 | Serial.println("*-------------------------------------------------------------------------*"); 105 | dispMatrix("UPD"); 106 | }); 107 | 108 | IAS.begin(true, 'P'); 109 | 110 | dispMatrix(VERSION); 111 | pinMode(A0, INPUT); 112 | 113 | while (!NTPch.setSNTPtime()) Serial.print("."); // set internal clock 114 | Serial.println("Setup done"); 115 | delay(3000); 116 | } 117 | 118 | 119 | // ================================================ LOOP ================================================= 120 | void loop() { 121 | yield(); 122 | IAS.buttonLoop(); // this routine handles the reaction of the Flash button. If short press: update of skethc, long press: Configuration 123 | 124 | 125 | //-------- Your Sketch starts from here --------------- 126 | 127 | myDisplay.setIntensity (adjustIntensity()); 128 | 129 | if (millis() - entrySubscriberLoop > nextRound) { 130 | entrySubscriberLoop = millis(); 131 | 132 | if (api.getChannelStatistics(CHANNEL_ID)) 133 | { 134 | // get subscribers from YouTube 135 | subscribers = api.channelStats.subscriberCount; 136 | dispString(String(subscribers)); 137 | 138 | if ((subscribers > lastSubscribers) && (lastSubscribers > 0) ) { 139 | beepUp(); 140 | if (subscribers % 10 <= lastSubscribers % 10) for (int ii = 0; ii < 1; ii++) beepUp(); 141 | if (subscribers % 100 <= lastSubscribers % 100) starwars(); 142 | if (subscribers % 1000 <= lastSubscribers % 1000) for (int ii = 0; ii < 3; ii++) starwars(); 143 | } else if (subscribers < lastSubscribers) beepDown(); 144 | lastSubscribers = subscribers; 145 | Serial.println("---------Stats---------"); 146 | Serial.print("Subscriber Count: "); 147 | Serial.println(api.channelStats.subscriberCount); 148 | Serial.print("View Count: "); 149 | Serial.println(api.channelStats.viewCount); 150 | Serial.print("Comment Count: "); 151 | Serial.println(api.channelStats.commentCount); 152 | Serial.print("Video Count: "); 153 | Serial.println(api.channelStats.videoCount); 154 | // Probably not needed :) 155 | Serial.print("hiddenSubscriberCount: "); 156 | Serial.println(subscribers); 157 | 158 | Serial.println("------------------------"); 159 | nextRound = 10000; 160 | } 161 | } 162 | } 163 | 164 | 165 | void dispMatrix(String content) { 166 | #ifdef DISPLAY 167 | Serial.println("dispMatrix"); 168 | char charBuf[50]; 169 | content.toCharArray(charBuf, 50); 170 | myDisplay.sendString(charBuf); 171 | #endif 172 | } 173 | 174 | 175 | void beepUp() { 176 | music.play("T80 L40 O4 CDEFGHAB>CDEFGHAB"); 177 | while (music.getIsPlaying() == 1) yield(); 178 | delay(500); 179 | } 180 | 181 | void beepDown() { 182 | music.play("T1000 L40 O5 BAGFEDC 15.0) ? 15 : (int)_intensity; 197 | intensity = (_intensity < 2.0) ? 0 : (int)_intensity; 198 | return intensity; 199 | } 200 | 201 | void dispString(String dispContent) { 202 | int intensity = adjustIntensity(); 203 | 204 | // display 205 | if (dispContent.length() < 5) dispContent = " " + dispContent; 206 | if (intensity == 0) dispContent = " "; 207 | else myDisplay.setIntensity (intensity); 208 | dispMatrix(dispContent); 209 | } 210 | 211 | void dispTime() { 212 | dateTime = NTPch.getTime(1.0, 1); // get time from internal clock 213 | NTPch.printDateTime(dateTime); 214 | 215 | byte actualHour = dateTime.hour; 216 | byte actualMinute = dateTime.minute; 217 | String strMin = String(actualMinute); 218 | if (strMin.length() < 2) strMin = "0" + strMin; 219 | dispString(String(actualHour) + ":" + strMin); 220 | } 221 | 222 | -------------------------------------------------------------------------------- /YoutubeCounterNeopixelV1.0/YoutubeCounterNeopixelV1.0.ino: -------------------------------------------------------------------------------- 1 | /* This is an initial sketch to be used as a "blueprint" to create apps which can be used with IOTappstory.com infrastructure 2 | Your code can be filled wherever it is marked. 3 | 4 | 5 | Copyright (c) [2016] [Andreas Spiess] 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define COMPDATE __DATE__ __TIME__ 36 | #define MODEBUTTON D3 37 | // IotAppStory.com library 38 | IOTAppStory IAS(COMPDATE, MODEBUTTON); // Initialize IotAppStory 39 | 40 | 41 | // ================================================ PIN DEFINITIONS ====================================== 42 | 43 | 44 | #define BUZ_PIN D5 45 | #define NEO_PIN D4 46 | #define POWER_PIN D1 47 | 48 | #define NUMBER_OF_DIGITS 6 49 | #define STARWARS "t112v127l12d2cg2d4cg2d4cc-cd2cg2d4cg2d4cc-c DISP_LOOP_INTERVAL) { 172 | entryDispLoop = millis(); 173 | displayNeo(subscribers.actual, subscribers.actual - subscribers.old[timeNow.hour]); 174 | } 175 | 176 | if (millis() - entryNTPLoop > NTP_LOOP_INTERVAL) { 177 | // Serial.println("NTP Loop"); 178 | entryNTPLoop = millis(); 179 | timeNow = NTPch.getTime(1.0, 1); // get time from internal clock 180 | NTPch.printDateTime(timeNow); 181 | if (timeNow.hour != lastHour ) { 182 | Serial.println("New hour!!!!!!!"); 183 | subscribers.old[lastHour] = subscribers.actual; 184 | subscribers.last = subscribers.actual; 185 | debugPrintSubs(); 186 | lastHour = timeNow.hour; 187 | } 188 | } 189 | 190 | if (millis() - entrySubscriberLoop > SUBSCRIBER_INTERVAL) { 191 | // Serial.println("Subscriber Loop"); 192 | entrySubscriberLoop = millis(); 193 | updateSubs(); 194 | } 195 | } 196 | 197 | void updateSubs() { 198 | if (api.getChannelStatistics(CHANNEL_ID)) 199 | { 200 | // get subscribers from YouTube 201 | // Serial.println("Get Subs"); 202 | subscribers.actual = api.channelStats.subscriberCount; 203 | displayNeo(subscribers.actual, subscribers.actual - subscribers.old[timeNow.hour]); 204 | Serial.print("Subs "); 205 | Serial.print(subscribers.actual); 206 | Serial.print(" yesterday "); 207 | Serial.println(subscribers.old[timeNow.hour]); 208 | if (subscribers.last > 0) { 209 | if (subscribers.actual > subscribers.last ) { 210 | beepUp(); 211 | if (subscribers.actual % 10 <= subscribers.last % 10) for (int ii = 0; ii < 1; ii++) beepUp(); 212 | if (subscribers.actual % 100 <= subscribers.last % 100) starwars(); 213 | if (subscribers.actual % 1000 <= subscribers.last % 1000) for (int ii = 0; ii < 2; ii++) starwars(); 214 | } 215 | else { 216 | if (subscribers.actual < subscribers.last ) beepDown(); 217 | } 218 | } 219 | subscribers.last = subscribers.actual; 220 | // debugPrint(); 221 | } 222 | } 223 | 224 | 225 | void displayNeo(int subs, int variance ) { 226 | // Serial.println("Display"); 227 | 228 | matrix.fillScreen(0); 229 | int bright = measureLight(); 230 | if (bright > -1) { 231 | matrix.setBrightness(bright); 232 | matrix.setTextColor(colors[2]); 233 | matrix.setCursor(0, 0); 234 | matrix.print(String(subs)); 235 | 236 | // Show arrow 237 | char arrow = (variance <= 0) ? 0x1F : 0x1E; 238 | if (variance > 0) matrix.setTextColor(colors[1]); 239 | else matrix.setTextColor(colors[0]); 240 | matrix.setCursor(36, 0); 241 | matrix.print(arrow); 242 | 243 | // show variance bar 244 | int h = map(variance, 0, 400, 0, 8); 245 | h = (h > 8) ? 8 : h; 246 | if (h > 0) matrix.fillRect(42, 8 - h, 1, h , colors[3]); 247 | } 248 | matrix.show(); 249 | } 250 | 251 | void beepUp() { 252 | music.play("T80 L40 O4 CDEFGHAB>CDEFGHAB"); 253 | while (music.getIsPlaying() == 1) yield(); 254 | delay(500); 255 | } 256 | 257 | void beepDown() { 258 | music.play("T1000 L40 O5 BAGFEDC MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : brightness; // clip value 272 | brightness = (brightness < 20) ? 0 : brightness; 273 | // brightness = MAX_BRIGHTNESS; 274 | return brightness; 275 | } 276 | 277 | void debugPrint() { 278 | Serial.println("---------Stats---------"); 279 | Serial.print("Subscriber Count: "); 280 | Serial.println(subscribers.actual); 281 | Serial.print("Variance: "); 282 | Serial.println(subscribers.actual - subscribers.old[timeNow.hour]); 283 | Serial.print("LastSubs: "); 284 | Serial.println(subscribers.last); 285 | Serial.print("View Count: "); 286 | Serial.println(api.channelStats.viewCount); 287 | Serial.print("Comment Count: "); 288 | Serial.println(api.channelStats.commentCount); 289 | Serial.print("Video Count: "); 290 | Serial.println(api.channelStats.videoCount); 291 | // Probably not needed :) 292 | Serial.print("hiddenSubscriberCount: "); 293 | Serial.println(subscribers.actual); 294 | Serial.println("------------------------"); 295 | } 296 | void debugPrintSubs() { 297 | for (int i = 0; i < 24; i++) { 298 | Serial.print(i); 299 | Serial.print(" old "); 300 | Serial.println(subscribers.old[i]); 301 | } 302 | } 303 | 304 | void displayText(String tt) { 305 | matrix.setTextWrap(false); 306 | matrix.setBrightness(40); 307 | matrix.fillScreen(0); 308 | matrix.setTextColor(colors[0]); 309 | matrix.setCursor(0, 0); 310 | matrix.print(tt); 311 | matrix.show(); 312 | } 313 | 314 | --------------------------------------------------------------------------------