 3754088a44
			
		
	
	3754088a44
	
	
	
		
			
			* Added support for H803FW controller * Create usermod_bme280.cpp * Create usermod_bme280.cpp * Added BME280 sensor * Update readme.md * Update usermod_bme280.cpp * Update platformio.ini * Update for lightweight sensor * Added travis build badge * Update readme.md * Update readme.md * Update .gitignore * Changed ldscript for a file system * Update NpbWrapper.h * Update .gitignore * Delete wled-ci.yml * Added usermod for ST7789 display Functionality tested with ESP32. Works with main WLED and @blazoncek fork. * fixes * Update .gitignore * Firmware updates!!! Updated official @Aircoookie firmware to v0.12.0-b4 build 2103290 and developer @blazoncek firmware to latest v0.12.0-b3 build 2103282. * Updated platformio.ini for Universal Wemos Shield board
		
			
				
	
	
		
			350 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Credits to @mrVanboy, @gwaland and my dearest friend @westward
 | |
| // Also for @spiff72 for usermod TTGO-T-Display
 | |
| // 210217
 | |
| #pragma once
 | |
| 
 | |
| #include "wled.h"
 | |
| #include <TFT_eSPI.h>
 | |
| #include <SPI.h>
 | |
| 
 | |
| #define USERMOD_ST7789_DISPLAY 97
 | |
| 
 | |
| #ifndef TFT_DISPOFF
 | |
| #define TFT_DISPOFF 0x28
 | |
| #endif
 | |
| 
 | |
| #ifndef TFT_SLPIN
 | |
| #define TFT_SLPIN   0x10
 | |
| #endif
 | |
| 
 | |
| #define TFT_MOSI            21
 | |
| #define TFT_SCLK            22
 | |
| #define TFT_DC              18
 | |
| #define TFT_RST             5
 | |
| #define TFT_BL              26  // Display backlight control pin
 | |
| 
 | |
| TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library
 | |
| 
 | |
| // How often we are redrawing screen
 | |
| #define USER_LOOP_REFRESH_RATE_MS 1000
 | |
| 
 | |
| 
 | |
| //class name. Use something descriptive and leave the ": public Usermod" part :)
 | |
| class St7789DisplayUsermod : public Usermod {
 | |
|   private:
 | |
|     //Private class members. You can declare variables and functions only accessible to your usermod here
 | |
|     unsigned long lastTime = 0;
 | |
| 
 | |
|     bool displayTurnedOff = false;
 | |
|     long lastRedraw = 0;
 | |
|     // needRedraw marks if redraw is required to prevent often redrawing.
 | |
|     bool needRedraw = true;
 | |
|     // Next variables hold the previous known values to determine if redraw is required.
 | |
|     String knownSsid = "";
 | |
|     IPAddress knownIp;
 | |
|     uint8_t knownBrightness = 0;
 | |
|     uint8_t knownMode = 0;
 | |
|     uint8_t knownPalette = 0;
 | |
|     uint8_t tftcharwidth = 19;  // Number of chars that fit on screen with text size set to 2
 | |
|     long lastUpdate = 0;
 | |
| 
 | |
|   public:
 | |
|     //Functions called by WLED
 | |
| 
 | |
|     /*
 | |
|      * setup() is called once at boot. WiFi is not yet connected at this point.
 | |
|      * You can use it to initialize variables, sensors or similar.
 | |
|      */
 | |
|     void setup()
 | |
|     {
 | |
|         tft.init();
 | |
|         tft.setRotation(0);  //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
 | |
|         tft.fillScreen(TFT_BLACK);
 | |
|         tft.setTextColor(TFT_RED);
 | |
|         tft.setCursor(60, 100);
 | |
|         tft.setTextDatum(MC_DATUM);
 | |
|         tft.setTextSize(2);
 | |
|         tft.print("Loading...");
 | |
|         if (TFT_BL > 0) 
 | |
|         { // TFT_BL has been set in the TFT_eSPI library
 | |
|          pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
 | |
|          digitalWrite(TFT_BL, HIGH); // Turn backlight on.
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * connected() is called every time the WiFi is (re)connected
 | |
|      * Use it to initialize network interfaces
 | |
|      */
 | |
|     void connected() {
 | |
|       //Serial.println("Connected to WiFi!");
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * loop() is called continuously. Here you can check for events, read sensors, etc.
 | |
|      *
 | |
|      * Tips:
 | |
|      * 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection.
 | |
|      *    Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker.
 | |
|      *
 | |
|      * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds.
 | |
|      *    Instead, use a timer check as shown here.
 | |
|      */
 | |
|     void loop() {
 | |
| // Check if we time interval for redrawing passes.
 | |
|     if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
|     lastUpdate = millis();
 | |
|   
 | |
| // Turn off display after 5 minutes with no change.
 | |
|     if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
 | |
|         {
 | |
|             digitalWrite(TFT_BL, LOW); // Turn backlight off. 
 | |
|             displayTurnedOff = true;
 | |
|         } 
 | |
| 
 | |
| // Check if values which are shown on display changed from the last time.
 | |
|     if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid)
 | |
|     {
 | |
|     needRedraw = true;
 | |
|     }
 | |
|     else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP()))
 | |
|     {
 | |
|     needRedraw = true;
 | |
|     }
 | |
|     else if (knownBrightness != bri)
 | |
|     {
 | |
|     needRedraw = true;
 | |
|     }
 | |
|     else if (knownMode != strip.getMode())
 | |
|     {
 | |
|     needRedraw = true;
 | |
|     }
 | |
|     else if (knownPalette != strip.getSegment(0).palette)
 | |
|     {
 | |
|     needRedraw = true;
 | |
|     }
 | |
| 
 | |
|     if (!needRedraw)
 | |
|     {
 | |
|     return;
 | |
|     }
 | |
|     needRedraw = false;
 | |
|   
 | |
|     if (displayTurnedOff)
 | |
|     {
 | |
|         digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
 | |
|         displayTurnedOff = false;
 | |
|     }
 | |
|     lastRedraw = millis();
 | |
| 
 | |
| // Update last known values.
 | |
|     #if defined(ESP8266)
 | |
|         knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
 | |
|     #else
 | |
|         knownSsid = WiFi.SSID();
 | |
|     #endif
 | |
|     knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
 | |
|     knownBrightness = bri;
 | |
|     knownMode = strip.getMode();
 | |
|     knownPalette = strip.getSegment(0).palette;
 | |
| 
 | |
|     tft.fillScreen(TFT_BLACK);
 | |
|     tft.setTextSize(2);
 | |
| // First row with Wifi name
 | |
|     tft.setTextColor(TFT_SILVER);
 | |
|     tft.setCursor(3, 40);
 | |
|     tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
 | |
| // Print `~` char to indicate that SSID is longer, than our dicplay
 | |
|     if (knownSsid.length() > tftcharwidth)
 | |
|         tft.print("~");
 | |
| 
 | |
| // Second row with AP IP and Password or IP
 | |
|     tft.setTextColor(TFT_GREEN);
 | |
|     tft.setTextSize(2);
 | |
|     tft.setCursor(3, 64);
 | |
| // Print AP IP and password in AP mode or knownIP if AP not active.
 | |
| 
 | |
|     if (apActive)
 | |
|     {
 | |
|     tft.setTextColor(TFT_YELLOW);
 | |
|     tft.print("AP IP: ");
 | |
|     tft.print(knownIp);
 | |
|     tft.setCursor(3,86);
 | |
|     tft.setTextColor(TFT_YELLOW);
 | |
|     tft.print("AP Pass:");
 | |
|     tft.print(apPass);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|     tft.setTextColor(TFT_GREEN);
 | |
|     tft.print("IP: ");
 | |
|     tft.print(knownIp);
 | |
|     tft.setCursor(3,86);
 | |
|     //tft.print("Signal Strength: ");
 | |
|     //tft.print(i.wifi.signal);
 | |
|     tft.setTextColor(TFT_WHITE);
 | |
|     tft.print("Bri: ");
 | |
|     tft.print(((float(bri)/255)*100),0);
 | |
|     tft.print("%");
 | |
|     }
 | |
| 
 | |
| // Third row with mode name
 | |
|     tft.setCursor(3, 108);
 | |
|     uint8_t qComma = 0;
 | |
|     bool insideQuotes = false;
 | |
|     uint8_t printedChars = 0;
 | |
|     char singleJsonSymbol;
 | |
| // Find the mode name in JSON
 | |
|     for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
 | |
|     {
 | |
|         singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
 | |
|         switch (singleJsonSymbol)
 | |
|         {
 | |
|             case '"':
 | |
|             insideQuotes = !insideQuotes;
 | |
|         break;
 | |
|         case '[':
 | |
|         case ']':
 | |
|         break;
 | |
|         case ',':
 | |
|         qComma++;
 | |
|         default:
 | |
|             if (!insideQuotes || (qComma != knownMode))
 | |
|             break;
 | |
|         tft.setTextColor(TFT_MAGENTA);
 | |
|         tft.print(singleJsonSymbol);
 | |
|         printedChars++;
 | |
|         }
 | |
|     if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
 | |
|       break;
 | |
|     }
 | |
| // Fourth row with palette name
 | |
|     tft.setTextColor(TFT_YELLOW);
 | |
|     tft.setCursor(3, 130);
 | |
|     qComma = 0;
 | |
|     insideQuotes = false;
 | |
|     printedChars = 0;
 | |
| // Looking for palette name in JSON.
 | |
|     for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
 | |
|     {
 | |
|     singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
 | |
|         switch (singleJsonSymbol)
 | |
|         {
 | |
|         case '"':
 | |
|         insideQuotes = !insideQuotes;
 | |
|         break;
 | |
|         case '[':
 | |
|         case ']':
 | |
|         break;
 | |
|         case ',':
 | |
|         qComma++;
 | |
|         default:
 | |
|             if (!insideQuotes || (qComma != knownPalette))
 | |
|             break;
 | |
|         tft.print(singleJsonSymbol);
 | |
|         printedChars++;
 | |
|         }
 | |
| // The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
 | |
|     if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
 | |
|       break;
 | |
|     }
 | |
| // Fifth row with estimated mA usage
 | |
|     tft.setTextColor(TFT_SILVER);
 | |
|     tft.setCursor(3, 152);
 | |
| // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
 | |
|     tft.print("Current: ");
 | |
|     tft.print(strip.currentMilliamps);
 | |
|     tft.print("mA");
 | |
|     }
 | |
|     /*
 | |
|      * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
 | |
|      * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
 | |
|      * Below it is shown how this could be used for e.g. a light sensor
 | |
|      */
 | |
|     /*
 | |
|     void addToJsonInfo(JsonObject& root)
 | |
|     {
 | |
|       int reading = 20;
 | |
|       //this code adds "u":{"Light":[20," lux"]} to the info object
 | |
|       JsonObject user = root["u"];
 | |
|       if (user.isNull()) user = root.createNestedObject("u");
 | |
| 
 | |
|       JsonArray lightArr = user.createNestedArray("Light"); //name
 | |
|       lightArr.add(reading); //value
 | |
|       lightArr.add(" lux"); //unit
 | |
|     }
 | |
|     */
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
 | |
|      * Values in the state object may be modified by connected clients
 | |
|      */
 | |
|     void addToJsonState(JsonObject& root)
 | |
|     {
 | |
|       //root["user0"] = userVar0;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
 | |
|      * Values in the state object may be modified by connected clients
 | |
|      */
 | |
|     void readFromJsonState(JsonObject& root)
 | |
|     {
 | |
|       userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
 | |
|       //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
 | |
|      * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
 | |
|      * If you want to force saving the current state, use serializeConfig() in your loop().
 | |
|      *
 | |
|      * CAUTION: serializeConfig() will initiate a filesystem write operation.
 | |
|      * It might cause the LEDs to stutter and will cause flash wear if called too often.
 | |
|      * Use it sparingly and always in the loop, never in network callbacks!
 | |
|      *
 | |
|      * addToConfig() will also not yet add your setting to one of the settings pages automatically.
 | |
|      * To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
 | |
|      *
 | |
|      * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
 | |
|      */
 | |
|     void addToConfig(JsonObject& root)
 | |
|     {
 | |
|       JsonObject top = root.createNestedObject("exampleUsermod");
 | |
|       top["great"] = userVar0; //save this var persistently whenever settings are saved
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * readFromConfig() can be used to read back the custom settings you added with addToConfig().
 | |
|      * This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
 | |
|      *
 | |
|      * readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
 | |
|      * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
 | |
|      * If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
 | |
|      */
 | |
|     void readFromConfig(JsonObject& root)
 | |
|     {
 | |
|       JsonObject top = root["top"];
 | |
|       userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*
 | |
|      * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
 | |
|      * This could be used in the future for the system to determine whether your usermod is installed.
 | |
|      */
 | |
|     uint16_t getId()
 | |
|     {
 | |
|       return USERMOD_ST7789_DISPLAY;
 | |
|     }
 | |
| 
 | |
|    //More methods can be added in the future, this example will then be extended.
 | |
|    //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class!
 | |
| }; |