" // slqi
+ "
" // slqi
"\0"
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
//=ZB_WEB_BATTERY
@@ -1779,22 +1779,22 @@ const char ZB_WEB_U[] PROGMEM =
// ++++++++++++++++++++vvvvvvvvvvvvvvvvvvv++++++++++++++++++++
enum {
ZB_WEB_CSS=0,
- ZB_WEB_PERMITJOIN_ACTIVE=512,
- ZB_WEB_VIS_JS_BEFORE=566,
- ZB_WEB_VIS_JS_AFTER=1039,
- ZB_WEB_AUTO_REFRESH=1103,
- ZB_WEB_MAP_REFRESH=1169,
- ZB_WEB_STATUS_LINE=1235,
- ZB_WEB_BATTERY=1344,
- ZB_WEB_LAST_SEEN=1394,
- ZB_WEB_COLOR_RGB=1442,
- ZB_WEB_LINE_START=1502,
- ZB_WEB_LIGHT_CT=1542,
- ZB_WEB_END_STATUS=1597,
- ZB_WEB_LINE_END=1614,
+ ZB_WEB_PERMITJOIN_ACTIVE=507,
+ ZB_WEB_VIS_JS_BEFORE=561,
+ ZB_WEB_VIS_JS_AFTER=1034,
+ ZB_WEB_AUTO_REFRESH=1098,
+ ZB_WEB_MAP_REFRESH=1164,
+ ZB_WEB_STATUS_LINE=1230,
+ ZB_WEB_BATTERY=1338,
+ ZB_WEB_LAST_SEEN=1388,
+ ZB_WEB_COLOR_RGB=1436,
+ ZB_WEB_LINE_START=1496,
+ ZB_WEB_LIGHT_CT=1536,
+ ZB_WEB_END_STATUS=1591,
+ ZB_WEB_LINE_END=1608,
};
-// Compressed from 1631 to 1109, -32.0%
+// Compressed from 1625 to 1111, -31.6%
const char ZB_WEB[] PROGMEM = "\x00\x66\x3D\x0E\xCA\xB1\xC1\x33\xF0\xF6\xD1\xEE\x3D\x3D\x46\x41\x33\xF0\xE8\x6D"
"\xA1\x15\x08\x79\xF6\x51\xDD\x3C\xCC\x6F\xFD\x47\x58\x62\xB4\x21\x0E\xF1\xED\x1F"
"\xD1\x28\x51\xE6\x72\x99\x0C\x36\x1E\x0C\x67\x51\xD7\xED\x36\xB3\xCC\xE7\x99\xF4"
@@ -1805,52 +1805,52 @@ const char ZB_WEB[] PROGMEM = "\x00\x66\x3D\x0E\xCA\xB1\xC1\x33\xF0\xF6\xD1\xEE\
"\x13\x7C\x30\x2B\x32\x3C\xF7\x82\xDE\x67\x58\xE0\xB0\x33\x43\xC0\xEC\xF8\x8F\xE7"
"\x99\xC8\x43\x0D\x8B\xD8\x16\x88\x83\x17\xFF\xBE\xA2\x0F\x02\xCF\x9E\x07\x58\x66"
"\x83\xDF\xC1\x7C\x21\xD6\x1E\x05\x9F\x3C\xCC\xEF\xE7\x74\xEB\x3A\xC3\x08\xEA\x3C"
- "\x8C\x18\x30\x77\x8F\x71\xD3\xDE\xD3\xDA\x09\x59\xA1\x80\x99\xB0\xF1\x61\x68\xF7"
- "\x1D\x7B\x4C\x6F\x8F\x33\x01\x33\x61\xD6\xF8\x43\xC0\x21\xEE\x87\x34\x86\x1B\x0F"
- "\x03\x2C\x41\x37\x87\x8F\x33\x8C\xF7\x1D\x0B\xDE\xD5\xA0\x85\xC2\x91\xCB\x21\x86"
- "\xC3\xC0\x24\xF0\x90\x30\xD8\x08\x5C\x2A\x01\x1D\x7F\xB1\x34\x5F\x8F\x33\x96\x43"
- "\x0D\x80\x9B\xBA\x1E\x4D\xB0\x41\xC9\x8E\x83\x8E\x32\x04\x3E\x17\x4E\x56\x9F\x47"
- "\xD1\x02\x1D\x13\x90\x81\x0E\x89\xCD\x64\x08\xB4\x4E\x51\x02\x1D\x13\x9E\x20\x46"
- "\xC1\x8E\x59\x02\x27\x13\x87\x1B\x3E\x8F\xA3\xDC\x74\x2C\x39\x6C\xF6\x96\x0C\xB0"
- "\xF6\x8C\x8F\x33\xA1\xCB\x3D\xC7\xA1\xD8\x40\x83\xCA\x4C\xE1\x7C\xF4\x18\x7E\x1E"
- "\x83\x8F\xC3\xDE\x47\xA7\x86\x5F\x2F\x51\x90\x4C\xF8\x7D\x82\x16\xD4\x71\xFD\x9E"
- "\x0F\xB3\xF0\xFA\x2F\x1E\x87\x67\x86\x5F\x1F\x88\xF7\xCF\x43\xB0\x71\xF8\x7A\x1D"
- "\x83\x0F\xC9\xC2\xF9\xE9\xE0\xFF\xA3\x29\x51\x90\xC6\x7C\x3D\x94\xCD\x94\x76\x1A"
- "\xEC\xCE\xC1\x06\x91\xEC\x5E\xF8\x67\xC3\xD8\x2A\x2B\xA8\x67\x8F\x33\xB0\xEC\x17"
- "\xC3\x0D\x07\x8E\x81\xE0\xD3\xB0\xCF\x7C\x75\xF3\xA1\xFC\xF9\xA1\xD9\xEA\xBE\x12"
- "\xC2\xCE\x67\x60\xB1\xA2\x02\x3D\x73\xA0\xDD\xE3\xA1\xAF\xC7\xB0\xFC\x3D\x0E\xC0"
- "\x41\xCB\x0F\xC3\xD0\x4D\x33\x5A\x21\xF0\xF6\x0D\x32\x04\x2C\x2A\x01\xF6\x02\x17"
- "\x2A\x01\xC7\xB0\x13\x78\x9F\x30\x60\xC1\xE0\x10\xF8\x1C\x38\xD9\x02\x17\x32\xC7"
- "\x3E\xD9\x0C\x36\x02\x1F\x22\xC7\x31\xB2\x04\x4E\x3A\xC1\x1B\x98\xF0\xB4\x78\x55"
- "\x0F\x7E\xCC\x8F\x1F\x7E\xD3\x6B\x3C\xC7\x65\x0A\x3C\x1E\xC3\xF0\x85\xF5\x8E\x09"
- "\xAA\xC4\x16\x58\x88\xCF\x7C\x74\x35\xF8\xF4\x3B\x04\xD3\x33\xF0\x16\x78\x63\x3F"
- "\x0C\xEF\xE8\x3C\xEA\xBD\xE7\xF3\xE0\x98\x18\xB1\xAF\xA8\xE8\x3C\xE8\x98\x4C\x6B"
- "\xEA\x21\xC6\x45\xA2\x1D\xD0\x46\xE0\xC8\xEF\x1E\x0C\xEF\xEB\x06\x56\xE7\x78\xF8"
- "\x7B\x47\xBF\x82\xC6\x78\xF3\x3D\xB8\x79\x9E\xDF\x0A\xB1\x8C\xF3\x3D\x81\xEF\xC3"
- "\x09\x9E\xC3\xA8\x10\x78\x3D\x3D\x87\x90\x87\x37\x4F\x61\xEE\x3A\x8B\xE0\x89\x70"
- "\x76\x1B\x01\x16\xC9\x81\xC7\x3C\x7B\x0F\x71\xD4\x4C\x11\x2C\xB0\x82\xD1\x9E\x04"
- "\x6C\x6A\xC4\x30\x7B\x0F\x71\xEE\x3D\xC7\x83\x3B\xFA\x12\xEA\xCF\x87\xB6\x70\xBE"
- "\x08\x32\x41\x0B\x6C\x3E\x73\x1F\x46\x7B\xE3\xA1\x70\x20\xCC\x3B\xA0\x89\xC1\x49"
- "\xD4\x25\xD5\x9D\x40\x85\xC0\x29\xDE\x3C\x02\x27\x20\xC0\x87\xCB\xA9\xF9\xE7\x45"
- "\x5A\x35\xE0\xBA\x3B\xA6\x05\xF0\x75\xB9\xC7\x74\xEF\x1E\xD0\xB0\x3B\xAD\xCE\x3A"
- "\x7D\x85\x96\x21\xDD\x3B\xC7\x83\xDC\x75\x1C\x89\x32\x04\x8C\x78\x61\xF8\x7A\x1D"
- "\x83\x0F\xC3\xD0\xC6\x7C\x6A\xB0\xEB\x73\x8F\x87\xD9\xB4\x77\xCF\xB4\x35\xD0\xAC"
- "\x10\xF8\x7D\x8F\x3A\x3E\xCF\xC3\xD0\x70\xBA\xAC\xE3\xF0\xFA\xF1\xE8\x76\x02\x14"
- "\x73\xD0\xEC\x31\x9F\x1A\x7E\x4E\x17\xCF\x4A\xFA\x0C\x2B\xF7\x8F\x87\xD9\xB6\x84"
- "\x42\xAB\xE7\xD9\xF8\x7A\x50\x87\xE1\xE8\x39\x56\xD0\x4C\xF8\x7D\x9C\x64\x6C\x3E"
- "\x8E\x3C\x22\x36\x23\xEB\xC8\xEB\x47\xD7\x81\x07\xA0\x7E\x38\xFC\x3D\x0E\xCA\x10"
- "\xFC\x3D\x28\x43\xF0\xFA\xF0\x22\x47\x3D\x04\xD3\x30\x43\xC4\x88\x22\x35\x16\xA3"
- "\xEB\xC7\xD8\x21\xE7\x1E\xF6\x9F\x67\xE4\xE1\x7C\xF4\xD0\x42\x98\x7B\x07\x51\xEC"
- "\x04\x2C\x18\xF6\x1F\x42\x1F\x47\xD0\x22\x73\xFE\x75\x9D\x63\x82\x3C\xCF\xA1\x06"
- "\x1B\x0F\x61\xF8\x7A\x1D\x9A\x7E\x4E\x17\xCF\x4A\x10\x10\xEB\x82\x17\x40\x10\xFA"
- "\x38\xE8\x8D\x87\xD1\xC7\x44\x6C\x3E\x8E\x3A\x23\x61\xEC\x3F\x0F\xD1\xE4\x6C\x39"
- "\x08\x8C\x1C\xDD\xF1\xE0\xFA\x74\x42\x1F\x41\xCE\x17\xD0\x23\x67\xE6\xC0\x46\xCC"
- "\x83\x08\xF3\x3C\x8F\xA3\x8E\x88\x8D\x87\xD1\xC7\x44\x46\xC3\xE8\xE3\xA2\x23\x60"
- "\x20\xE7\x60\x91\x3C\x11\xF8\x67\x04\x3E\x18\xD0\x78\x17\x86\x5F\x1F\x0F\x61\xCC"
- "\x3D\x87\xE1\xFA\x3C\x96\x7B\xE7\x82\x9C\x2F\x82\x17\x45\x6C\x10\xB8\x1B\x20\xCA"
- "\x91\xF4\x21\xEC\x3F\x0F\x4F\x0D\xB0\x82\x3F\x0F\xD1\xE4\x71\x7D\x7C\xF0\x77\x0F"
- "\x43\xB0\x81\x06\x61\xF4\x21\x1A\x02\x17\x45\xB6\x70\xBE\x08\x5D\x06\x23\xB2\x84"
- "\x3F\x0F\xAF\x1E\xD6\x7B\x81\x32\x6C\xE1\x7C";
+ "\x8C\x18\x30\x77\x8F\x71\xD3\xDA\x7B\x41\x2B\x33\x30\x13\x36\x1E\x2C\x2D\x1E\xE3"
+ "\xAF\x69\x8D\xF1\xE6\x60\x26\x6C\x3A\xDF\x08\x78\x04\x3D\xCC\xE6\x90\xC3\x61\xE0"
+ "\x65\x88\x26\xF0\xF1\xE6\x71\x9E\xE3\xA1\x7B\x56\x82\x17\x0A\x07\x2C\x86\x1B\x0F"
+ "\x2A\x01\x93\xC2\x30\xC3\x60\x21\x6F\xC7\x5F\xEC\x4D\x17\xE3\xCC\xE5\x90\xC3\x60"
+ "\x26\xEE\x47\x91\xF4\x71\xF1\x1B\x0F\x71\xD3\xDA\x8E\x83\x8E\x32\x04\x3E\x16\xCE"
+ "\x56\x9F\x47\xD1\x02\x15\x03\x90\x81\x0E\x81\xCD\x64\x08\x94\x0E\x51\x02\x1D\x03"
+ "\x9E\x20\x45\xC1\x0E\x59\x02\x27\x12\xE7\x1B\x3E\x8F\xA3\xDC\x74\x2C\x39\x6C\xF6"
+ "\x96\x0C\xB0\xF6\x8C\x8F\x33\xA1\xCB\x3D\xC7\xA1\xD8\x40\x83\xCA\x24\xE1\x7C\xF4"
+ "\x18\x7E\x1E\x83\x8F\xC3\xDE\x47\xA7\x86\x5F\x2F\x51\x90\x4C\xF8\x7D\x82\x16\xCE"
+ "\x71\xFD\x9E\x0F\xB3\xF0\xFA\x2F\x1E\x87\x67\x86\x5F\x1F\x88\xF7\xCF\x43\xB0\x71"
+ "\xF8\x7A\x1D\x83\x0F\xC9\xC2\xF9\xE9\xE0\xFF\xA3\x29\x51\x90\xC6\x7C\x3D\x94\xCD"
+ "\x94\x76\x1A\xEC\xCE\xC1\x06\x91\xEC\x5E\xF8\x67\xC3\xD8\x2A\x2B\xA8\x67\x8F\x33"
+ "\xB0\xEC\x17\xC3\x0D\x07\x8E\x81\xE0\xD3\xB0\xCF\x7C\x75\xF3\xA1\xFC\xF9\xA1\xD9"
+ "\xEA\xBE\x12\xC2\xCE\x67\x60\xB1\xA2\x02\x3D\x73\xA0\xDD\xE3\xA1\xAF\xC7\xB0\xFC"
+ "\x3D\x0E\xC0\x41\xCB\x0F\xC3\xD0\x4D\x33\x5A\x21\xF0\xF6\x0D\x32\x04\x2C\x2A\x01"
+ "\xF6\x02\x17\x2A\x01\xC7\xB0\x13\x78\x9C\x30\x60\xC1\xE0\x10\xF8\x1C\x38\xD9\x02"
+ "\x17\x32\x27\x3E\xD9\x0C\x36\x02\x1F\x22\x47\x31\xB2\x04\x4E\x3A\x01\x1B\x98\xA0"
+ "\xB4\x78\x55\x0F\x7E\xCC\x8F\x1F\x7E\xD3\x6B\x3C\xC7\x65\x0A\x3C\x1E\xC3\xF0\x85"
+ "\xF5\x8E\x09\xAA\xC4\x16\x58\x88\xCF\x7C\x74\x35\xF8\xF4\x3B\x04\xD3\x33\xF0\x16"
+ "\x78\x63\x3F\x0C\xEF\xE8\x3C\xEA\xBD\xE7\xF3\xE0\x98\x18\xB1\xAF\xA8\xE8\x3C\xE8"
+ "\x98\x4C\x6B\xEA\x21\xC6\x45\xA2\x1D\xD0\x46\xE0\xC8\xEF\x1E\x0C\xEF\xEB\x06\x56"
+ "\xE7\x78\xF8\x7B\x47\xBF\x82\xC6\x78\xF3\x3D\xB8\x79\x9E\xDF\x0A\xB1\x8C\xF3\x3D"
+ "\x81\xEF\xC3\x09\x9E\xC3\xA8\x10\x78\x3D\x3D\x87\x90\x87\x37\x4F\x61\xEE\x3A\x8B"
+ "\xE0\x89\x70\x76\x1B\x01\x16\xC9\x81\xC7\x3C\x7B\x0F\x71\xD4\x4C\x11\x2C\xB0\x82"
+ "\xD1\x9E\x04\x6C\x6A\xC4\x30\x7B\x0F\x71\xEE\x3D\xC7\x83\x3B\xFA\x12\xEA\xCF\x87"
+ "\xB6\x70\xBE\x08\x32\x41\x0B\x6C\x3E\x73\x1F\x46\x7B\xE3\xA1\x70\x20\xCC\x3B\xA0"
+ "\x89\xC1\x49\xD4\x25\xD5\x9D\x40\x85\xC0\x29\xDE\x3C\x02\x27\x20\xC0\x87\xCB\xA9"
+ "\xF9\xE7\x45\x5A\x35\xE0\xBA\x3B\xA6\x05\xF0\x75\xB9\xC7\x74\xEF\x1E\xD0\xB0\x3B"
+ "\xAD\xCE\x3A\x7D\x85\x96\x21\xDD\x3B\xC7\x83\xDC\x75\x1C\x89\x32\x04\x8C\x78\x61"
+ "\xF8\x7A\x1D\x83\x0F\xC3\xD0\xC6\x7C\x6A\xB0\xEB\x73\x8F\x87\xD9\xB4\x77\xCF\xB4"
+ "\x35\xD0\xAC\x10\xF8\x7D\x8F\x3A\x3E\xCF\xC3\xD0\x70\xBA\xAC\xE3\xF0\xFA\xF1\xE8"
+ "\x76\x02\x14\x73\xD0\xEC\x31\x9F\x1A\x7E\x4E\x17\xCF\x4A\xFA\x0C\x2B\xF7\x8F\x87"
+ "\xD9\xB6\x84\x42\xAB\xE7\xD9\xF8\x7A\x50\x87\xE1\xE8\x39\x56\xD0\x4C\xF8\x7D\x9C"
+ "\x64\x6C\x3E\x8E\x3C\x22\x36\x23\xEB\xC8\xEB\x47\xD7\x81\x07\xA0\x7E\x38\xFC\x3D"
+ "\x0E\xCA\x10\xFC\x3D\x28\x43\xF0\xFA\xF0\x22\x47\x3D\x04\xD3\x30\x43\xC4\x88\x22"
+ "\x35\x16\xA3\xEB\xC7\xD8\x21\xE7\x1E\xD3\xEC\xFC\x9C\x2F\x9E\x9A\x08\x52\xCF\x60"
+ "\xEA\x3D\x80\x85\x82\x9E\xC3\xE8\x43\xE8\xFA\x04\x4E\x7F\x8E\xB3\xAC\x70\x47\x99"
+ "\xF4\x20\xC3\x61\xEC\x3F\x0F\x43\xB3\x4F\xC9\xC2\xF9\xE9\x42\x02\x1D\x70\x44\xE8"
+ "\xA7\x1C\xA2\x36\x1F\x47\x1D\x11\xB0\xFA\x38\xE8\x8D\x87\xB0\xFC\x3F\x47\x91\xB0"
+ "\xE4\x22\x30\x73\x77\xC7\x83\xE9\xD1\x08\x7D\x07\x38\x5F\x40\x8D\x9F\x9B\x01\x1B"
+ "\x32\x0C\x23\xCC\xF2\x3E\x8E\x3A\x22\x36\x1F\x47\x1D\x11\x1B\x0F\xA3\x8E\x88\x8D"
+ "\x80\x83\x9D\x82\x44\xF0\x47\xE1\x98\x10\xF8\x62\x41\xE0\x5E\x19\x7C\x7C\x3D\x87"
+ "\x30\xF6\x1F\x87\xE8\xF2\x59\xEF\x9E\x0A\x70\xBE\x08\x5D\x15\xA0\x42\xE0\x6C\x83"
+ "\x2A\x2B\x47\xD0\x87\xB0\xFC\x3D\x3C\x36\xC2\x08\xFC\x3F\x47\x91\xC5\xF5\xF3\xC1"
+ "\xDC\x3D\x0E\xC2\x04\x19\x87\xD0\x84\x68\x08\x5D\x16\xC9\xC2\xF8\x21\x74\x18\x4E"
+ "\xCA\x10\xFC\x3E\xBC\x7B\x59\xEE\x04\xC9\xB3\x85\xF3";
// ++++++++++++++++++++^^^^^^^^^^^^^^^^^^^++++++++++++++++++++
// ++++++++++++++++++++ DO NOT EDIT ABOVE ++++++++++++++++++++
@@ -1887,7 +1887,11 @@ extern "C" {
uint32_t convert_seconds_to_dhm(uint32_t seconds, char *unit, uint8_t *color){
static uint32_t conversions[3] = {24 * 3600, 3600, 60};
static char units[3] = { 'd', 'h', 'm'}; // day, hour, minute
- static uint8_t colors[3] = { 0x60, 0xA0, 0xEA};
+ uint8_t color_text_8 = WebColor(COL_TEXT) & 0xFF; // color of text on 8 bits
+ uint8_t color_back_8 = WebColor(COL_BACKGROUND) & 0xFF; // color of background on 8 bits
+ uint8_t colors[3] = { (uint8_t) changeUIntScale(6, 0, 16, color_back_8, color_text_8), // 6/16 of text
+ (uint8_t) changeUIntScale(10, 0, 16, color_back_8, color_text_8), // 10/16 of text color
+ color_text_8};
for(int i = 0; i < 3; ++i) {
*color = colors[i];
*unit = units[i];
@@ -1915,7 +1919,7 @@ void ZigbeeShow(bool json)
if (!zigbee_num) { return; }
if (zigbee_num > 255) { zigbee_num = 255; }
- WSContentSend_P(msg[ZB_WEB_CSS]);
+ WSContentSend_P(msg[ZB_WEB_CSS], WebColor(COL_TEXT));
// WSContentSend_compressed(ZB_WEB, 0);
// sort elements by name, then by id
From 805b69e289bdb0fca38354553ee51d84a41f0df4 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 29 Dec 2020 15:55:14 +0100
Subject: [PATCH 017/255] add tasmota-AF
---
.github/workflows/Tasmota_build_master.yml | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml
index 244bfe8ce..5d1453c60 100644
--- a/.github/workflows/Tasmota_build_master.yml
+++ b/.github/workflows/Tasmota_build_master.yml
@@ -230,6 +230,30 @@ jobs:
name: firmware
path: ./build_output/firmware
+
+ tasmota-AF:
+ needs: tasmota_pull
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota-AF
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
+
tasmota-BG:
needs: tasmota_pull
runs-on: ubuntu-latest
From 8fb6ba2bf7c7bfaf70c587f62e854b7ce6b08325 Mon Sep 17 00:00:00 2001
From: Jeroen Vermeulen - MageHost
Date: Tue, 29 Dec 2020 17:42:36 +0100
Subject: [PATCH 018/255] fix: ST7789 display driver for 135*240px
Improved the driver to support both 135*240 and 240*240 formats. These are the most common for the ST7789 display.
Fixes #10282
---
.../Arduino_ST7789.cpp | 68 ++++++++++++-------
.../Arduino_ST7789-gemu-1.0/Arduino_ST7789.h | 20 ++++--
tasmota/xdsp_12_ST7789.ino | 18 +++--
3 files changed, 72 insertions(+), 34 deletions(-)
diff --git a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
index f45b33095..74837242b 100644
--- a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
+++ b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
@@ -27,7 +27,7 @@ uint16_t Arduino_ST7789::GetColorFromIndex(uint8_t index) {
}
static const uint8_t PROGMEM
- cmd_240x240[] = { // Initialization commands for 7789 screens
+ init_cmd[] = { // Initialization commands for 7789 screens
10, // 9 commands in list:
ST7789_SWRESET, ST_CMD_DELAY, // 1: Software reset, no args, w/delay
150, // 150 ms delay
@@ -38,14 +38,6 @@ static const uint8_t PROGMEM
10, // 10 ms delay
ST7789_MADCTL , 1, // 4: Memory access ctrl (directions), 1 arg:
0x00, // Row addr/col addr, bottom to top refresh
- ST7789_CASET , 4, // 5: Column addr set, 4 args, no delay:
- 0x00, ST7789_240x240_XSTART, // XSTART = 0
- (ST7789_TFTWIDTH+ST7789_240x240_XSTART) >> 8,
- (ST7789_TFTWIDTH+ST7789_240x240_XSTART) & 0xFF, // XEND = 240
- ST7789_RASET , 4, // 6: Row addr set, 4 args, no delay:
- 0x00, ST7789_240x240_YSTART, // YSTART = 0
- (ST7789_TFTHEIGHT+ST7789_240x240_YSTART) >> 8,
- (ST7789_TFTHEIGHT+ST7789_240x240_YSTART) & 0xFF, // YEND = 240
ST7789_INVON , ST_CMD_DELAY, // 7: Inversion ON
10,
ST7789_NORON , ST_CMD_DELAY, // 8: Normal display on, no args, w/delay
@@ -75,7 +67,7 @@ inline uint16_t swapcolor(uint16_t x) {
// Constructor when using software SPI. All output pins are configurable.
Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t sid, int8_t sclk, int8_t cs, int8_t bp)
- : Renderer(ST7789_TFTWIDTH, ST7789_TFTHEIGHT)
+ : Renderer(_width, _height)
{
_cs = cs;
_dc = dc;
@@ -91,7 +83,7 @@ Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t sid, int8_t sclk, i
// Constructor when using hardware SPI. Faster, but must use SPI pins
// specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.)
Arduino_ST7789::Arduino_ST7789(int8_t dc, int8_t rst, int8_t cs, int8_t bp)
- : Renderer(ST7789_TFTWIDTH, ST7789_TFTHEIGHT) {
+ : Renderer(_width, _height) {
_cs = cs;
_dc = dc;
_rst = rst;
@@ -335,29 +327,59 @@ void Arduino_ST7789::setRotation(uint8_t m) {
case 0:
writedata(ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB);
- _xstart = _colstart;
- // _ystart = _rowstart;
- _ystart = 80;
+ _xstart = 0;
+ _ystart = 0;
+ if (_width==240 && _height==240) {
+ _xstart = ST7789_240x240_XSTART_R0;
+ _ystart = ST7789_240x240_YSTART_R0;
+ }
+ if (_width==135 && _height==240) {
+ _xstart = ST7789_135x240_XSTART_R0;
+ _ystart = ST7789_135x240_YSTART_R0;
+ }
break;
case 1:
writedata(ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
- _ystart = _colstart;
- // _xstart = _rowstart;
- _xstart = 80;
+ _ystart = 0;
+ _xstart = 0;
+ if (_width==240 && _height==240) {
+ _xstart = ST7789_240x240_XSTART_R1;
+ _ystart = ST7789_240x240_YSTART_R1;
+ }
+ if (_width==240 && _height==135) {
+ _xstart = ST7789_135x240_XSTART_R1;
+ _ystart = ST7789_135x240_YSTART_R1;
+ }
break;
case 2:
writedata(ST7789_MADCTL_RGB);
- _xstart = _colstart;
- _ystart = _rowstart;
+ _xstart = 0;
+ _ystart = 0;
+ if (_width==240 && _height==240) {
+ _xstart = ST7789_240x240_XSTART_R2;
+ _ystart = ST7789_240x240_YSTART_R2;
+ }
+ if (_width==135 && _height==240) {
+ _xstart = ST7789_135x240_XSTART_R2;
+ _ystart = ST7789_135x240_YSTART_R2;
+ }
break;
case 3:
writedata(ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
- _ystart = _colstart;
- _xstart = _rowstart;
+ _xstart = 0;
+ _ystart = 0;
+ if (_width==240 && _height==240) {
+ _xstart = ST7789_240x240_XSTART_R3;
+ _ystart = ST7789_240x240_YSTART_R3;
+ }
+ if (_width==240 && _height==135) {
+ _xstart = ST7789_135x240_XSTART_R3;
+ _ystart = ST7789_135x240_YSTART_R3;
+ }
break;
}
}
@@ -533,12 +555,10 @@ inline void Arduino_ST7789::DC_LOW(void) {
void Arduino_ST7789::init(uint16_t width, uint16_t height) {
commonInit(NULL);
- _colstart = ST7789_240x240_XSTART;
- _rowstart = ST7789_240x240_YSTART;
_height = height;
_width = width;
- displayInit(cmd_240x240);
+ displayInit(init_cmd);
setRotation(2);
diff --git a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
index 2d97346e7..3dfaa03a8 100644
--- a/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
+++ b/lib/lib_display/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
@@ -39,11 +39,23 @@
//#define SPI_HAS_TRANSACTION // already defined in SPI.h
-#define ST7789_TFTWIDTH 240
-#define ST7789_TFTHEIGHT 240
+#define ST7789_240x240_XSTART_R0 0
+#define ST7789_240x240_YSTART_R0 80
+#define ST7789_240x240_XSTART_R1 80
+#define ST7789_240x240_YSTART_R1 0
+#define ST7789_240x240_XSTART_R2 0
+#define ST7789_240x240_YSTART_R2 0
+#define ST7789_240x240_XSTART_R3 0
+#define ST7789_240x240_YSTART_R3 0
-#define ST7789_240x240_XSTART 0
-#define ST7789_240x240_YSTART 0
+#define ST7789_135x240_XSTART_R0 53
+#define ST7789_135x240_YSTART_R0 40
+#define ST7789_135x240_XSTART_R1 40
+#define ST7789_135x240_YSTART_R1 52
+#define ST7789_135x240_XSTART_R2 52
+#define ST7789_135x240_YSTART_R2 40
+#define ST7789_135x240_XSTART_R3 40
+#define ST7789_135x240_YSTART_R3 53
#define ST_CMD_DELAY 0x80 // special signifier for command lists
diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/xdsp_12_ST7789.ino
index 3c85f168a..1c9ec1aff 100644
--- a/tasmota/xdsp_12_ST7789.ino
+++ b/tasmota/xdsp_12_ST7789.ino
@@ -64,11 +64,11 @@ void ST7789_InitDriver()
if (XDSP_12 == Settings.display_model) {
- if (Settings.display_width != ST7789_TFTWIDTH) {
- Settings.display_width = ST7789_TFTWIDTH;
+ if (!Settings.display_width) {
+ Settings.display_width = 240;
}
- if (Settings.display_height != ST7789_TFTHEIGHT) {
- Settings.display_height = ST7789_TFTHEIGHT;
+ if (!Settings.display_height) {
+ Settings.display_height = 240;
}
// disable screen buffer
@@ -130,9 +130,15 @@ void ST7789_InitDriver()
#ifdef SHOW_SPLASH
// Welcome text
- renderer->setTextFont(2);
renderer->setTextColor(ST7789_WHITE,ST7789_BLACK);
- renderer->DrawStringAt(30, 100, "ST7789 TFT!", ST7789_WHITE,0);
+ int fontSize = 2;
+ renderer->setTextFont(2);
+ if (Settings.display_width<240) {
+ fontSize = 1;
+ }
+ renderer->setTextFont(fontSize);
+ int fontHeight = 12 * fontSize;
+ renderer->DrawStringAt(30, (Settings.display_height-fontHeight)/2, "ST7789 TFT!", ST7789_WHITE,0);
delay(1000);
#endif
From f004cfea60928a723419b35a618fce26fb30c1c1 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 29 Dec 2020 17:42:53 +0100
Subject: [PATCH 019/255] Breaking change regarding MFRC522 and ILI9341
- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
- Replaced ILI9341 GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_ILI9341_CS`` and ``GPIO_SPI_DC`` by ``GPIO_ILI9341_DC``
---
CHANGELOG.md | 7 ++-
RELEASENOTES.md | 6 +-
platformio_tasmota32.ini | 1 +
platformio_tasmota_env32.ini | 8 +++
tasmota/support.ino | 10 +++-
tasmota/support_tasmota.ino | 62 ++++++++++++++-------
tasmota/tasmota_configurations_ESP32.h | 20 ++++++-
tasmota/tasmota_template.h | 76 ++++++++++++++------------
tasmota/tasmota_version.h | 2 +-
tasmota/xdrv_80_odroidgo.ino | 34 ++++++++++++
tasmota/xdsp_04_ili9341.ino | 13 ++++-
tasmota/xsns_80_mfrc522.ino | 6 +-
12 files changed, 182 insertions(+), 63 deletions(-)
create mode 100644 tasmota/xdrv_80_odroidgo.ino
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 706721d0b..4e45bf32e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,12 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
-## [9.2.0.1]
+## [9.2.0.2]
+### Breaking Changed
+- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
+- Replaced ILI9341 GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_ILI9341_CS`` and ``GPIO_SPI_DC`` by ``GPIO_ILI9341_DC``
+
+## [9.2.0.1] 20201229
### Added
- Milliseconds to console output (#10152)
- Support for P9813 RGB Led MOSFET controller (#10104)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index b9ed48538..1f9677073 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -56,7 +56,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
[Complete list](BUILDS.md) of available feature and sensors.
-## Changelog v9.2.0.1
+## Changelog v9.2.0.2
### Added
- Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152)
- Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196)
@@ -69,6 +69,10 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for IR inverted leds using ``#define IR_SEND_INVERTED true`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
+### Breaking Changed
+- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
+- Replaced ILI9341 GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_ILI9341_CS`` and ``GPIO_SPI_DC`` by ``GPIO_ILI9341_DC``
+
### Changed
- Logging from heap to stack freeing 700 bytes RAM
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index 1fe7667a2..430fcc344 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -8,6 +8,7 @@ default_envs = ${build_envs.default_envs}
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota32
; tasmota32-webcam
+; tasmota32-odroidgo
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini
index ff416538e..886b2cfaf 100644
--- a/platformio_tasmota_env32.ini
+++ b/platformio_tasmota_env32.ini
@@ -36,6 +36,14 @@ board_build.f_cpu = 240000000L
build_flags = ${common32.build_flags} -DFIRMWARE_WEBCAM
lib_extra_dirs = lib/libesp32, lib/lib_basic
+[env:tasmota32-odroidgo]
+extends = env:tasmota32
+board = odroid_esp32
+board_build.f_cpu = 160000000L
+board_build.partitions = esp32_partition_app1984k_ffat12M.csv
+build_flags = ${common32.build_flags} -DFIRMWARE_ODROID_GO
+lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display
+
[env:tasmota32-minimal]
extends = env:tasmota32
build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 577c8bb95..fcf21a014 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -1307,7 +1307,7 @@ uint8_t ModuleNr(void)
uint32_t ModuleTemplate(uint32_t module) {
uint32_t i = 0;
for (i = 0; i < sizeof(kModuleNiceList); i++) {
- if (Settings.module == pgm_read_byte(kModuleNiceList + i)) {
+ if (module == pgm_read_byte(kModuleNiceList + i)) {
break;
}
}
@@ -1346,6 +1346,9 @@ String AnyModuleName(uint32_t index)
if (USER_MODULE == index) {
return String(SettingsText(SET_TEMPLATE_NAME));
} else {
+#ifdef ESP32
+ index = ModuleTemplate(index);
+#endif
char name[TOPSZ];
return String(GetTextIndexed(name, sizeof(name), index, kModuleNames));
}
@@ -1451,6 +1454,11 @@ void ModuleDefault(uint32_t module)
{
if (USER_MODULE == module) { module = WEMOS; } // Generic
Settings.user_template_base = module;
+
+#ifdef ESP32
+ module = ModuleTemplate(module);
+#endif
+
char name[TOPSZ];
SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames));
#ifdef ESP8266
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
index bd92688f6..58b79e905 100644
--- a/tasmota/support_tasmota.ino
+++ b/tasmota/support_tasmota.ino
@@ -1594,24 +1594,47 @@ void GpioInit(void)
#ifdef ESP8266
if ((2 == Pin(GPIO_TXD)) || (H801 == TasmotaGlobal.module_type)) { Serial.set_tx(2); }
+#endif
+
+ TasmotaGlobal.soft_spi_enabled = (PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO)));
#ifdef USE_SPI
- TasmotaGlobal.spi_enabled = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12)));
- if (TasmotaGlobal.spi_enabled) {
- TasmotaGlobal.my_module.io[12] = AGPIO(GPIO_SPI_MISO);
- SetPin(12, AGPIO(GPIO_SPI_MISO));
- TasmotaGlobal.my_module.io[13] = AGPIO(GPIO_SPI_MOSI);
- SetPin(13, AGPIO(GPIO_SPI_MOSI));
- TasmotaGlobal.my_module.io[14] = AGPIO(GPIO_SPI_CLK);
- SetPin(14, AGPIO(GPIO_SPI_CLK));
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK)"));
+ uint32_t pin_cs = Pin(GPIO_SPI_CS);
+ uint32_t pin_dc = Pin(GPIO_SPI_DC);
+ if (PinUsed(GPIO_RC522_CS)) {
+ pin_cs = Pin(GPIO_RC522_CS);
+ }
+ if (PinUsed(GPIO_ILI9341_CS)) {
+ pin_cs = Pin(GPIO_ILI9341_CS);
+ if (PinUsed(GPIO_ILI9341_DC)) {
+ pin_dc = Pin(GPIO_ILI9341_DC);
+ }
+ }
+
+#ifdef ESP8266
+ if (!TasmotaGlobal.soft_spi_enabled) {
+ // If SPI_CS is used it must be valid
+ TasmotaGlobal.spi_enabled = ((pin_cs < 99) && ((pin_cs > 14) || (pin_cs < 12)));
+ if (TasmotaGlobal.spi_enabled && (pin_dc < 99)) {
+ // If SPI_DC is used it must be valid
+ TasmotaGlobal.spi_enabled = ((pin_dc > 14) || (pin_dc < 12));
+ }
+ if (TasmotaGlobal.spi_enabled) {
+ TasmotaGlobal.my_module.io[12] = AGPIO(GPIO_SPI_MISO);
+ SetPin(12, AGPIO(GPIO_SPI_MISO));
+ TasmotaGlobal.my_module.io[13] = AGPIO(GPIO_SPI_MOSI);
+ SetPin(13, AGPIO(GPIO_SPI_MOSI));
+ TasmotaGlobal.my_module.io[14] = AGPIO(GPIO_SPI_CLK);
+ SetPin(14, AGPIO(GPIO_SPI_CLK));
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK)"));
+ }
}
-#endif // USE_SPI
#endif // ESP8266
#ifdef ESP32
-#ifdef USE_SPI
- if (PinUsed(GPIO_SPI_CS) || PinUsed(GPIO_SPI_DC)) {
- if ((15 == Pin(GPIO_SPI_CS)) && (!GetPin(12) && !GetPin(13) && !GetPin(14))) { // HSPI
+ if (pin_cs < 99) {
+/*
+ // Do not do this as ESP32 can have SPI_CS everywhere
+ if ((15 == pin_cs) && (!GetPin(12) && !GetPin(13) && !GetPin(14))) { // HSPI
TasmotaGlobal.my_module.io[12] = AGPIO(GPIO_SPI_MISO);
SetPin(12, AGPIO(GPIO_SPI_MISO));
TasmotaGlobal.my_module.io[13] = AGPIO(GPIO_SPI_MOSI);
@@ -1619,7 +1642,7 @@ void GpioInit(void)
TasmotaGlobal.my_module.io[14] = AGPIO(GPIO_SPI_CLK);
SetPin(14, AGPIO(GPIO_SPI_CLK));
}
- else if ((5 == Pin(GPIO_SPI_CS)) && (!GetPin(19) && !GetPin(23) && !GetPin(18))) { // VSPI
+ else if ((5 == pin_cs) && (!GetPin(19) && !GetPin(23) && !GetPin(18))) { // VSPI
TasmotaGlobal.my_module.io[19] = AGPIO(GPIO_SPI_MISO);
SetPin(19, AGPIO(GPIO_SPI_MISO));
TasmotaGlobal.my_module.io[23] = AGPIO(GPIO_SPI_MOSI);
@@ -1644,24 +1667,25 @@ void GpioInit(void)
SetPin(18, AGPIO(GPIO_SPI_CLK));
}
TasmotaGlobal.spi_enabled = (PinUsed(GPIO_SPI_CLK) && (PinUsed(GPIO_SPI_MOSI) || PinUsed(GPIO_SPI_MISO)));
+*/
+ TasmotaGlobal.spi_enabled = (pin_cs < 99);
if (TasmotaGlobal.spi_enabled) {
- if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO)) {
+ if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO) && PinUsed(GPIO_SPI_CLK)) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MISO), GPIO%02d(MOSI) and GPIO%02d(CLK)"),
Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_CLK));
}
- else if (PinUsed(GPIO_SPI_MOSI)) {
+ else if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MOSI) and GPIO%02d(CLK)"),
Pin(GPIO_SPI_MOSI), Pin(GPIO_SPI_CLK));
}
- else if (PinUsed(GPIO_SPI_MISO)) {
+ else if (PinUsed(GPIO_SPI_MISO) && PinUsed(GPIO_SPI_CLK)) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("SPI: Using GPIO%02d(MISO) and GPIO%02d(CLK)"),
Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_CLK));
}
}
}
-#endif // USE_SPI
#endif // ESP32
- TasmotaGlobal.soft_spi_enabled = (PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO)));
+#endif // USE_SPI
for (uint32_t i = 0; i < ARRAY_SIZE(TasmotaGlobal.my_module.io); i++) {
uint32_t mpin = ValidPin(i, TasmotaGlobal.my_module.io[i]);
diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h
index 86e27a534..c3bb7044b 100644
--- a/tasmota/tasmota_configurations_ESP32.h
+++ b/tasmota/tasmota_configurations_ESP32.h
@@ -33,9 +33,27 @@
#define CODE_IMAGE_STR "webcam"
#define USE_WEBCAM
-#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#endif // FIRMWARE_WEBCAM
+/*********************************************************************************************\
+ * [tasmota32-odroidgo.bin]
+ * Provide an image with useful supported sensors enabled
+\*********************************************************************************************/
+
+#ifdef FIRMWARE_ODROID_GO
+
+#undef CODE_IMAGE_STR
+#define CODE_IMAGE_STR "odroid-go"
+
+#define USE_ODROID_GO // Add support for Odroid Go
+#define USE_ADC
+#define USE_SPI
+ #define USE_DISPLAY // Add SPI Display Support (+2k code)
+ #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code)
+#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#endif // FIRMWARE_ODROID_GO
+
#endif // ESP32
#endif // _TASMOTA_CONFIGURATIONS_ESP32_H_
diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h
index 5f1eafb8e..46915f280 100644
--- a/tasmota/tasmota_template.h
+++ b/tasmota/tasmota_template.h
@@ -359,19 +359,23 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#endif
#ifdef USE_SPI
- AGPIO(GPIO_SPI_MISO), // SPI MISO
- AGPIO(GPIO_SPI_MOSI), // SPI MOSI
- AGPIO(GPIO_SPI_CLK), // SPI Clk
- AGPIO(GPIO_SPI_CS), // SPI Chip Select
- AGPIO(GPIO_SPI_DC), // SPI Data Direction
+ AGPIO(GPIO_SPI_MISO), // SPI MISO
+ AGPIO(GPIO_SPI_MOSI), // SPI MOSI
+ AGPIO(GPIO_SPI_CLK), // SPI Clk
+ AGPIO(GPIO_SPI_CS), // SPI Chip Select
+ AGPIO(GPIO_SPI_DC), // SPI Data Direction
#ifdef USE_NRF24
// AGPIO(GPIO_NRF24_CS),
// AGPIO(GPIO_NRF24_DC),
#endif
+#ifdef USE_RC522
+ AGPIO(GPIO_RC522_CS), // RC522 Rfid Chip Select
+ AGPIO(GPIO_RC522_RST), // RC522 Rfid Reset
+#endif
#ifdef USE_DISPLAY
#ifdef USE_DISPLAY_ILI9341
-// AGPIO(GPIO_ILI9341_CS),
-// AGPIO(GPIO_ILI9341_DC),
+ AGPIO(GPIO_ILI9341_CS),
+ AGPIO(GPIO_ILI9341_DC),
#endif // USE_DISPLAY_ILI9341
#endif // USE_DISPLAY
#endif // USE_SPI
@@ -710,10 +714,6 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_MIEL_HVAC_TX), // Mitsubishi Electric HVAC TX pin
AGPIO(GPIO_MIEL_HVAC_RX), // Mitsubishi Electric HVAC RX pin
#endif
-#ifdef USE_RC522
- AGPIO(GPIO_RC522_RST), // RC522 Rfid reset
-// AGPIO(GPIO_RC522_CS), // RC522 Rfid chip select
-#endif
/*-------------------------------------------------------------------------------------------*\
* ESP32 specifics
@@ -2319,7 +2319,9 @@ const char kModuleNames[] PROGMEM =
#ifdef USE_WEBCAM
"ESP32-Cam|"
#endif // USE_WEBCAM
-// "Odroid Go|""
+#ifdef USE_ODROID_GO
+ "Odroid Go|"
+#endif // USE_ODROID_GO
// "ESP32-Solo|"
// "WT32-Eth01|"
// "TTGO Watch|"
@@ -2334,7 +2336,9 @@ const uint8_t kModuleNiceList[] PROGMEM = {
#ifdef USE_WEBCAM
ESP32_CAM_AITHINKER,
#endif // USE_WEBCAM
-// ODROID_GO,
+#ifdef USE_ODROID_GO
+ ODROID_GO,
+#endif // USE_ODROID_GO
// ESP32_SOLO,
// WT32_ETH01,
// TTGO_WATCH,
@@ -2435,46 +2439,46 @@ const mytmplt kModules[] PROGMEM =
#endif // USE_WEBCAM
#ifdef USE_ODROID_GO
{ // ODROID_GO - (ESP32)
- AGPIO(GPIO_KEY1), // 0 (I)O GPIO0, Button1
- AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2
- AGPIO(GPIO_USER), // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0
- AGPIO(GPIO_USER), // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2
+ AGPIO(GPIO_KEY1), // 0 (I)O GPIO0, BTN-VOLUME
+ AGPIO(GPIO_TXD), // 1 IO TXD0 GPIO1, TXD0
+ AGPIO(GPIO_LEDLNK), // 2 IO GPIO2, STATUS LED
+ AGPIO(GPIO_RXD), // 3 IO RXD0 GPIO3, RXD0
AGPIO(GPIO_USER), // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER
- AGPIO(GPIO_USER), // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK
+ AGPIO(GPIO_ILI9341_CS), // 5 IO GPIO5, VSPI_CS0_LCD
// 6 IO GPIO6, Flash CLK
// 7 IO GPIO7, Flash D0
// 8 IO GPIO8, Flash D1
- AGPIO(GPIO_USER), // 9 IO GPIO9, Flash D2, U1RXD
- AGPIO(GPIO_USER), // 10 IO GPIO10, Flash D3, U1TXD
+ 0, // 9 IO GPIO9, Flash D2, U1RXD
+ 0, // 10 IO GPIO10, Flash D3, U1TXD
// 11 IO GPIO11, Flash CMD
AGPIO(GPIO_USER), // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.)
- AGPIO(GPIO_USER), // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER
- AGPIO(GPIO_USER), // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2
+ AGPIO(GPIO_KEY1) +1, // 13 IO GPIO13, BTN-MENU
+ AGPIO(GPIO_PWM1), // 14 IO GPIO14, LCD Backlight
AGPIO(GPIO_USER), // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.)
AGPIO(GPIO_USER), // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT
AGPIO(GPIO_USER), // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180
- AGPIO(GPIO_USER), // 18 IO GPIO18, VSPICLK, HS1_DATA7
- AGPIO(GPIO_USER), // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0
+ AGPIO(GPIO_SPI_CLK), // 18 IO GPIO18, VSPI_CLK
+ AGPIO(GPIO_SPI_MISO), // 19 IO GPIO19, VSPI_MISO
0, // 20
- AGPIO(GPIO_USER), // 21 IO GPIO21, VSPIHD, EMAC_TX_EN
- AGPIO(GPIO_USER), // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1
- AGPIO(GPIO_USER), // 23 IO GPIO23, VSPID, HS1_STROBE
+ AGPIO(GPIO_ILI9341_DC), // 21 IO GPIO21, SPI_DC_LCD
+ 0, // 22 IO LED GPIO22, VSPI_CS1_TFLASH
+ AGPIO(GPIO_SPI_MOSI), // 23 IO GPIO23, VSPI_MOSI
0, // 24
- AGPIO(GPIO_USER), // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0
- AGPIO(GPIO_USER), // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1
- AGPIO(GPIO_USER), // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV
+ 0, // 25 IO GPIO25, DAC_1 (PAM8304A)
+ 0, // 26 IO GPIO26, DAC_2 (PAM8304A)
+ AGPIO(GPIO_KEY1) +2, // 27 IO GPIO27, BTN-SELECT
0, // 28
0, // 29
0, // 30
0, // 31
- AGPIO(GPIO_USER), // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9
- AGPIO(GPIO_USER), // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8
- AGPIO(GPIO_USER), // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4
- AGPIO(GPIO_USER), // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5
- AGPIO(GPIO_USER), // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0
+ AGPIO(GPIO_SWT1) +4, // 32 IO GPIO32, BTN-A
+ AGPIO(GPIO_SWT1) +5, // 33 IO GPIO33, BTN-B
+ AGPIO(GPIO_ADC_JOY), // 34 I NO PULLUP GPIO34, JOY-X (LEFT-RIGHT)
+ AGPIO(GPIO_ADC_JOY) +1, // 35 I NO PULLUP GPIO35, JOY-Y (UP-DOWN)
+ AGPIO(GPIO_ADC_RANGE) +2, // 36 I NO PULLUP GPIO36, SENSOR_VP (BATTERY CARGER)
0, // 37 NO PULLUP
0, // 38 NO PULLUP
- AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3
+ AGPIO(GPIO_KEY1) +3, // 39 I NO PULLUP GPIO39, BTN-START
0 // Flag
},
#endif // USE_ODROID_GO
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index d4fbfcf76..490f68bd8 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x09020001;
+const uint32_t VERSION = 0x09020002;
#endif // _TASMOTA_VERSION_H_
diff --git a/tasmota/xdrv_80_odroidgo.ino b/tasmota/xdrv_80_odroidgo.ino
new file mode 100644
index 000000000..4886f87d6
--- /dev/null
+++ b/tasmota/xdrv_80_odroidgo.ino
@@ -0,0 +1,34 @@
+/*
+ xdrv_81_webcam.ino - ESP32 webcam support for Tasmota
+
+ Copyright (C) 2020 Gerhard Mutz and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef ESP32
+#ifdef USE_ODROID_GO
+/*********************************************************************************************\
+ * Odroid Go
+ *
+ * Clock frequency 160MHz (board_build.f_cpu = 160000000L)
+ * SPI Flash Size = 16MB (board_build.partitions = esp32_partition_app1984k_ffat12M.csv)
+ *
+ * To be done:
+ * - Audio on GPIO25/26
+ *
+/*********************************************************************************************/
+
+#endif // USE_ODROID_GO
+#endif // ESP32
diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino
index fd61b4a82..adabfe6b0 100644
--- a/tasmota/xdsp_04_ili9341.ino
+++ b/tasmota/xdsp_04_ili9341.ino
@@ -97,7 +97,18 @@ void Ili9341Init(uint8_t mode)
void Ili9341InitDriver(void)
{
+ uint32_t pin_cs = Pin(GPIO_SPI_CS);
+ uint32_t pin_dc = Pin(GPIO_SPI_DC);
if (!Settings.display_model) {
+ if (PinUsed(GPIO_ILI9341_CS)) {
+ pin_cs = Pin(GPIO_ILI9341_CS);
+ if (PinUsed(GPIO_ILI9341_DC)) {
+ pin_dc = Pin(GPIO_ILI9341_DC);
+ }
+ Settings.display_model = XDSP_04;
+ }
+
+ // Legacy
Settings.display_model = XDSP_04;
}
@@ -109,7 +120,7 @@ void Ili9341InitDriver(void)
Settings.display_height = ILI9341_TFTHEIGHT;
}
- tft = new Adafruit_ILI9341(Pin(GPIO_SPI_CS), Pin(GPIO_SPI_DC));
+ tft = new Adafruit_ILI9341(pin_cs, pin_dc);
tft->begin();
#ifdef USE_DISPLAY_MODES1TO5
diff --git a/tasmota/xsns_80_mfrc522.ino b/tasmota/xsns_80_mfrc522.ino
index 2c970119f..899c77909 100644
--- a/tasmota/xsns_80_mfrc522.ino
+++ b/tasmota/xsns_80_mfrc522.ino
@@ -17,6 +17,7 @@
along with this program. If not, see .
*/
+#ifdef USE_SPI
#ifdef USE_RC522
/*********************************************************************************************\
* MFRC522 - 13.56 MHz RFID reader
@@ -97,8 +98,8 @@ void RC522ScanForTag(void) {
}
void RC522Init(void) {
- if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_RC522_RST)) {
- Mfrc522 = new MFRC522(Pin(GPIO_SPI_CS), Pin(GPIO_RC522_RST));
+ if (PinUsed(GPIO_RC522_CS) && PinUsed(GPIO_RC522_RST)) {
+ Mfrc522 = new MFRC522(Pin(GPIO_RC522_CS), Pin(GPIO_RC522_RST));
SPI.begin();
Mfrc522->PCD_Init();
// if (Mfrc522->PCD_PerformSelfTest()) { // Saves 0k5 code
@@ -155,3 +156,4 @@ bool Xsns80(uint8_t function) {
}
#endif // USE_RC522
+#endif // USE_SPI
From 01647b96dece90cb68dc6aaed371e0e71bc7e1fb Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 29 Dec 2020 17:52:25 +0100
Subject: [PATCH 020/255] Forgot the workflows for odroid-go
---
.github/workflows/CI_github_ESP32.yml | 20 +++++++++++++++++
.github/workflows/Tasmota_build.yml | 23 ++++++++++++++++++++
.github/workflows/Tasmota_build_master.yml | 25 +++++++++++++++++++++-
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml
index a36d747dc..ecf8bf561 100644
--- a/.github/workflows/CI_github_ESP32.yml
+++ b/.github/workflows/CI_github_ESP32.yml
@@ -44,6 +44,26 @@ jobs:
name: firmware
path: ./build_output/firmware
+ tasmota32-odroidgo:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-odroidgo
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
tasmota32-minimal:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml
index 1507fc2c2..fe06a70d2 100644
--- a/.github/workflows/Tasmota_build.yml
+++ b/.github/workflows/Tasmota_build.yml
@@ -875,6 +875,29 @@ jobs:
path: ./build_output/firmware
+ tasmota32-odroidgo:
+ needs: tasmota_pull
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-odroidgo
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
+
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml
index 5d1453c60..4169bd028 100644
--- a/.github/workflows/Tasmota_build_master.yml
+++ b/.github/workflows/Tasmota_build_master.yml
@@ -875,6 +875,29 @@ jobs:
path: ./build_output/firmware
+ tasmota32-odroidgo:
+ needs: tasmota_pull
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-odroidgo
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
+
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
@@ -1544,7 +1567,7 @@ jobs:
[ ! -f ./mv_firmware/tasmota-ir*.* ] || mv ./mv_firmware/tasmota-ir*.* ./firmware/tasmota/
[ ! -f ./mv_firmware/tasmota-display.* ] || mv ./mv_firmware/tasmota-display.* ./firmware/tasmota/
[ ! -f ./mv_firmware/tasmota-knx.* ] || mv ./mv_firmware/tasmota-knx.* ./firmware/tasmota/
- [ ! -f ./mv_firmware/tasmota-zbbridge.* ] || mv ./mv_firmware/tasmota-zbbridge.* ./firmware/tasmota/
+ [ ! -f ./mv_firmware/tasmota-zbbridge.* ] || mv ./mv_firmware/tasmota-zbbridge.* ./firmware/tasmota/
[ ! -f ./mv_firmware/tasmota32.* ] || mv ./mv_firmware/tasmota32.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-sensors.* ] || mv ./mv_firmware/tasmota32-sensors.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-minimal.* ] || mv ./mv_firmware/tasmota32-minimal.* ./firmware/tasmota32/
From 37bd2a99f60092ef0419af6bfbc33c357c2c1c62 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 29 Dec 2020 18:03:05 +0100
Subject: [PATCH 021/255] Add basic support for ESP32 Odroid Go 16MB binary
tasmota32-odroidgo.bin
Add basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
---
CHANGELOG.md | 3 +++
RELEASENOTES.md | 1 +
platformio_tasmota_env32.ini | 2 +-
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e45bf32e..bd49bc4c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
## [9.2.0.2]
+### Added
+- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
+
### Breaking Changed
- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
- Replaced ILI9341 GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_ILI9341_CS`` and ``GPIO_SPI_DC`` by ``GPIO_ILI9341_DC``
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 1f9677073..6f02fa36f 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -68,6 +68,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for Afrikaans language translations by Christiaan Heerze
- Support for IR inverted leds using ``#define IR_SEND_INVERTED true`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
+- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
### Breaking Changed
- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini
index 886b2cfaf..bad0b800a 100644
--- a/platformio_tasmota_env32.ini
+++ b/platformio_tasmota_env32.ini
@@ -39,7 +39,7 @@ lib_extra_dirs = lib/libesp32, lib/lib_basic
[env:tasmota32-odroidgo]
extends = env:tasmota32
board = odroid_esp32
-board_build.f_cpu = 160000000L
+board_build.f_cpu = 240000000L
board_build.partitions = esp32_partition_app1984k_ffat12M.csv
build_flags = ${common32.build_flags} -DFIRMWARE_ODROID_GO
lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display
From 488712c3f0ace7ccb3d6d12dc0c721c7e6e25597 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Tue, 29 Dec 2020 19:31:27 +0100
Subject: [PATCH 022/255] Commands `CTRange` and `VirtualCT`
---
CHANGELOG.md | 2 +
tasmota/i18n.h | 2 +
tasmota/my_user_config.h | 2 +
tasmota/support_command.ino | 5 +
tasmota/tasmota_configurations.h | 2 +
tasmota/xdrv_04_light.ino | 463 ++++++++++++++++++++++---------
6 files changed, 345 insertions(+), 131 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd49bc4c8..d542934fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file.
## [9.2.0.2]
### Added
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
+- Command ``CTRange`` to specify the visible CT range the bulb is capable of
+- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels
### Breaking Changed
- Replaced MFRC522 13.56MHz rfid card reader GPIO selection from ``GPIO_SPI_CS`` by ``GPIO_RC522_CS``
diff --git a/tasmota/i18n.h b/tasmota/i18n.h
index 17cd2fe53..3fd70be4e 100644
--- a/tasmota/i18n.h
+++ b/tasmota/i18n.h
@@ -425,6 +425,8 @@
#define D_CMND_DIMMER_RANGE "DimmerRange"
#define D_CMND_DIMMER_STEP "DimmerStep"
#define D_CMND_HSBCOLOR "HSBColor"
+#define D_CMND_VIRTUALCT "VirtualCT"
+#define D_CMND_CTRANGE "CTRange"
#define D_CMND_LED "Led"
#define D_CMND_LEDTABLE "LedTable"
#define D_CMND_FADE "Fade"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 7c354c8bf..cc75735cf 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -309,6 +309,7 @@
#define LIGHT_WHITE_BLEND_MODE false // [SetOption105] White Blend Mode - used to be `RGBWWTable` last value `0`, now deprecated in favor of this option
#define LIGHT_VIRTUAL_CT false // [SetOption106] Virtual CT - Creates a virtual White ColorTemp for RGBW lights
#define LIGHT_VIRTUAL_CT_CW false // [SetOption107] Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
+#define LIGHT_VIRTUAL_CT_POINTS 3 // Number of reference points for Virtual CT (min 2, default 3)
// -- Energy --------------------------------------
#define ENERGY_VOLTAGE_ALWAYS false // [SetOption21] Enable show voltage even if powered off
@@ -494,6 +495,7 @@
#define USE_SONOFF_L1 // Add support for Sonoff L1 led control
#define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code)
#define USE_LIGHT_PALETTE // Add support for color palette (+0k7 code)
+#define USE_LIGHT_VIRTUAL_CT // Add support for Virtual White Color Temperature (+1.1k code)
#define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code)
// -- Counter input -------------------------------
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 34a3bd2e3..bc1121123 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -941,6 +941,11 @@ void CmndSetoption(void)
else if (4 == ptype) { // SetOption82 .. 113
bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload);
switch (pindex) {
+#ifdef USE_LIGHT
+ case 0: // SetOption 82 - (Alexa) Reduced CT range for Alexa (1)
+ setAlexaCTRange();
+ break;
+#endif
case 3: // SetOption85 - Enable Device Groups
case 6: // SetOption88 - PWM Dimmer Buttons control remote devices
case 15: // SetOption97 - Set Baud rate for TuyaMCU serial communication (0 = 9600 or 1 = 115200)
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index 52bd19c58..6e4169e82 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -508,6 +508,7 @@
// -- Optional light modules ----------------------
//#undef USE_LIGHT // Enable Dimmer/Light support
+#undef USE_LIGHT_VIRTUAL_CT // Disable support for Virtual White Color Temperature (SO106)
#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
#undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas
#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code)
@@ -643,6 +644,7 @@
//#undef USE_SONOFF_D1 // Disable support for Sonoff D1 Dimmer (+0k7 code)
// -- Optional light modules ----------------------
+#undef USE_LIGHT_VIRTUAL_CT // Disable support for Virtual White Color Temperature (SO106)
//#undef USE_LIGHT // Also disable all Dimmer/Light support
#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
#undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index 2cd84f780..a47c4a4ca 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -132,6 +132,10 @@ const char kLightCommands[] PROGMEM = "|" // No prefix
D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_DIMMER_STEP "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|"
D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|"
D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR
+ "|" D_CMND_CTRANGE
+#ifdef USE_LIGHT_VIRTUAL_CT
+ "|" D_CMND_VIRTUALCT
+#endif // USE_LIGHT_VIRTUAL_CT
#ifdef USE_LIGHT_PALETTE
"|" D_CMND_PALETTE
#endif // USE_LIGHT_PALETTE
@@ -144,6 +148,10 @@ void (* const LightCommand[])(void) PROGMEM = {
&CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndDimmerStep, &CmndLedTable, &CmndFade,
&CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration,
&CmndWhite, &CmndChannel, &CmndHsbColor,
+ &CmndCTRange,
+#ifdef USE_LIGHT_VIRTUAL_CT
+ &CmndVirtualCT,
+#endif // USE_LIGHT_VIRTUAL_CT
#ifdef USE_LIGHT_PALETTE
&CmndPalette,
#endif // USE_LIGHT_PALETTE
@@ -181,6 +189,12 @@ const uint16_t CT_MAX = 500; // 2000K
// Ranges used for Alexa
const uint16_t CT_MIN_ALEXA = 200; // also 5000K
const uint16_t CT_MAX_ALEXA = 380; // also 2600K
+// Virtual CT default values
+typedef uint8_t vct_pivot_t[LST_MAX];
+const size_t CT_PIVOTS = LIGHT_VIRTUAL_CT_POINTS;
+const vct_pivot_t CT_PIVOTS_RGB PROGMEM = { 255, 255, 255, 0, 0 };
+const vct_pivot_t CT_PIVOTS_CWW PROGMEM = { 0, 0, 0, 255, 0 };
+const vct_pivot_t CT_PIVOTS_WWW PROGMEM = { 0, 0, 0, 0, 255 };
// New version of Gamma correction compute
// Instead of a table, we do a multi-linear approximation, which is close enough
@@ -308,6 +322,12 @@ struct LIGHT {
uint16_t pwm_min = 0; // minimum value for PWM, from DimmerRange, 0..1023
uint16_t pwm_max = 1023; // maxumum value for PWM, from DimmerRange, 0..1023
+
+ // Virtual CT
+ uint16_t vct_ct[CT_PIVOTS]; // CT value for each segment
+#ifdef USE_LIGHT_VIRTUAL_CT
+ vct_pivot_t vct_color[CT_PIVOTS]; // array of 3 colors each with 5 values
+#endif
} Light;
power_t LightPower(void)
@@ -378,13 +398,6 @@ class LightStateClass {
uint8_t _briCT = 255;
uint8_t _color_mode = LCM_RGB; // RGB by default
- // the CT range below represents the rendered range,
- // This is due to Alexa whose CT range is 199..383
- // Hence setting Min=200 and Max=380 makes Alexa use the full range
- // Please note that you can still set CT to 153..500, but any
- // value below _ct_min_range or above _ct_max_range not change the CT
- uint16_t _ct_min_range = CT_MIN; // the minimum CT rendered range
- uint16_t _ct_max_range = CT_MAX; // the maximum CT rendered range
public:
LightStateClass() {
@@ -535,22 +548,7 @@ class LightStateClass {
inline uint16_t getCT() const {
return _ct; // 153..500, or CT_MIN..CT_MAX
- }
-
- // get the CT value within the range into a 10 bits 0..1023 value
- uint16_t getCT10bits() const {
- return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023);
- }
-
- inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) {
- _ct_min_range = ct_min_range;
- _ct_max_range = ct_max_range;
- }
-
- inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const {
- if (ct_min_range) { *ct_min_range = _ct_min_range; }
- if (ct_max_range) { *ct_max_range = _ct_max_range; }
- }
+ }
// get current color in XY format
void getXY(float *x, float *y) {
@@ -603,7 +601,7 @@ class LightStateClass {
setColorMode(LCM_RGB); // try deactivating CT mode, setColorMode() will check which is legal
} else {
ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct));
- _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255);
+ _ww = changeUIntScale(ct, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1], 0, 255);
_wc = 255 - _ww;
_ct = ct;
addCTMode();
@@ -932,15 +930,6 @@ public:
return prev;
}
- void setAlexaCTRange(bool alexa_ct_range) {
- // depending on SetOption82, full or limited CT range
- if (alexa_ct_range) {
- _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); // 200..380
- } else {
- _state->setCTRange(CT_MIN, CT_MAX); // 153..500
- }
- }
-
inline bool isCTRGBLinked() {
return _ct_rgb_linked;
}
@@ -1190,6 +1179,63 @@ uint8_t change10to8(uint16_t v) {
return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255);
}
+/*********************************************************************************************\
+ * CT (White Color Temperature)
+\*********************************************************************************************/
+//
+// Ensure that invariants for Virtual CT are good:
+// - CT_MIN <= ct[0] <= ct[1] <= ct[2] <= CT_MAX
+
+#ifdef USE_LIGHT_VIRTUAL_CT
+void checkVirtualCT(void) {
+ if (Light.vct_ct[0] < CT_MIN) { Light.vct_ct[0] = CT_MIN; }
+ if (Light.vct_ct[CT_PIVOTS-1] > CT_MAX) { Light.vct_ct[CT_PIVOTS-1] = CT_MAX; }
+ for (uint32_t i = 0; i < CT_PIVOTS-1; i++) {
+ if (Light.vct_ct[i+1] < Light.vct_ct[i]) { Light.vct_ct[i+1] = Light.vct_ct[i]; }
+ }
+}
+#endif // USE_LIGHT_VIRTUAL_CT
+
+#ifdef USE_LIGHT_VIRTUAL_CT
+// Init default values for virtual CT, depending on the number of channels
+void initCTRange(uint32_t channels) {
+ if (channels == 4) {
+ if (Settings.flag4.virtual_ct_cw) { // Hardware White is Cold White
+ memcpy_P(Light.vct_color[0], CT_PIVOTS_CWW, sizeof(Light.vct_color[0])); // Cold white
+ memcpy_P(Light.vct_color[1], CT_PIVOTS_RGB, sizeof(Light.vct_color[1])); // Warm white
+ } else { // Hardware White is Warm White
+ memcpy_P(Light.vct_color[0], CT_PIVOTS_RGB, sizeof(Light.vct_color[0])); // Cold white
+ memcpy_P(Light.vct_color[1], CT_PIVOTS_CWW, sizeof(Light.vct_color[1])); // Warm white
+ }
+ } else if (channels == 5) {
+ memcpy_P(Light.vct_color[0], CT_PIVOTS_CWW, sizeof(Light.vct_color[0])); // Cold white
+ memcpy_P(Light.vct_color[1], CT_PIVOTS_WWW, sizeof(Light.vct_color[1])); // Warm white
+ } else {
+ memcpy_P(Light.vct_color[0], CT_PIVOTS_RGB, sizeof(Light.vct_color[0])); // Cold white
+ memcpy_P(Light.vct_color[1], CT_PIVOTS_RGB, sizeof(Light.vct_color[1])); // Warm white
+ }
+ for (uint32_t i = 1; i < CT_PIVOTS-1; i++) {
+ memcpy_P(Light.vct_color[i+1], Light.vct_color[i], sizeof(Light.vct_color[0])); // Copy slot 1 into slot 2 (slot 2 in unused)
+ }
+ checkVirtualCT();
+}
+#endif // USE_LIGHT_VIRTUAL_CT
+
+void setCTRange(uint16_t ct_min, uint16_t ct_max) {
+ Light.vct_ct[0] = ct_min;
+ for (uint32_t i = 1; i < CT_PIVOTS; i++) {
+ Light.vct_ct[i] = ct_max; // all slots above [1] are not used
+ }
+}
+
+void setAlexaCTRange(void) { // depending on SetOption82, full or limited CT range
+ if (Settings.flag4.alexa_ct_range) {
+ setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA);
+ } else {
+ setCTRange(CT_MIN, CT_MAX);
+ }
+}
+
/*********************************************************************************************\
* Gamma correction
\*********************************************************************************************/
@@ -1299,9 +1345,14 @@ bool LightModuleInit(void)
} else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= pwm_channels)) { // SetOption37
// if RGBW or RGBCW, and SetOption37 >= 128, we manage RGB and W separately, hence adding a device
TasmotaGlobal.devices_present++;
- } else if ((Settings.flag4.virtual_ct) && (LST_RGBW == pwm_channels)) {
- Light.virtual_ct = true; // enabled
- TasmotaGlobal.light_type++; // create an additional virtual 5th channel
+ } else {
+#ifdef USE_LIGHT_VIRTUAL_CT
+ initCTRange(pwm_channels);
+ if ((Settings.flag4.virtual_ct) && (LST_RGB <= pwm_channels)) {
+ Light.virtual_ct = true; // enabled
+ TasmotaGlobal.light_type += 5 - pwm_channels; // pretend it is a 5 channels bulb
+ }
+#endif // USE_LIGHT_VIRTUAL_CT
}
return (TasmotaGlobal.light_type > LT_BASIC);
@@ -1367,7 +1418,7 @@ void LightInit(void)
light_controller.setSubType(Light.subtype);
light_controller.loadSettings();
- light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range);
+ setAlexaCTRange();
light_controller.calcLevels(); // calculate the initial values (#8058)
if (LST_SINGLE == Light.subtype) {
@@ -1859,7 +1910,6 @@ void LightAnimate(void)
bool power_off = false;
// make sure we update CT range in case SetOption82 was changed
- light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range);
Light.strip_timer_counter++;
// set sleep parameter: either settings,
@@ -1966,7 +2016,6 @@ void LightAnimate(void)
uint16_t cur_col_10[LST_MAX]; // 10 bits resolution
Light.update = false;
- bool rgbwwtable_applied = false; // did we already applied RGBWWTable (ex: in white_blend_mode or virtual_ct)
// first set 8 and 10 bits channels
for (uint32_t i = 0; i < LST_MAX; i++) {
@@ -1975,58 +2024,19 @@ void LightAnimate(void)
cur_col_10[i] = change8to10(Light.new_color[i]);
}
+ bool rgbwwtable_applied_white = false; // did we already applied RGBWWTable to white channels (ex: in white_blend_mode or virtual_ct)
if (Light.pwm_multi_channels) {
calcGammaMultiChannels(cur_col_10);
} else {
- calcGammaBulbs(cur_col_10);
-
- // Now see if we need to mix RGB and True White
- // Valid only for LST_RGBW, LST_RGBCW, rgbwwTable[4] is zero, and white is zero (see doc)
- if ((LST_RGBW <= Light.subtype) && (Settings.flag4.white_blend_mode) && (0 == cur_col_10[3]+cur_col_10[4])) {
- uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]);
- for (uint32_t i=0; i<3; i++) {
- // substract white and adjust according to rgbwwTable
- uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]);
- cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10);
- }
-
- // compute the adjusted white levels for 10 and 8 bits
- uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023);
- uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); // set white power down corrected with rgbwwTable[3]
- if (LST_RGBW == Light.subtype) {
- // we simply set the white channel
- cur_col_10[3] = white_10;
- } else { // LST_RGBCW
- // we distribute white between cold and warm according to CT value
- uint32_t ct = light_state.getCT10bits();
- cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10);
- cur_col_10[3] = white_10 - cur_col_10[4];
- }
- rgbwwtable_applied = true;
- } else if ((Light.virtual_ct) && (0 == cur_col_10[0]+cur_col_10[1]+cur_col_10[2])) {
- // virtual_ct is on and we don't have any RGB set
- uint16_t sw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[4] : cur_col_10[3]; // white power for virtual RGB
- uint16_t hw_white = Settings.flag4.virtual_ct_cw ? cur_col_10[3] : cur_col_10[4]; // white for hardware LED
- uint32_t adjust_sw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[4] : Settings.rgbwwTable[3]);
- uint32_t adjust_hw = change8to10(Settings.flag4.virtual_ct_cw ? Settings.rgbwwTable[3] : Settings.rgbwwTable[4]);
- // set the target channels. Note: Gamma correction was arleady applied
- cur_col_10[3] = changeUIntScale(hw_white, 0, 1023, 0, adjust_hw);
- cur_col_10[4] = 0; // we don't actually have a 5the channel
- sw_white = changeUIntScale(sw_white, 0, 1023, 0, adjust_sw); // pre-adjust virtual channel
- for (uint32_t i=0; i<3; i++) {
- uint32_t adjust = change8to10(Settings.rgbwwTable[i]);
- cur_col_10[i] = changeUIntScale(sw_white, 0, 1023, 0, adjust);
- }
- rgbwwtable_applied = true;
- }
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs In %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
+ rgbwwtable_applied_white = calcGammaBulbs(cur_col_10); // true means that one PWM channel is used for CT
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs Out %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
}
// Apply RGBWWTable only if not Settings.flag4.white_blend_mode
- if (!rgbwwtable_applied) {
- for (uint32_t i = 0; i 1023) ? 1023 : white_bri10; // max 1023
-
-#ifdef ESP8266
- if ((PHILIPS == TasmotaGlobal.module_type) || (Settings.flag4.pwm_ct_mode)) { // channel 1 is the color tone, mapped to cold channel (0..255)
- // Xiaomi Philips bulbs follow a different scheme:
- cur_col_10[cw1] = light_state.getCT10bits();
- // channel 0=intensity, channel1=temperature
- if (Settings.light_correction) { // gamma correction
- cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); // 10 bits gamma correction
- } else {
- cur_col_10[cw0] = white_bri10_1023; // no gamma, extend to 10 bits
- }
- } else
-#endif // ESP8266
- if (Settings.light_correction) {
- // if sum of both channels is > 255, then channels are probably uncorrelated
- if (white_bri10 <= 1031) { // take a margin of 8 above 1023 to account for rounding errors
- // we calculate the gamma corrected sum of CW + WW
- uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023);
- // then we split the total energy among the cold and warm leds
- cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10);
- cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10);
- } else {
- cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]);
- cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]);
- }
- }
- }
+//
+// Compute the Gamma correction for CW/WW
+// Can be used for 2-channels (channels 0,1) or 5 channels (channels 3,4)
+//
+// It is implicitly called by calcGammaBulb5Channels()
+//
+// In:
+// - 2 channels CW/WW in 10 bits format (0..1023)
+// Out:
+// - 2 channels CW/WW in 10 bits format, with Gamma corretion (if enabled), replaced in place
+// - white_bri10: global brightness of white channel, split over CW/WW (basically the sum of CW+WW, but it's easier to compute on this basis)
+// - white_free_cw: signals that CW/WW are free mode, and not linked via CT. This is used when channels are manually set on a channel per channel basis. CT is ignored
+//
+void calcGammaBulbCW(uint16_t cw10[2], uint16_t *white_bri10_out, bool *white_free_cw_out) {
+ uint16_t white_bri10 = cw10[0] + cw10[1]; // cumulated brightness
+ bool white_free_cw = (white_bri10 > 1031); // take a margin of 8 above 1023 to account for rounding errors
+ white_bri10 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023
if (Settings.light_correction) {
- // then apply gamma correction to RGB channels
- if (LST_RGB <= Light.subtype) {
- for (uint32_t i = 0; i < 3; i++) {
- cur_col_10[i] = ledGamma10_10(cur_col_10[i]);
- }
- }
- // If RGBW or Single channel, also adjust White channel
- if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) {
- cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]);
+ if (white_free_cw) {
+ cw10[0] = ledGamma10_10(cw10[0]);
+ cw10[1] = ledGamma10_10(cw10[1]);
+ } else {
+ uint16_t white_bri10_gamma = ledGamma10_10(white_bri10); // gamma corrected white
+ // now distributed among both channels
+ cw10[0] = changeUIntScale(cw10[0], 0, white_bri10, 0, white_bri10_gamma);
+ cw10[1] = changeUIntScale(cw10[1], 0, white_bri10, 0, white_bri10_gamma);
+ // now use white_bri10_gamma as a reference
+ white_bri10 = white_bri10_gamma;
}
}
+ if (white_bri10_out != nullptr) { *white_bri10_out = white_bri10; }
+ if (white_free_cw_out != nullptr) { *white_free_cw_out = white_free_cw; }
+}
+
+//
+// Calculate the gamma correction for all 5 channels RGBCW
+// Computation is valid for 1,3,4,5 channels
+// 2-channels bulbs must be handled separately
+//
+// In:
+// - 5 channels RGBCW in 10 bits format (0..1023)
+// Out:
+// - 5 channels RGBCW in 10 bits format, with Gamma corretion (if enabled), replaced in place
+// - white_bri10: global brightness of white channel, split over CW/WW (basically the sum of CW+WW, but it's easier to compute on this basis)
+// - white_free_cw: signals that CW/WW are free mode, and not linked via CT. This is used when channels are manually set on a channel per channel basis. CT is ignored
+//
+void calcGammaBulb5Channels(uint16_t col10[LST_MAX], uint16_t *white_bri10_out, bool *white_free_cw) {
+ for (uint32_t i = 0; i < 3; i++) {
+ if (Settings.light_correction) {
+ col10[i] = ledGamma10_10(col10[i]);
+ }
+ }
+ calcGammaBulbCW(&col10[3], white_bri10_out, white_free_cw);
+}
+
+// sale but converts from 8 bits to 10 bits first
+void calcGammaBulb5Channels_8(uint8_t in8[LST_MAX], uint16_t col10[LST_MAX]) {
+ for (uint32_t i = 0; i < LST_MAX; i++) {
+ col10[i] = change8to10(in8[i]);
+ }
+ calcGammaBulb5Channels(col10, nullptr, nullptr);
+}
+
+bool calcGammaBulbs(uint16_t cur_col_10[5]) {
+ bool rgbwwtable_applied_white = false;
+ bool pwm_ct = false;
+ bool white_free_cw = false; // true if White channels are uncorrelated. Happens when CW+WW>255, i.e. manually setting white channels to exceed to total power of a single channel (may harm the power supply)
+ // Various values needed for accurate White calculation
+ // CT value streteched to 0..1023 (from within CT range, so not necessarily from 153 to 500). 0=Cold, 1023=Warm
+ uint16_t ct = light_state.getCT();
+ uint16_t ct_10 = changeUIntScale(ct, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1], 0, 1023);
+
+ uint16_t white_bri10 = 0; // White total brightness normalized to 0..1023
+ // uint32_t cw1 = Light.subtype - 1; // address for the ColorTone PWM
+ uint32_t cw0 = Light.subtype - 2; // address for the White Brightness PWM
+
+ // calc basic gamma correction for all types
+ if ((LST_SINGLE == Light.subtype) || (LST_RGB <= Light.subtype)) {
+ calcGammaBulb5Channels(cur_col_10, &white_bri10, &white_free_cw);
+ } else if (LST_COLDWARM == Light.subtype) {
+ calcGammaBulbCW(cur_col_10, &white_bri10, &white_free_cw);
+ }
+
+ // Now we know ct_10 and white_bri10 (gamma corrected if needed)
+
+#ifdef ESP8266
+ if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) {
+ if ((PHILIPS == TasmotaGlobal.module_type) || (Settings.flag4.pwm_ct_mode)) { // channel 1 is the color tone, mapped to cold channel (0..255)
+ pwm_ct = true;
+ // Xiaomi Philips bulbs follow a different scheme:
+ // channel 0=intensity, channel1=temperature
+ cur_col_10[cw0] = white_bri10;
+ cur_col_10[cw0+1] = ct_10;
+ return false; // avoid any interference
+ }
+ }
+#endif // ESP8266
+
+ // Now see if we need to mix RGB and White
+ // Valid only for LST_RGBW, LST_RGBCW, SetOption105 1, and white is zero (see doc)
+ if ((LST_RGBW <= Light.subtype) && (Settings.flag4.white_blend_mode) && (0 == cur_col_10[3]+cur_col_10[4])) {
+ uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]);
+ cur_col_10[0] -= min_rgb_10;
+ cur_col_10[1] -= min_rgb_10;
+ cur_col_10[2] -= min_rgb_10;
+
+ // Add to white level
+ uint32_t adjust_w_10 = change8to10(Settings.rgbwwTable[3]); // take the correction factor, bought back to 10 bits
+ white_bri10 += changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); // set white power down corrected with rgbwwTable[3]
+ white_bri10 = (white_bri10 > 1023) ? 1023 : white_bri10; // max 1023
+ rgbwwtable_applied_white = true;
+ }
+
+#ifdef USE_LIGHT_VIRTUAL_CT
+ // compute virtual CT, which is suppsed to be compatible with white_blend_mode
+ if (Light.virtual_ct && (!white_free_cw) && (LST_RGBW <= Light.subtype)) { // any light with a white channel
+ vct_pivot_t *pivot = &Light.vct_color[0];
+ uint16_t *from_ct = &Light.vct_ct[0];
+
+ for (uint32_t i = 1; i < CT_PIVOTS-1; i++) {
+ if (ct > Light.vct_ct[i]) { // if above mid-point, take range [1]..[2] instead of [0]..[1]
+ pivot++;
+ from_ct++;
+ }
+ }
+ uint16_t from10[LST_MAX];
+ uint16_t to10[LST_MAX];
+ calcGammaBulb5Channels_8(*pivot, from10);
+ calcGammaBulb5Channels_8(*(pivot+1), to10);
+
+ vct_pivot_t *pivot1 = pivot + 1;
+ // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from_ct %d, to_ct %d [%03X,%03X,%03X,%03X,%03X] - [%03X,%03X,%03X,%03X,%03X]"),
+ // *from_ct, *(from_ct+1), (*pivot)[0], (*pivot)[1], (*pivot)[2], (*pivot)[3], (*pivot)[4],
+ // (*pivot1)[0], (*pivot1)[1], (*pivot1)[2], (*pivot1)[3], (*pivot1)[4]);
+ // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from10 [%03X,%03X,%03X,%03X,%03X] - to 10 [%03X,%03X,%03X,%03X,%03X]"),
+ // from10[0],from10[0],from10[0],from10[0],from10[4],
+ // to10[0],to10[0],to10[0],to10[0],to10[4]);
+
+ // set both CW/WW to zero since their previous value don't count anymore
+ cur_col_10[3] = 0;
+ cur_col_10[4] = 0;
+
+ // Add the interpolated point to each component
+ for (uint32_t i = 0; i < LST_MAX; i++) {
+ cur_col_10[i] += changeUIntScale(changeUIntScale(ct, *from_ct, *(from_ct+1), from10[i], to10[i]), 0, 1023, 0, white_bri10);
+ if (cur_col_10[i] > 1023) { cur_col_10[i] = 1023; }
+ }
+ } else
+#endif // USE_LIGHT_VIRTUAL_CT
+ // compute the actual levels for CW/WW
+ // We know ct_10 and white_bri_10 (which may be Gamma corrected)
+ // cur_col_10[cw0] and cur_col_10[cw1] were unmodified up to now
+ if (LST_RGBW == Light.subtype) {
+ cur_col_10[3] = white_bri10; // simple case, we set the White level to the required brightness
+ } else if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) {
+ // if sum of both channels is > 255, then channels are probably uncorrelated
+ if (!white_free_cw) {
+ // then we split the total energy among the cold and warm leds
+ cur_col_10[cw0+1] = changeUIntScale(ct_10, 0, 1023, 0, white_bri10);
+ cur_col_10[cw0] = white_bri10 - cur_col_10[cw0+1];
+ }
+ }
+ return rgbwwtable_applied_white;
}
#ifdef USE_DEVICE_GROUPS
@@ -3045,6 +3169,83 @@ void CmndWakeupDuration(void)
ResponseCmndNumber(Settings.light_wakeup);
}
+void CmndCTRange(void)
+{
+ // Format is "CTRange ctmin,ctmax"
+ // Ex:
+ // CTRange 153,500
+ // CTRange
+ // CTRange 200,350
+ char *p;
+ strtok_r(XdrvMailbox.data, ",", &p);
+ if (p != nullptr) {
+ int32_t ct_min = strtol(XdrvMailbox.data, nullptr, 0);
+ int32_t ct_max = strtol(p, nullptr, 0);
+ if ( (ct_min >= CT_MIN) && (ct_min <= CT_MAX) &&
+ (ct_max >= CT_MIN) && (ct_max <= CT_MAX) &&
+ (ct_min <= ct_max)
+ ) {
+ setCTRange(ct_min, ct_max);
+ } else {
+ return; // error
+ }
+ }
+ Response_P(PSTR("{\"%s\":\"%d,%d\"}"), XdrvMailbox.command, Light.vct_ct[0], Light.vct_ct[CT_PIVOTS-1]);
+}
+
+#ifdef USE_LIGHT_VIRTUAL_CT
+void CmndVirtualCT(void)
+{
+ if (!Settings.flag4.virtual_ct) {
+ ResponseCmndChar_P(PSTR("You need to enable `SetOption106 1`"));
+ return;
+ }
+ if (XdrvMailbox.data[0] == ('{')) {
+ // parse JSON
+ JsonParser parser(XdrvMailbox.data);
+ JsonParserObject root = parser.getRootObject();
+ if (!root) { return; }
+
+ uint32_t idx = 0;
+ for (auto key : root) {
+ if (idx >= CT_PIVOTS) { ResponseCmndChar_P(PSTR("Too many points")); return; }
+
+ int32_t ct_val = strtol(key.getStr(), nullptr, 0);
+ if ((ct_val < CT_MIN) || (ct_val > CT_MAX)) { ResponseCmndChar_P(PSTR("CT out of range")); return; }
+ char * color = (char*) key.getValue().getStr();
+ // call color parser
+ Light.vct_ct[idx] = ct_val;
+ if (LightColorEntry(color, strlen(color))) {
+ memcpy(&Light.vct_color[idx], Light.entry_color, sizeof(Light.vct_color[idx]));
+ }
+ idx++;
+ }
+ for (uint32_t i = idx-1; i < CT_PIVOTS-1; i++) {
+ Light.vct_ct[i+1] = Light.vct_ct[i];
+ memcpy(&Light.vct_color[i+1], &Light.vct_color[i], sizeof(Light.vct_color[0]));
+ }
+ }
+ checkVirtualCT();
+
+ Response_P(PSTR("{\"%s\":{"), XdrvMailbox.command);
+ uint32_t pivot_len = CT_PIVOTS;
+ vct_pivot_t * pivot = &Light.vct_color[0];
+ if (Light.vct_ct[1] >= Light.vct_ct[2]) { pivot_len = 2; } // only 2 points are valid
+
+ bool end = false;
+ for (uint32_t i = 0; (i < CT_PIVOTS) && !end; i++) {
+ if ((i >= CT_PIVOTS-1) || (Light.vct_ct[i] >= Light.vct_ct[i+1])) {
+ end = true;
+ }
+ ResponseAppend_P(PSTR("\"%d\":\"%02X%02X%02X%02X%02X\"%c"), Light.vct_ct[i],
+ (*pivot)[0], (*pivot)[1], (*pivot)[2], (*pivot)[3], (*pivot)[4],
+ end ? '}' : ',');
+ pivot++;
+ }
+ ResponseJsonEnd();
+}
+#endif // USE_LIGHT_VIRTUAL_CT
+
#ifdef USE_LIGHT_PALETTE
void CmndPalette(void)
{
From 2afc18bac2da5621d57d631807529b55d14725dc Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Tue, 29 Dec 2020 19:58:38 +0100
Subject: [PATCH 023/255] Fix compilation for TuyaMCU
---
tasmota/xdrv_04_light.ino | 5 +++++
tasmota/xdrv_16_tuyamcu.ino | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index a47c4a4ca..f0b94bcec 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -1221,6 +1221,11 @@ void initCTRange(uint32_t channels) {
}
#endif // USE_LIGHT_VIRTUAL_CT
+void getCTRange(uint16_t * min_ct, uint16_t * max_ct) {
+ if (min_ct != nullptr) { *min_ct = Light.vct_ct[0]; }
+ if (max_ct != nullptr) { *max_ct = Light.vct_ct[CT_PIVOTS-1]; }
+}
+
void setCTRange(uint16_t ct_min, uint16_t ct_max) {
Light.vct_ct[0] = ct_min;
for (uint32_t i = 1; i < CT_PIVOTS; i++) {
diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino
index 13a7864c5..51a0a9c5b 100644
--- a/tasmota/xdrv_16_tuyamcu.ino
+++ b/tasmota/xdrv_16_tuyamcu.ino
@@ -517,7 +517,7 @@ bool TuyaSetChannels(void)
Tuya.Snapshot[0] = changeUIntScale(Light.current_color[0], 0, 255, 0, 100);
Tuya.Snapshot[1] = changeUIntScale(Light.current_color[1], 0, 255, 0, 100);
} else { // CT Light or RGBWC
- light_state.getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range
+ getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range
Tuya.Snapshot[0] = light_state.getDimmer();
Tuya.Snapshot[1] = light_state.getCT();
}
From e866db7364c1322d83e97b3adac73722b3bc1a0b Mon Sep 17 00:00:00 2001
From: Jeroen Vermeulen - MageHost
Date: Tue, 29 Dec 2020 22:39:06 +0100
Subject: [PATCH 024/255] feature: SSD1331 SPI display driver
Based on release 1.2.0: https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/releases/tag/1.2.0
Inspired by the xdsp_09_SSD1351 driver.
---
.../Adafruit_SPITFT_Renderer.cpp | 2217 +++++++++++++++++
.../Adafruit_SPITFT_Renderer.h | 520 ++++
.../Adafruit_SSD1331.cpp | 190 ++
.../Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h | 76 +
.../Adafruit_SSD1331-1.2.0/README.md | 24 +
.../Adafruit_SSD1331-1.2.0/library.properties | 10 +
.../Adafruit_SSD1331-1.2.0/license.txt | 26 +
tasmota/xdsp_14_SSD1331.ino | 182 ++
8 files changed, 3245 insertions(+)
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/README.md
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties
create mode 100644 lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt
create mode 100644 tasmota/xdsp_14_SSD1331.ino
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp
new file mode 100644
index 000000000..d49141a28
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp
@@ -0,0 +1,2217 @@
+/*!
+ * @file Adafruit_SPITFT.cpp
+ *
+ * @mainpage Adafruit SPI TFT Displays (and some others)
+ *
+ * @section intro_sec Introduction
+ *
+ * Part of Adafruit's GFX graphics library. Originally this class was
+ * written to handle a range of color TFT displays connected via SPI,
+ * but over time this library and some display-specific subclasses have
+ * mutated to include some color OLEDs as well as parallel-interfaced
+ * displays. The name's been kept for the sake of older code.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+
+ * @section dependencies Dependencies
+ *
+ * This library depends on
+ * Adafruit_GFX being present on your system. Please make sure you have
+ * installed the latest version before using this library.
+ *
+ * @section author Author
+ *
+ * Written by Limor "ladyada" Fried for Adafruit Industries,
+ * with contributions from the open source community.
+ *
+ * @section license License
+ *
+ * BSD license, all text here must be included in any redistribution.
+ */
+
+#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
+
+#include "Adafruit_SPITFT_Renderer.h"
+
+#if defined(__AVR__)
+#if defined(__AVR_XMEGA__) //only tested with __AVR_ATmega4809__
+#define AVR_WRITESPI(x) for(SPI0_DATA = (x); (!(SPI0_INTFLAGS & _BV(SPI_IF_bp))); )
+#else
+#define AVR_WRITESPI(x) for(SPDR = (x); (!(SPSR & _BV(SPIF))); )
+#endif
+#endif
+
+#if defined(PORT_IOBUS)
+// On SAMD21, redefine digitalPinToPort() to use the slightly-faster
+// PORT_IOBUS rather than PORT (not needed on SAMD51).
+#undef digitalPinToPort
+#define digitalPinToPort(P) (&(PORT_IOBUS->Group[g_APinDescription[P].ulPort]))
+#endif // end PORT_IOBUS
+
+#if defined(USE_SPI_DMA)
+ #include
+ #include "wiring_private.h" // pinPeripheral() function
+ #include // memalign() function
+ #define tcNum 2 // Timer/Counter for parallel write strobe PWM
+ #define wrPeripheral PIO_CCL // Use CCL to invert write strobe
+
+ // DMA transfer-in-progress indicator and callback
+ static volatile bool dma_busy = false;
+ static void dma_callback(Adafruit_ZeroDMA *dma) {
+ dma_busy = false;
+ }
+
+ #if defined(__SAMD51__)
+ // Timer/counter info by index #
+ static const struct {
+ Tc *tc; // -> Timer/Counter base address
+ int gclk; // GCLK ID
+ int evu; // EVSYS user ID
+ } tcList[] = {
+ { TC0, TC0_GCLK_ID, EVSYS_ID_USER_TC0_EVU },
+ { TC1, TC1_GCLK_ID, EVSYS_ID_USER_TC1_EVU },
+ { TC2, TC2_GCLK_ID, EVSYS_ID_USER_TC2_EVU },
+ { TC3, TC3_GCLK_ID, EVSYS_ID_USER_TC3_EVU },
+ #if defined(TC4)
+ { TC4, TC4_GCLK_ID, EVSYS_ID_USER_TC4_EVU },
+ #endif
+ #if defined(TC5)
+ { TC5, TC5_GCLK_ID, EVSYS_ID_USER_TC5_EVU },
+ #endif
+ #if defined(TC6)
+ { TC6, TC6_GCLK_ID, EVSYS_ID_USER_TC6_EVU },
+ #endif
+ #if defined(TC7)
+ { TC7, TC7_GCLK_ID, EVSYS_ID_USER_TC7_EVU }
+ #endif
+ };
+ #define NUM_TIMERS (sizeof tcList / sizeof tcList[0]) ///< # timer/counters
+ #endif // end __SAMD51__
+
+#endif // end USE_SPI_DMA
+
+// Possible values for Adafruit_SPITFT.connection:
+#define TFT_HARD_SPI 0 ///< Display interface = hardware SPI
+#define TFT_SOFT_SPI 1 ///< Display interface = software SPI
+#define TFT_PARALLEL 2 ///< Display interface = 8- or 16-bit parallel
+
+
+// CONSTRUCTORS ------------------------------------------------------------
+
+/*!
+ @brief Adafruit_SPITFT constructor for software (bitbang) SPI.
+ @param w Display width in pixels at default rotation setting (0).
+ @param h Display height in pixels at default rotation setting (0).
+ @param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
+ @param dc Arduino pin # for data/command select (required).
+ @param mosi Arduino pin # for bitbang SPI MOSI signal (required).
+ @param sck Arduino pin # for bitbang SPI SCK signal (required).
+ @param rst Arduino pin # for display reset (optional, display reset
+ can be tied to MCU reset, default of -1 means unused).
+ @param miso Arduino pin # for bitbang SPI MISO signal (optional,
+ -1 default, many displays don't support SPI read).
+ @return Adafruit_SPITFT object.
+ @note Output pins are not initialized; application typically will
+ need to call subclass' begin() function, which in turn calls
+ this library's initSPI() function to initialize pins.
+*/
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h,
+ int8_t cs, int8_t dc, int8_t mosi, int8_t sck, int8_t rst, int8_t miso) :
+ Renderer(w, h), connection(TFT_SOFT_SPI), _rst(rst), _cs(cs), _dc(dc) {
+ swspi._sck = sck;
+ swspi._mosi = mosi;
+ swspi._miso = miso;
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(CORE_TEENSY)
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ swspi.sckPinMask = digitalPinToBitMask(sck);
+ swspi.mosiPinMask = digitalPinToBitMask(mosi);
+ #endif
+ dcPortSet = portSetRegister(dc);
+ dcPortClr = portClearRegister(dc);
+ swspi.sckPortSet = portSetRegister(sck);
+ swspi.sckPortClr = portClearRegister(sck);
+ swspi.mosiPortSet = portSetRegister(mosi);
+ swspi.mosiPortClr = portClearRegister(mosi);
+ if(cs >= 0) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else {
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ if(miso >= 0) {
+ swspi.misoPort = portInputRegister(miso);
+ #if !defined(KINETISK)
+ swspi.misoPinMask = digitalPinToBitMask(miso);
+ #endif
+ } else {
+ swspi.misoPort = portInputRegister(dc);
+ }
+ #else // !CORE_TEENSY
+ dcPinMask =digitalPinToBitMask(dc);
+ swspi.sckPinMask =digitalPinToBitMask(sck);
+ swspi.mosiPinMask=digitalPinToBitMask(mosi);
+ dcPortSet =&(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg);
+ dcPortClr =&(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg);
+ swspi.sckPortSet =&(PORT->Group[g_APinDescription[sck].ulPort].OUTSET.reg);
+ swspi.sckPortClr =&(PORT->Group[g_APinDescription[sck].ulPort].OUTCLR.reg);
+ swspi.mosiPortSet=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTSET.reg);
+ swspi.mosiPortClr=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTCLR.reg);
+ if(cs >= 0) {
+ csPinMask = digitalPinToBitMask(cs);
+ csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg);
+ csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ csPinMask = 0;
+ }
+ if(miso >= 0) {
+ swspi.misoPinMask=digitalPinToBitMask(miso);
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso));
+ } else {
+ swspi.misoPinMask=0;
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc));
+ }
+ #endif // end !CORE_TEENSY
+ #else // !HAS_PORT_SET_CLR
+ dcPort =(PORTreg_t)portOutputRegister(digitalPinToPort(dc));
+ dcPinMaskSet =digitalPinToBitMask(dc);
+ swspi.sckPort =(PORTreg_t)portOutputRegister(digitalPinToPort(sck));
+ swspi.sckPinMaskSet =digitalPinToBitMask(sck);
+ swspi.mosiPort =(PORTreg_t)portOutputRegister(digitalPinToPort(mosi));
+ swspi.mosiPinMaskSet=digitalPinToBitMask(mosi);
+ if(cs >= 0) {
+ csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs));
+ csPinMaskSet = digitalPinToBitMask(cs);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPort = dcPort;
+ csPinMaskSet = 0;
+ }
+ if(miso >= 0) {
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso));
+ swspi.misoPinMask=digitalPinToBitMask(miso);
+ } else {
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc));
+ swspi.misoPinMask=0;
+ }
+ csPinMaskClr = ~csPinMaskSet;
+ dcPinMaskClr = ~dcPinMaskSet;
+ swspi.sckPinMaskClr = ~swspi.sckPinMaskSet;
+ swspi.mosiPinMaskClr = ~swspi.mosiPinMaskSet;
+ #endif // !end HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+}
+
+/*!
+ @brief Adafruit_SPITFT constructor for hardware SPI using the board's
+ default SPI peripheral.
+ @param w Display width in pixels at default rotation setting (0).
+ @param h Display height in pixels at default rotation setting (0).
+ @param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
+ @param dc Arduino pin # for data/command select (required).
+ @param rst Arduino pin # for display reset (optional, display reset
+ can be tied to MCU reset, default of -1 means unused).
+ @return Adafruit_SPITFT object.
+ @note Output pins are not initialized; application typically will
+ need to call subclass' begin() function, which in turn calls
+ this library's initSPI() function to initialize pins.
+*/
+#if defined(ESP8266) // See notes below
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs,
+ int8_t dc, int8_t rst) : Renderer(w, h),
+ connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) {
+ hwspi._spi = &SPI;
+}
+#else // !ESP8266
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs,
+ int8_t dc, int8_t rst) : Adafruit_SPITFT(w, h, &SPI, cs, dc, rst) {
+ // This just invokes the hardware SPI constructor below,
+ // passing the default SPI device (&SPI).
+}
+#endif // end !ESP8266
+
+#if !defined(ESP8266)
+// ESP8266 compiler freaks out at this constructor -- it can't disambiguate
+// beteween the SPIClass pointer (argument #3) and a regular integer.
+// Solution here it to just not offer this variant on the ESP8266. You can
+// use the default hardware SPI peripheral, or you can use software SPI,
+// but if there's any library out there that creates a 'virtual' SPIClass
+// peripheral and drives it with software bitbanging, that's not supported.
+/*!
+ @brief Adafruit_SPITFT constructor for hardware SPI using a specific
+ SPI peripheral.
+ @param w Display width in pixels at default rotation (0).
+ @param h Display height in pixels at default rotation (0).
+ @param spiClass Pointer to SPIClass type (e.g. &SPI or &SPI1).
+ @param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
+ @param dc Arduino pin # for data/command select (required).
+ @param rst Arduino pin # for display reset (optional, display reset
+ can be tied to MCU reset, default of -1 means unused).
+ @return Adafruit_SPITFT object.
+ @note Output pins are not initialized in constructor; application
+ typically will need to call subclass' begin() function, which
+ in turn calls this library's initSPI() function to initialize
+ pins. EXCEPT...if you have built your own SERCOM SPI peripheral
+ (calling the SPIClass constructor) rather than one of the
+ built-in SPI devices (e.g. &SPI, &SPI1 and so forth), you will
+ need to call the begin() function for your object as well as
+ pinPeripheral() for the MOSI, MISO and SCK pins to configure
+ GPIO manually. Do this BEFORE calling the display-specific
+ begin or init function. Unfortunate but unavoidable.
+*/
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass,
+ int8_t cs, int8_t dc, int8_t rst) : Renderer(w, h),
+ connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) {
+ hwspi._spi = spiClass;
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(CORE_TEENSY)
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ #endif
+ dcPortSet = portSetRegister(dc);
+ dcPortClr = portClearRegister(dc);
+ if(cs >= 0) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else { // see comments below
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ #else // !CORE_TEENSY
+ dcPinMask = digitalPinToBitMask(dc);
+ dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg);
+ dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg);
+ if(cs >= 0) {
+ csPinMask = digitalPinToBitMask(cs);
+ csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg);
+ csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ csPinMask = 0;
+ }
+ #endif // end !CORE_TEENSY
+ #else // !HAS_PORT_SET_CLR
+ dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc));
+ dcPinMaskSet = digitalPinToBitMask(dc);
+ if(cs >= 0) {
+ csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs));
+ csPinMaskSet = digitalPinToBitMask(cs);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPort = dcPort;
+ csPinMaskSet = 0;
+ }
+ csPinMaskClr = ~csPinMaskSet;
+ dcPinMaskClr = ~dcPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+}
+#endif // end !ESP8266
+
+/*!
+ @brief Adafruit_SPITFT constructor for parallel display connection.
+ @param w Display width in pixels at default rotation (0).
+ @param h Display height in pixels at default rotation (0).
+ @param busWidth If tft16 (enumeration in header file), is a 16-bit
+ parallel connection, else 8-bit.
+ 16-bit isn't fully implemented or tested yet so
+ applications should pass "tft8bitbus" for now...needed to
+ stick a required enum argument in there to
+ disambiguate this constructor from the soft-SPI case.
+ Argument is ignored on 8-bit architectures (no 'wide'
+ support there since PORTs are 8 bits anyway).
+ @param d0 Arduino pin # for data bit 0 (1+ are extrapolated).
+ The 8 (or 16) data bits MUST be contiguous and byte-
+ aligned (or word-aligned for wide interface) within
+ the same PORT register (might not correspond to
+ Arduino pin sequence).
+ @param wr Arduino pin # for write strobe (required).
+ @param dc Arduino pin # for data/command select (required).
+ @param cs Arduino pin # for chip-select (optional, -1 if unused,
+ tie CS low).
+ @param rst Arduino pin # for display reset (optional, display reset
+ can be tied to MCU reset, default of -1 means unused).
+ @param rd Arduino pin # for read strobe (optional, -1 if unused).
+ @return Adafruit_SPITFT object.
+ @note Output pins are not initialized; application typically will need
+ to call subclass' begin() function, which in turn calls this
+ library's initSPI() function to initialize pins.
+ Yes, the name is a misnomer...this library originally handled
+ only SPI displays, parallel being a recent addition (but not
+ wanting to break existing code).
+*/
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth,
+ int8_t d0, int8_t wr, int8_t dc, int8_t cs, int8_t rst, int8_t rd) :
+ Renderer(w, h), connection(TFT_PARALLEL), _rst(rst), _cs(cs), _dc(dc) {
+ tft8._d0 = d0;
+ tft8._wr = wr;
+ tft8._rd = rd;
+ tft8.wide = (busWidth == tft16bitbus);
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(CORE_TEENSY)
+ tft8.wrPortSet = portSetRegister(wr);
+ tft8.wrPortClr = portClearRegister(wr);
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ #endif
+ dcPortSet = portSetRegister(dc);
+ dcPortClr = portClearRegister(dc);
+ if(cs >= 0) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else { // see comments below
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ if(rd >= 0) { // if read-strobe pin specified...
+ #if defined(KINETISK)
+ tft8.rdPinMask = 1;
+ #else // !KINETISK
+ tft8.rdPinMask = digitalPinToBitMask(rd);
+ #endif
+ tft8.rdPortSet = portSetRegister(rd);
+ tft8.rdPortClr = portClearRegister(rd);
+ } else {
+ tft8.rdPinMask = 0;
+ tft8.rdPortSet = dcPortSet;
+ tft8.rdPortClr = dcPortClr;
+ }
+ // These are all uint8_t* pointers -- elsewhere they're recast
+ // as necessary if a 'wide' 16-bit interface is in use.
+ tft8.writePort = portOutputRegister(d0);
+ tft8.readPort = portInputRegister(d0);
+ tft8.dirSet = portModeRegister(d0);
+ tft8.dirClr = portModeRegister(d0);
+ #else // !CORE_TEENSY
+ tft8.wrPinMask = digitalPinToBitMask(wr);
+ tft8.wrPortSet = &(PORT->Group[g_APinDescription[wr].ulPort].OUTSET.reg);
+ tft8.wrPortClr = &(PORT->Group[g_APinDescription[wr].ulPort].OUTCLR.reg);
+ dcPinMask = digitalPinToBitMask(dc);
+ dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg);
+ dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg);
+ if(cs >= 0) {
+ csPinMask = digitalPinToBitMask(cs);
+ csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg);
+ csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ csPinMask = 0;
+ }
+ if(rd >= 0) { // if read-strobe pin specified...
+ tft8.rdPinMask =digitalPinToBitMask(rd);
+ tft8.rdPortSet =&(PORT->Group[g_APinDescription[rd].ulPort].OUTSET.reg);
+ tft8.rdPortClr =&(PORT->Group[g_APinDescription[rd].ulPort].OUTCLR.reg);
+ } else {
+ tft8.rdPinMask = 0;
+ tft8.rdPortSet = dcPortSet;
+ tft8.rdPortClr = dcPortClr;
+ }
+ // Get pointers to PORT write/read/dir bytes within 32-bit PORT
+ uint8_t dBit = g_APinDescription[d0].ulPin; // d0 bit # in PORT
+ PortGroup *p = (&(PORT->Group[g_APinDescription[d0].ulPort]));
+ uint8_t offset = dBit / 8; // d[7:0] byte # within PORT
+ if(tft8.wide) offset &= ~1; // d[15:8] byte # within PORT
+ // These are all uint8_t* pointers -- elsewhere they're recast
+ // as necessary if a 'wide' 16-bit interface is in use.
+ tft8.writePort = (volatile uint8_t *)&(p->OUT.reg) + offset;
+ tft8.readPort = (volatile uint8_t *)&(p->IN.reg) + offset;
+ tft8.dirSet = (volatile uint8_t *)&(p->DIRSET.reg) + offset;
+ tft8.dirClr = (volatile uint8_t *)&(p->DIRCLR.reg) + offset;
+ #endif // end !CORE_TEENSY
+ #else // !HAS_PORT_SET_CLR
+ tft8.wrPort = (PORTreg_t)portOutputRegister(digitalPinToPort(wr));
+ tft8.wrPinMaskSet = digitalPinToBitMask(wr);
+ dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc));
+ dcPinMaskSet = digitalPinToBitMask(dc);
+ if(cs >= 0) {
+ csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs));
+ csPinMaskSet = digitalPinToBitMask(cs);
+ } else {
+ // No chip-select line defined; might be permanently tied to GND.
+ // Assign a valid GPIO register (though not used for CS), and an
+ // empty pin bitmask...the nonsense bit-twiddling might be faster
+ // than checking _cs and possibly branching.
+ csPort = dcPort;
+ csPinMaskSet = 0;
+ }
+ if(rd >= 0) { // if read-strobe pin specified...
+ tft8.rdPort =(PORTreg_t)portOutputRegister(digitalPinToPort(rd));
+ tft8.rdPinMaskSet =digitalPinToBitMask(rd);
+ } else {
+ tft8.rdPort = dcPort;
+ tft8.rdPinMaskSet = 0;
+ }
+ csPinMaskClr = ~csPinMaskSet;
+ dcPinMaskClr = ~dcPinMaskSet;
+ tft8.wrPinMaskClr = ~tft8.wrPinMaskSet;
+ tft8.rdPinMaskClr = ~tft8.rdPinMaskSet;
+ tft8.writePort = (PORTreg_t)portOutputRegister(digitalPinToPort(d0));
+ tft8.readPort = (PORTreg_t)portInputRegister(digitalPinToPort(d0));
+ tft8.portDir = (PORTreg_t)portModeRegister(digitalPinToPort(d0));
+ #endif // end !HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+}
+
+// end constructors -------
+
+
+// CLASS MEMBER FUNCTIONS --------------------------------------------------
+
+// begin() and setAddrWindow() MUST be declared by any subclass.
+
+/*!
+ @brief Configure microcontroller pins for TFT interfacing. Typically
+ called by a subclass' begin() function.
+ @param freq SPI frequency when using hardware SPI. If default (0)
+ is passed, will fall back on a device-specific value.
+ Value is ignored when using software SPI or parallel
+ connection.
+ @param spiMode SPI mode when using hardware SPI. MUST be one of the
+ values SPI_MODE0, SPI_MODE1, SPI_MODE2 or SPI_MODE3
+ defined in SPI.h. Do NOT attempt to pass '0' for
+ SPI_MODE0 and so forth...the values are NOT the same!
+ Use ONLY the defines! (Pity it's not an enum.)
+ @note Another anachronistically-named function; this is called even
+ when the display connection is parallel (not SPI). Also, this
+ could probably be made private...quite a few class functions
+ were generously put in the public section.
+*/
+void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) {
+
+ if(!freq) freq = DEFAULT_SPI_FREQ; // If no freq specified, use default
+
+ // Init basic control pins common to all connection types
+ if(_cs >= 0) {
+ pinMode(_cs, OUTPUT);
+ digitalWrite(_cs, HIGH); // Deselect
+ }
+ pinMode(_dc, OUTPUT);
+ digitalWrite(_dc, HIGH); // Data mode
+
+ if(connection == TFT_HARD_SPI) {
+
+#if defined(SPI_HAS_TRANSACTION)
+ hwspi.settings = SPISettings(freq, MSBFIRST, spiMode);
+#else
+ hwspi._freq = freq; // Save freq value for later
+#endif
+ hwspi._mode = spiMode; // Save spiMode value for later
+ // Call hwspi._spi->begin() ONLY if this is among the 'established'
+ // SPI interfaces in variant.h. For DIY roll-your-own SERCOM SPIs,
+ // begin() and pinPeripheral() calls MUST be made in one's calling
+ // code, BEFORE the screen-specific begin/init function is called.
+ // Reason for this is that SPI::begin() makes its own calls to
+ // pinPeripheral() based on g_APinDescription[n].ulPinType, which
+ // on non-established SPI interface pins will always be PIO_DIGITAL
+ // or similar, while we need PIO_SERCOM or PIO_SERCOM_ALT...it's
+ // highly unique between devices and variants for each pin or
+ // SERCOM so we can't make those calls ourselves here. And the SPI
+ // device needs to be set up before calling this because it's
+ // immediately followed with initialization commands. Blargh.
+ if(
+#if !defined(SPI_INTERFACES_COUNT)
+ 1
+#endif
+#if SPI_INTERFACES_COUNT > 0
+ (hwspi._spi == &SPI)
+#endif
+#if SPI_INTERFACES_COUNT > 1
+ || (hwspi._spi == &SPI1)
+#endif
+#if SPI_INTERFACES_COUNT > 2
+ || (hwspi._spi == &SPI2)
+#endif
+#if SPI_INTERFACES_COUNT > 3
+ || (hwspi._spi == &SPI3)
+#endif
+#if SPI_INTERFACES_COUNT > 4
+ || (hwspi._spi == &SPI4)
+#endif
+#if SPI_INTERFACES_COUNT > 5
+ || (hwspi._spi == &SPI5)
+#endif
+ ) {
+ hwspi._spi->begin();
+ }
+ } else if(connection == TFT_SOFT_SPI) {
+
+ pinMode(swspi._mosi, OUTPUT);
+ digitalWrite(swspi._mosi, LOW);
+ pinMode(swspi._sck, OUTPUT);
+ digitalWrite(swspi._sck, LOW);
+ if(swspi._miso >= 0) {
+ pinMode(swspi._miso, INPUT);
+ }
+
+ } else { // TFT_PARALLEL
+
+ // Initialize data pins. We were only passed d0, so scan
+ // the pin description list looking for the other pins.
+ // They'll be on the same PORT, and within the next 7 (or 15) bits
+ // (because we need to write to a contiguous PORT byte or word).
+#if defined(__AVR__)
+ // PORT registers are 8 bits wide, so just need a register match...
+ for(uint8_t i=0; i= dBit ) &&
+ (g_APinDescription[i].ulPin <= (uint32_t)lastBit)) {
+ pinMode(i, OUTPUT);
+ digitalWrite(i, LOW);
+ }
+ }
+ #endif // end !CORE_TEENSY
+#endif
+ pinMode(tft8._wr, OUTPUT);
+ digitalWrite(tft8._wr, HIGH);
+ if(tft8._rd >= 0) {
+ pinMode(tft8._rd, OUTPUT);
+ digitalWrite(tft8._rd, HIGH);
+ }
+ }
+
+ if(_rst >= 0) {
+ // Toggle _rst low to reset
+ pinMode(_rst, OUTPUT);
+ digitalWrite(_rst, HIGH);
+ delay(100);
+ digitalWrite(_rst, LOW);
+ delay(100);
+ digitalWrite(_rst, HIGH);
+ delay(200);
+ }
+
+#if defined(USE_SPI_DMA)
+ if(((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) &&
+ (dma.allocate() == DMA_STATUS_OK)) { // Allocate channel
+ // The DMA library needs to alloc at least one valid descriptor,
+ // so we do that here. It's not used in the usual sense though,
+ // just before a transfer we copy descriptor[0] to this address.
+ if(dptr = dma.addDescriptor(NULL, NULL, 42, DMA_BEAT_SIZE_BYTE,
+ false, false)) {
+ // Alloc 2 scanlines worth of pixels on display's major axis,
+ // whichever that is, rounding each up to 2-pixel boundary.
+ int major = (WIDTH > HEIGHT) ? WIDTH : HEIGHT;
+ major += (major & 1); // -> next 2-pixel bound, if needed.
+ maxFillLen = major * 2; // 2 scanlines
+ // Note to future self: if you decide to make the pixel buffer
+ // much larger, remember that DMA transfer descriptors can't
+ // exceed 65,535 bytes (not 65,536), meaning 32,767 pixels max.
+ // Not that we have that kind of RAM to throw around right now.
+ if((pixelBuf[0] =
+ (uint16_t *)malloc(maxFillLen * sizeof(uint16_t)))) {
+ // Alloc OK. Get pointer to start of second scanline.
+ pixelBuf[1] = &pixelBuf[0][major];
+ // Determine number of DMA descriptors needed to cover
+ // entire screen when entire 2-line pixelBuf is used
+ // (round up for fractional last descriptor).
+ int numDescriptors = (WIDTH * HEIGHT + (maxFillLen - 1)) /
+ maxFillLen;
+ // DMA descriptors MUST be 128-bit (16 byte) aligned.
+ // memalign() is considered obsolete but it's replacements
+ // (aligned_alloc() or posix_memalign()) are not currently
+ // available in the version of ARM GCC in use, but this
+ // is, so here we are.
+ if((descriptor = (DmacDescriptor *)memalign(16,
+ numDescriptors * sizeof(DmacDescriptor)))) {
+ int dmac_id;
+ volatile uint32_t *data_reg;
+
+ if(connection == TFT_HARD_SPI) {
+ // THIS IS AN AFFRONT TO NATURE, but I don't know
+ // any "clean" way to get the sercom number from the
+ // the SPIClass pointer (e.g. &SPI or &SPI1), which
+ // is all we have to work with. SPIClass does contain
+ // a SERCOM pointer but it is a PRIVATE member!
+ // Doing an UNSPEAKABLY HORRIBLE THING here, directly
+ // accessing the first 32-bit value in the SPIClass
+ // structure, knowing that's (currently) where the
+ // SERCOM pointer lives, but this ENTIRELY DEPENDS on
+ // that structure not changing nor the compiler
+ // rearranging things. Oh the humanity!
+
+ if(*(SERCOM **)hwspi._spi == &sercom0) {
+ dmac_id = SERCOM0_DMAC_ID_TX;
+ data_reg = &SERCOM0->SPI.DATA.reg;
+#if defined SERCOM1
+ } else if(*(SERCOM **)hwspi._spi == &sercom1) {
+ dmac_id = SERCOM1_DMAC_ID_TX;
+ data_reg = &SERCOM1->SPI.DATA.reg;
+#endif
+#if defined SERCOM2
+ } else if(*(SERCOM **)hwspi._spi == &sercom2) {
+ dmac_id = SERCOM2_DMAC_ID_TX;
+ data_reg = &SERCOM2->SPI.DATA.reg;
+#endif
+#if defined SERCOM3
+ } else if(*(SERCOM **)hwspi._spi == &sercom3) {
+ dmac_id = SERCOM3_DMAC_ID_TX;
+ data_reg = &SERCOM3->SPI.DATA.reg;
+#endif
+#if defined SERCOM4
+ } else if(*(SERCOM **)hwspi._spi == &sercom4) {
+ dmac_id = SERCOM4_DMAC_ID_TX;
+ data_reg = &SERCOM4->SPI.DATA.reg;
+#endif
+#if defined SERCOM5
+ } else if(*(SERCOM **)hwspi._spi == &sercom5) {
+ dmac_id = SERCOM5_DMAC_ID_TX;
+ data_reg = &SERCOM5->SPI.DATA.reg;
+#endif
+#if defined SERCOM6
+ } else if(*(SERCOM **)hwspi._spi == &sercom6) {
+ dmac_id = SERCOM6_DMAC_ID_TX;
+ data_reg = &SERCOM6->SPI.DATA.reg;
+#endif
+#if defined SERCOM7
+ } else if(*(SERCOM **)hwspi._spi == &sercom7) {
+ dmac_id = SERCOM7_DMAC_ID_TX;
+ data_reg = &SERCOM7->SPI.DATA.reg;
+#endif
+ }
+ dma.setPriority(DMA_PRIORITY_3);
+ dma.setTrigger(dmac_id);
+ dma.setAction(DMA_TRIGGER_ACTON_BEAT);
+
+ // Initialize descriptor list.
+ for(int d=0; dChannel[dmaChannel].CHEVCTRL.bit.EVOE = 1;
+ DMAC->Channel[dmaChannel].CHEVCTRL.bit.EVOMODE = 0;
+
+ // CONFIGURE TIMER/COUNTER (for write strobe)
+
+ Tc *timer = tcList[tcNum].tc; // -> Timer struct
+ int id = tcList[tcNum].gclk; // Timer GCLK ID
+ GCLK_PCHCTRL_Type pchctrl;
+
+ // Set up timer clock source from GCLK
+ GCLK->PCHCTRL[id].bit.CHEN = 0; // Stop timer
+ while(GCLK->PCHCTRL[id].bit.CHEN); // Wait for it
+ pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;
+ pchctrl.bit.CHEN = 1; // Enable
+ GCLK->PCHCTRL[id].reg = pchctrl.reg;
+ while(!GCLK->PCHCTRL[id].bit.CHEN); // Wait for it
+
+ // Disable timer/counter before configuring it
+ timer->COUNT8.CTRLA.bit.ENABLE = 0;
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+ timer->COUNT8.WAVE.bit.WAVEGEN = 2; // NPWM
+ timer->COUNT8.CTRLA.bit.MODE = 1; // 8-bit
+ timer->COUNT8.CTRLA.bit.PRESCALER = 0; // 1:1
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+ timer->COUNT8.CTRLBCLR.bit.DIR = 1; // Count UP
+ while(timer->COUNT8.SYNCBUSY.bit.CTRLB);
+ timer->COUNT8.CTRLBSET.bit.ONESHOT = 1; // One-shot
+ while(timer->COUNT8.SYNCBUSY.bit.CTRLB);
+ timer->COUNT8.PER.reg = 6; // PWM top
+ while(timer->COUNT8.SYNCBUSY.bit.PER);
+ timer->COUNT8.CC[0].reg = 2; // Compare
+ while(timer->COUNT8.SYNCBUSY.bit.CC0);
+ // Enable async input events,
+ // event action = restart.
+ timer->COUNT8.EVCTRL.bit.TCEI = 1;
+ timer->COUNT8.EVCTRL.bit.EVACT = 1;
+
+ // Enable timer
+ timer->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE;
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+#if(wrPeripheral == PIO_CCL)
+ // CONFIGURE CCL (inverts timer/counter output)
+
+ MCLK->APBCMASK.bit.CCL_ = 1; // Enable CCL clock
+ CCL->CTRL.bit.ENABLE = 0; // Disable to config
+ CCL->CTRL.bit.SWRST = 1; // Reset CCL registers
+ CCL->LUTCTRL[tcNum].bit.ENABLE = 0; // Disable LUT
+ CCL->LUTCTRL[tcNum].bit.FILTSEL = 0; // No filter
+ CCL->LUTCTRL[tcNum].bit.INSEL0 = 6; // TC input
+ CCL->LUTCTRL[tcNum].bit.INSEL1 = 0; // MASK
+ CCL->LUTCTRL[tcNum].bit.INSEL2 = 0; // MASK
+ CCL->LUTCTRL[tcNum].bit.TRUTH = 1; // Invert in 0
+ CCL->LUTCTRL[tcNum].bit.ENABLE = 1; // Enable LUT
+ CCL->CTRL.bit.ENABLE = 1; // Enable CCL
+#endif
+
+ // CONFIGURE EVENT SYSTEM
+
+ // Set up event system clock source from GCLK...
+ // Disable EVSYS, wait for disable
+ GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 0;
+ while(GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN);
+ pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;
+ pchctrl.bit.CHEN = 1; // Re-enable
+ GCLK->PCHCTRL[EVSYS_GCLK_ID_0].reg = pchctrl.reg;
+ // Wait for it, then enable EVSYS clock
+ while(!GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN);
+ MCLK->APBBMASK.bit.EVSYS_ = 1;
+
+ // Connect Timer EVU to ch 0
+ EVSYS->USER[tcList[tcNum].evu].reg = 1;
+ // Datasheet recommends single write operation;
+ // reg instead of bit. Also datasheet: PATH bits
+ // must be zero when using async!
+ EVSYS_CHANNEL_Type ev;
+ ev.reg = 0;
+ ev.bit.PATH = 2; // Asynchronous
+ ev.bit.EVGEN = 0x22 + dmaChannel; // DMA channel 0+
+ EVSYS->Channel[0].CHANNEL.reg = ev.reg;
+
+ // Initialize descriptor list.
+ for(int d=0; d= 0) SPI_CS_LOW();
+}
+
+/*!
+ @brief Call after issuing command(s) or data to display. Performs
+ chip-deselect (if required) and ends an SPI transaction (if
+ using hardware SPI and transactions are supported). Required
+ for all display types; not an SPI-specific function.
+*/
+void Adafruit_SPITFT::endWrite(void) {
+ if(_cs >= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+
+// -------------------------------------------------------------------------
+// Lower-level graphics operations. These functions require a chip-select
+// and/or SPI transaction around them (via startWrite(), endWrite() above).
+// Higher-level graphics primitives might start a single transaction and
+// then make multiple calls to these functions (e.g. circle or text
+// rendering might make repeated lines or rects) before ending the
+// transaction. It's more efficient than starting a transaction every time.
+
+/*!
+ @brief Draw a single pixel to the display at requested coordinates.
+ Not self-contained; should follow a startWrite() call.
+ @param x Horizontal position (0 = left).
+ @param y Vertical position (0 = top).
+ @param color 16-bit pixel color in '565' RGB format.
+*/
+void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) {
+ setAddrWindow(x, y, 1, 1);
+ SPI_WRITE16(color);
+ }
+}
+
+/*!
+ @brief Issue a series of pixels from memory to the display. Not self-
+ contained; should follow startWrite() and setAddrWindow() calls.
+ @param colors Pointer to array of 16-bit pixel values in '565' RGB
+ format.
+ @param len Number of elements in 'colors' array.
+ @param block If true (default case if unspecified), function blocks
+ until DMA transfer is complete. This is simply IGNORED
+ if DMA is not enabled. If false, the function returns
+ immediately after the last DMA transfer is started,
+ and one should use the dmaWait() function before
+ doing ANY other display-related activities (or even
+ any SPI-related activities, if using an SPI display
+ that shares the bus with other devices).
+ @param bigEndian If using DMA, and if set true, bitmap in memory is in
+ big-endian order (most significant byte first). By
+ default this is false, as most microcontrollers seem
+ to be little-endian and 16-bit pixel values must be
+ byte-swapped before issuing to the display (which tend
+ to be big-endian when using SPI or 8-bit parallel).
+ If an application can optimize around this -- for
+ example, a bitmap in a uint16_t array having the byte
+ values already reordered big-endian, this can save
+ some processing time here, ESPECIALLY if using this
+ function's non-blocking DMA mode. Not all cases are
+ covered...this is really here only for SAMD DMA and
+ much forethought on the application side.
+*/
+void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len,
+ bool block, bool bigEndian) {
+
+ if(!len) return; // Avoid 0-byte transfers
+
+#if defined(ESP32) // ESP32 has a special SPI pixel-writing function...
+ if(connection == TFT_HARD_SPI) {
+ hwspi._spi->writePixels(colors, len * 2);
+ return;
+ }
+#elif defined(USE_SPI_DMA)
+ if((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) {
+ int maxSpan = maxFillLen / 2; // One scanline max
+ uint8_t pixelBufIdx = 0; // Active pixel buffer number
+ #if defined(__SAMD51__)
+ if(connection == TFT_PARALLEL) {
+ // Switch WR pin to PWM or CCL
+ pinPeripheral(tft8._wr, wrPeripheral);
+ }
+ #endif // end __SAMD51__
+ if(!bigEndian) { // Normal little-endian situation...
+ while(len) {
+ int count = (len < maxSpan) ? len : maxSpan;
+
+ // Because TFT and SAMD endianisms are different, must swap
+ // bytes from the 'colors' array passed into a DMA working
+ // buffer. This can take place while the prior DMA transfer
+ // is in progress, hence the need for two pixelBufs.
+ for(int i=0; isetDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__ || _SAMD21_
+ }
+ return;
+ }
+#endif // end USE_SPI_DMA
+
+ // All other cases (bitbang SPI or non-DMA hard SPI or parallel),
+ // use a loop with the normal 16-bit data write function:
+ while(len--) {
+ SPI_WRITE16(*colors++);
+ }
+}
+
+/*!
+ @brief Wait for the last DMA transfer in a prior non-blocking
+ writePixels() call to complete. This does nothing if DMA
+ is not enabled, and is not needed if blocking writePixels()
+ was used (as is the default case).
+*/
+void Adafruit_SPITFT::dmaWait(void) {
+#if defined(USE_SPI_DMA)
+ while(dma_busy);
+ #if defined(__SAMD51__) || defined(_SAMD21_)
+ if(connection == TFT_HARD_SPI) {
+ // See SAMD51/21 note in writeColor()
+ hwspi._spi->setDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__ || _SAMD21_
+#endif
+}
+
+/*!
+ @brief Issue a series of pixels, all the same color. Not self-
+ contained; should follow startWrite() and setAddrWindow() calls.
+ @param color 16-bit pixel color in '565' RGB format.
+ @param len Number of pixels to draw.
+*/
+void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) {
+
+ if(!len) return; // Avoid 0-byte transfers
+
+ uint8_t hi = color >> 8, lo = color;
+
+#if defined(ESP32) // ESP32 has a special SPI pixel-writing function...
+ if(connection == TFT_HARD_SPI) {
+ #define SPI_MAX_PIXELS_AT_ONCE 32
+ #define TMPBUF_LONGWORDS (SPI_MAX_PIXELS_AT_ONCE + 1) / 2
+ #define TMPBUF_PIXELS (TMPBUF_LONGWORDS * 2)
+ static uint32_t temp[TMPBUF_LONGWORDS];
+ uint32_t c32 = color * 0x00010001;
+ uint16_t bufLen = (len < TMPBUF_PIXELS) ? len : TMPBUF_PIXELS,
+ xferLen, fillLen;
+ // Fill temp buffer 32 bits at a time
+ fillLen = (bufLen + 1) / 2; // Round up to next 32-bit boundary
+ for(uint32_t t=0; t= 16)) { // Don't bother with DMA on short pixel runs
+ int i, d, numDescriptors;
+ if(hi == lo) { // If high & low bytes are same...
+ onePixelBuf = color;
+ // Can do this with a relatively short descriptor list,
+ // each transferring a max of 32,767 (not 32,768) pixels.
+ // This won't run off the end of the allocated descriptor list,
+ // since we're using much larger chunks per descriptor here.
+ numDescriptors = (len + 32766) / 32767;
+ for(d=0; d lastFillLen) {
+ int fillStart = lastFillLen / 2,
+ fillEnd = (((len < maxFillLen) ?
+ len : maxFillLen) + 1) / 2;
+ for(i=fillStart; isetDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__
+ return;
+ }
+ #endif // end USE_SPI_DMA
+#endif // end !ESP32
+
+ // All other cases (non-DMA hard SPI, bitbang SPI, parallel)...
+
+ if(connection == TFT_HARD_SPI) {
+#if defined(ESP8266)
+ do {
+ uint32_t pixelsThisPass = len;
+ if(pixelsThisPass > 50000) pixelsThisPass = 50000;
+ len -= pixelsThisPass;
+ yield(); // Periodic yield() on long fills
+ while(pixelsThisPass--) {
+ hwspi._spi->write(hi);
+ hwspi._spi->write(lo);
+ }
+ } while(len);
+#else // !ESP8266
+ while(len--) {
+ #if defined(__AVR__)
+ AVR_WRITESPI(hi);
+ AVR_WRITESPI(lo);
+ #elif defined(ESP32)
+ hwspi._spi->write(hi);
+ hwspi._spi->write(lo);
+ #else
+ hwspi._spi->transfer(hi);
+ hwspi._spi->transfer(lo);
+ #endif
+ }
+#endif // end !ESP8266
+ } else if(connection == TFT_SOFT_SPI) {
+#if defined(ESP8266)
+ do {
+ uint32_t pixelsThisPass = len;
+ if(pixelsThisPass > 20000) pixelsThisPass = 20000;
+ len -= pixelsThisPass;
+ yield(); // Periodic yield() on long fills
+ while(pixelsThisPass--) {
+ for(uint16_t bit=0, x=color; bit<16; bit++) {
+ if(x & 0x8000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ }
+ } while(len);
+#else // !ESP8266
+ while(len--) {
+ #if defined(__AVR__)
+ for(uint8_t bit=0, x=hi; bit<8; bit++) {
+ if(x & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ for(uint8_t bit=0, x=lo; bit<8; bit++) {
+ if(x & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ #else // !__AVR__
+ for(uint16_t bit=0, x=color; bit<16; bit++) {
+ if(x & 0x8000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ x <<= 1;
+ SPI_SCK_LOW();
+ }
+ #endif // end !__AVR__
+ }
+#endif // end !ESP8266
+ } else { // PARALLEL
+ if(hi == lo) {
+#if defined(__AVR__)
+ len *= 2;
+ *tft8.writePort = hi;
+ while(len--) {
+ TFT_WR_STROBE();
+ }
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ len *= 2;
+ *tft8.writePort = hi;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = color;
+ }
+ while(len--) {
+ TFT_WR_STROBE();
+ }
+#endif
+ } else {
+ while(len--) {
+#if defined(__AVR__)
+ *tft8.writePort = hi;
+ TFT_WR_STROBE();
+ *tft8.writePort = lo;
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ *tft8.writePort = hi;
+ TFT_WR_STROBE();
+ *tft8.writePort = lo;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = color;
+ }
+#endif
+ TFT_WR_STROBE();
+ }
+ }
+ }
+}
+
+/*!
+ @brief Draw a filled rectangle to the display. Not self-contained;
+ should follow startWrite(). Typically used by higher-level
+ graphics primitives; user code shouldn't need to call this and
+ is likely to use the self-contained fillRect() instead.
+ writeFillRect() performs its own edge clipping and rejection;
+ see writeFillRectPreclipped() for a more 'raw' implementation.
+ @param x Horizontal position of first corner.
+ @param y Vertical position of first corner.
+ @param w Rectangle width in pixels (positive = right of first
+ corner, negative = left of first corner).
+ @param h Rectangle height in pixels (positive = below first
+ corner, negative = above first corner).
+ @param color 16-bit fill color in '565' RGB format.
+ @note Written in this deep-nested way because C by definition will
+ optimize for the 'if' case, not the 'else' -- avoids branches
+ and rejects clipped rectangles at the least-work possibility.
+*/
+void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y,
+ int16_t w, int16_t h, uint16_t color) {
+ if(w && h) { // Nonzero width and height?
+ if(w < 0) { // If negative width...
+ x += w + 1; // Move X to left edge
+ w = -w; // Use positive width
+ }
+ if(x < _width) { // Not off right
+ if(h < 0) { // If negative height...
+ y += h + 1; // Move Y to top edge
+ h = -h; // Use positive height
+ }
+ if(y < _height) { // Not off bottom
+ int16_t x2 = x + w - 1;
+ if(x2 >= 0) { // Not off left
+ int16_t y2 = y + h - 1;
+ if(y2 >= 0) { // Not off top
+ // Rectangle partly or fully overlaps screen
+ if(x < 0) { x = 0; w = x2 + 1; } // Clip left
+ if(y < 0) { y = 0; h = y2 + 1; } // Clip top
+ if(x2 >= _width) { w = _width - x; } // Clip right
+ if(y2 >= _height) { h = _height - y; } // Clip bottom
+ writeFillRectPreclipped(x, y, w, h, color);
+ }
+ }
+ }
+ }
+ }
+}
+
+/*!
+ @brief Draw a horizontal line on the display. Performs edge clipping
+ and rejection. Not self-contained; should follow startWrite().
+ Typically used by higher-level graphics primitives; user code
+ shouldn't need to call this and is likely to use the self-
+ contained drawFastHLine() instead.
+ @param x Horizontal position of first point.
+ @param y Vertical position of first point.
+ @param w Line width in pixels (positive = right of first point,
+ negative = point of first corner).
+ @param color 16-bit line color in '565' RGB format.
+*/
+void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w,
+ uint16_t color) {
+ if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width
+ if(w < 0) { // If negative width...
+ x += w + 1; // Move X to left edge
+ w = -w; // Use positive width
+ }
+ if(x < _width) { // Not off right
+ int16_t x2 = x + w - 1;
+ if(x2 >= 0) { // Not off left
+ // Line partly or fully overlaps screen
+ if(x < 0) { x = 0; w = x2 + 1; } // Clip left
+ if(x2 >= _width) { w = _width - x; } // Clip right
+ writeFillRectPreclipped(x, y, w, 1, color);
+ }
+ }
+ }
+}
+
+/*!
+ @brief Draw a vertical line on the display. Performs edge clipping and
+ rejection. Not self-contained; should follow startWrite().
+ Typically used by higher-level graphics primitives; user code
+ shouldn't need to call this and is likely to use the self-
+ contained drawFastVLine() instead.
+ @param x Horizontal position of first point.
+ @param y Vertical position of first point.
+ @param h Line height in pixels (positive = below first point,
+ negative = above first point).
+ @param color 16-bit line color in '565' RGB format.
+*/
+void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h,
+ uint16_t color) {
+ if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height
+ if(h < 0) { // If negative height...
+ y += h + 1; // Move Y to top edge
+ h = -h; // Use positive height
+ }
+ if(y < _height) { // Not off bottom
+ int16_t y2 = y + h - 1;
+ if(y2 >= 0) { // Not off top
+ // Line partly or fully overlaps screen
+ if(y < 0) { y = 0; h = y2 + 1; } // Clip top
+ if(y2 >= _height) { h = _height - y; } // Clip bottom
+ writeFillRectPreclipped(x, y, 1, h, color);
+ }
+ }
+ }
+}
+
+/*!
+ @brief A lower-level version of writeFillRect(). This version requires
+ all inputs are in-bounds, that width and height are positive,
+ and no part extends offscreen. NO EDGE CLIPPING OR REJECTION IS
+ PERFORMED. If higher-level graphics primitives are written to
+ handle their own clipping earlier in the drawing process, this
+ can avoid unnecessary function calls and repeated clipping
+ operations in the lower-level functions.
+ @param x Horizontal position of first corner. MUST BE WITHIN
+ SCREEN BOUNDS.
+ @param y Vertical position of first corner. MUST BE WITHIN SCREEN
+ BOUNDS.
+ @param w Rectangle width in pixels. MUST BE POSITIVE AND NOT
+ EXTEND OFF SCREEN.
+ @param h Rectangle height in pixels. MUST BE POSITIVE AND NOT
+ EXTEND OFF SCREEN.
+ @param color 16-bit fill color in '565' RGB format.
+ @note This is a new function, no graphics primitives besides rects
+ and horizontal/vertical lines are written to best use this yet.
+*/
+inline void Adafruit_SPITFT::writeFillRectPreclipped(int16_t x, int16_t y,
+ int16_t w, int16_t h, uint16_t color) {
+ setAddrWindow(x, y, w, h);
+ writeColor(color, (uint32_t)w * h);
+}
+
+
+// -------------------------------------------------------------------------
+// Ever-so-slightly higher-level graphics operations. Similar to the 'write'
+// functions above, but these contain their own chip-select and SPI
+// transactions as needed (via startWrite(), endWrite()). They're typically
+// used solo -- as graphics primitives in themselves, not invoked by higher-
+// level primitives (which should use the functions above for better
+// performance).
+
+/*!
+ @brief Draw a single pixel to the display at requested coordinates.
+ Self-contained and provides its own transaction as needed
+ (see writePixel(x,y,color) for a lower-level variant).
+ Edge clipping is performed here.
+ @param x Horizontal position (0 = left).
+ @param y Vertical position (0 = top).
+ @param color 16-bit pixel color in '565' RGB format.
+*/
+void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color) {
+ // Clip first...
+ if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) {
+ // THEN set up transaction (if needed) and draw...
+ startWrite();
+ setAddrWindow(x, y, 1, 1);
+ SPI_WRITE16(color);
+ endWrite();
+ }
+}
+
+/*!
+ @brief Draw a filled rectangle to the display. Self-contained and
+ provides its own transaction as needed (see writeFillRect() or
+ writeFillRectPreclipped() for lower-level variants). Edge
+ clipping and rejection is performed here.
+ @param x Horizontal position of first corner.
+ @param y Vertical position of first corner.
+ @param w Rectangle width in pixels (positive = right of first
+ corner, negative = left of first corner).
+ @param h Rectangle height in pixels (positive = below first
+ corner, negative = above first corner).
+ @param color 16-bit fill color in '565' RGB format.
+ @note This repeats the writeFillRect() function almost in its entirety,
+ with the addition of a transaction start/end. It's done this way
+ (rather than starting the transaction and calling writeFillRect()
+ to handle clipping and so forth) so that the transaction isn't
+ performed at all if the rectangle is rejected. It's really not
+ that much code.
+*/
+void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
+ uint16_t color) {
+ if(w && h) { // Nonzero width and height?
+ if(w < 0) { // If negative width...
+ x += w + 1; // Move X to left edge
+ w = -w; // Use positive width
+ }
+ if(x < _width) { // Not off right
+ if(h < 0) { // If negative height...
+ y += h + 1; // Move Y to top edge
+ h = -h; // Use positive height
+ }
+ if(y < _height) { // Not off bottom
+ int16_t x2 = x + w - 1;
+ if(x2 >= 0) { // Not off left
+ int16_t y2 = y + h - 1;
+ if(y2 >= 0) { // Not off top
+ // Rectangle partly or fully overlaps screen
+ if(x < 0) { x = 0; w = x2 + 1; } // Clip left
+ if(y < 0) { y = 0; h = y2 + 1; } // Clip top
+ if(x2 >= _width) { w = _width - x; } // Clip right
+ if(y2 >= _height) { h = _height - y; } // Clip bottom
+ startWrite();
+ writeFillRectPreclipped(x, y, w, h, color);
+ endWrite();
+ }
+ }
+ }
+ }
+ }
+}
+
+/*!
+ @brief Draw a horizontal line on the display. Self-contained and
+ provides its own transaction as needed (see writeFastHLine() for
+ a lower-level variant). Edge clipping and rejection is performed
+ here.
+ @param x Horizontal position of first point.
+ @param y Vertical position of first point.
+ @param w Line width in pixels (positive = right of first point,
+ negative = point of first corner).
+ @param color 16-bit line color in '565' RGB format.
+ @note This repeats the writeFastHLine() function almost in its
+ entirety, with the addition of a transaction start/end. It's
+ done this way (rather than starting the transaction and calling
+ writeFastHLine() to handle clipping and so forth) so that the
+ transaction isn't performed at all if the line is rejected.
+*/
+void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, int16_t w,
+ uint16_t color) {
+ if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width
+ if(w < 0) { // If negative width...
+ x += w + 1; // Move X to left edge
+ w = -w; // Use positive width
+ }
+ if(x < _width) { // Not off right
+ int16_t x2 = x + w - 1;
+ if(x2 >= 0) { // Not off left
+ // Line partly or fully overlaps screen
+ if(x < 0) { x = 0; w = x2 + 1; } // Clip left
+ if(x2 >= _width) { w = _width - x; } // Clip right
+ startWrite();
+ writeFillRectPreclipped(x, y, w, 1, color);
+ endWrite();
+ }
+ }
+ }
+}
+
+/*!
+ @brief Draw a vertical line on the display. Self-contained and provides
+ its own transaction as needed (see writeFastHLine() for a lower-
+ level variant). Edge clipping and rejection is performed here.
+ @param x Horizontal position of first point.
+ @param y Vertical position of first point.
+ @param h Line height in pixels (positive = below first point,
+ negative = above first point).
+ @param color 16-bit line color in '565' RGB format.
+ @note This repeats the writeFastVLine() function almost in its
+ entirety, with the addition of a transaction start/end. It's
+ done this way (rather than starting the transaction and calling
+ writeFastVLine() to handle clipping and so forth) so that the
+ transaction isn't performed at all if the line is rejected.
+*/
+void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, int16_t h,
+ uint16_t color) {
+ if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height
+ if(h < 0) { // If negative height...
+ y += h + 1; // Move Y to top edge
+ h = -h; // Use positive height
+ }
+ if(y < _height) { // Not off bottom
+ int16_t y2 = y + h - 1;
+ if(y2 >= 0) { // Not off top
+ // Line partly or fully overlaps screen
+ if(y < 0) { y = 0; h = y2 + 1; } // Clip top
+ if(y2 >= _height) { h = _height - y; } // Clip bottom
+ startWrite();
+ writeFillRectPreclipped(x, y, 1, h, color);
+ endWrite();
+ }
+ }
+ }
+}
+
+/*!
+ @brief Essentially writePixel() with a transaction around it. I don't
+ think this is in use by any of our code anymore (believe it was
+ for some older BMP-reading examples), but is kept here in case
+ any user code relies on it. Consider it DEPRECATED.
+ @param color 16-bit pixel color in '565' RGB format.
+*/
+void Adafruit_SPITFT::pushColor(uint16_t color) {
+ startWrite();
+ SPI_WRITE16(color);
+ endWrite();
+}
+
+/*!
+ @brief Draw a 16-bit image (565 RGB) at the specified (x,y) position.
+ For 16-bit display devices; no color reduction performed.
+ Adapted from https://github.com/PaulStoffregen/ILI9341_t3
+ by Marc MERLIN. See examples/pictureEmbed to use this.
+ 5/6/2017: function name and arguments have changed for
+ compatibility with current GFX library and to avoid naming
+ problems in prior implementation. Formerly drawBitmap() with
+ arguments in different order. Handles its own transaction and
+ edge clipping/rejection.
+ @param x Top left corner horizontal coordinate.
+ @param y Top left corner vertical coordinate.
+ @param pcolors Pointer to 16-bit array of pixel values.
+ @param w Width of bitmap in pixels.
+ @param h Height of bitmap in pixels.
+*/
+void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y,
+ uint16_t *pcolors, int16_t w, int16_t h) {
+
+ int16_t x2, y2; // Lower-right coord
+ if(( x >= _width ) || // Off-edge right
+ ( y >= _height) || // " top
+ ((x2 = (x+w-1)) < 0 ) || // " left
+ ((y2 = (y+h-1)) < 0) ) return; // " bottom
+
+ int16_t bx1=0, by1=0, // Clipped top-left within bitmap
+ saveW=w; // Save original bitmap width value
+ if(x < 0) { // Clip left
+ w += x;
+ bx1 = -x;
+ x = 0;
+ }
+ if(y < 0) { // Clip top
+ h += y;
+ by1 = -y;
+ y = 0;
+ }
+ if(x2 >= _width ) w = _width - x; // Clip right
+ if(y2 >= _height) h = _height - y; // Clip bottom
+
+ pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left
+ startWrite();
+ setAddrWindow(x, y, w, h); // Clipped area
+ while(h--) { // For each (clipped) scanline...
+ writePixels(pcolors, w); // Push one (clipped) row
+ pcolors += saveW; // Advance pointer by one full (unclipped) line
+ }
+ endWrite();
+}
+
+
+// -------------------------------------------------------------------------
+// Miscellaneous class member functions that don't draw anything.
+
+/*!
+ @brief Invert the colors of the display (if supported by hardware).
+ Self-contained, no transaction setup required.
+ @param i true = inverted display, false = normal display.
+*/
+void Adafruit_SPITFT::invertDisplay(bool i) {
+ startWrite();
+ writeCommand(i ? invertOnCommand : invertOffCommand);
+ endWrite();
+}
+
+/*!
+ @brief Given 8-bit red, green and blue values, return a 'packed'
+ 16-bit color value in '565' RGB format (5 bits red, 6 bits
+ green, 5 bits blue). This is just a mathematical operation,
+ no hardware is touched.
+ @param red 8-bit red brightnesss (0 = off, 255 = max).
+ @param green 8-bit green brightnesss (0 = off, 255 = max).
+ @param blue 8-bit blue brightnesss (0 = off, 255 = max).
+ @return 'Packed' 16-bit color value (565 format).
+*/
+uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) {
+ return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
+}
+
+/*!
+ @brief Adafruit_SPITFT Send Command handles complete sending of commands and data
+ @param commandByte The Command Byte
+ @param dataBytes A pointer to the Data bytes to send
+ @param numDataBytes The number of bytes we should send
+ */
+void Adafruit_SPITFT::sendCommand(uint8_t commandByte, uint8_t *dataBytes, uint8_t numDataBytes) {
+ SPI_BEGIN_TRANSACTION();
+ if(_cs >= 0) SPI_CS_LOW();
+
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte); // Send the command byte
+
+ SPI_DC_HIGH();
+ for (int i=0; i= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+/*!
+ @brief Adafruit_SPITFT Send Command handles complete sending of commands and const data
+ @param commandByte The Command Byte
+ @param dataBytes A pointer to the Data bytes to send
+ @param numDataBytes The number of bytes we should send
+ */
+void Adafruit_SPITFT::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
+ SPI_BEGIN_TRANSACTION();
+ if(_cs >= 0) SPI_CS_LOW();
+
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte); // Send the command byte
+
+ SPI_DC_HIGH();
+ for (int i=0; i= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+/*!
+ @brief Read 8 bits of data from display configuration memory (not RAM).
+ This is highly undocumented/supported and should be avoided,
+ function is only included because some of the examples use it.
+ @param commandByte
+ The command register to read data from.
+ @param index
+ The byte index into the command to read from.
+ @return Unsigned 8-bit data read from display register.
+ */
+/**************************************************************************/
+uint8_t Adafruit_SPITFT::readcommand8(uint8_t commandByte, uint8_t index) {
+ uint8_t result;
+ startWrite();
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte);
+ SPI_DC_HIGH(); // Data mode
+ do {
+ result = spiRead();
+ } while(index--); // Discard bytes up to index'th
+ endWrite();
+ return result;
+}
+
+// -------------------------------------------------------------------------
+// Lowest-level hardware-interfacing functions. Many of these are inline and
+// compile to different things based on #defines -- typically just a few
+// instructions. Others, not so much, those are not inlined.
+
+/*!
+ @brief Start an SPI transaction if using the hardware SPI interface to
+ the display. If using an earlier version of the Arduino platform
+ (before the addition of SPI transactions), this instead attempts
+ to set up the SPI clock and mode. No action is taken if the
+ connection is not hardware SPI-based. This does NOT include a
+ chip-select operation -- see startWrite() for a function that
+ encapsulated both actions.
+*/
+inline void Adafruit_SPITFT::SPI_BEGIN_TRANSACTION(void) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(SPI_HAS_TRANSACTION)
+ hwspi._spi->beginTransaction(hwspi.settings);
+#else // No transactions, configure SPI manually...
+ #if defined(__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1)
+ hwspi._spi->setClockDivider(SPI_CLOCK_DIV2);
+ #elif defined(__arm__)
+ hwspi._spi->setClockDivider(11);
+ #elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->setFrequency(hwspi._freq);
+ #elif defined(RASPI) || defined(ARDUINO_ARCH_STM32F1)
+ hwspi._spi->setClock(hwspi._freq);
+ #endif
+ hwspi._spi->setBitOrder(MSBFIRST);
+ hwspi._spi->setDataMode(hwspi._mode);
+#endif // end !SPI_HAS_TRANSACTION
+ }
+}
+
+/*!
+ @brief End an SPI transaction if using the hardware SPI interface to
+ the display. No action is taken if the connection is not
+ hardware SPI-based or if using an earlier version of the Arduino
+ platform (before the addition of SPI transactions). This does
+ NOT include a chip-deselect operation -- see endWrite() for a
+ function that encapsulated both actions.
+*/
+inline void Adafruit_SPITFT::SPI_END_TRANSACTION(void) {
+#if defined(SPI_HAS_TRANSACTION)
+ if(connection == TFT_HARD_SPI) {
+ hwspi._spi->endTransaction();
+ }
+#endif
+}
+
+/*!
+ @brief Issue a single 8-bit value to the display. Chip-select,
+ transaction and data/command selection must have been
+ previously set -- this ONLY issues the byte. This is another of
+ those functions in the library with a now-not-accurate name
+ that's being maintained for compatibility with outside code.
+ This function is used even if display connection is parallel.
+ @param b 8-bit value to write.
+*/
+void Adafruit_SPITFT::spiWrite(uint8_t b) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(b);
+#elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->write(b);
+#else
+ hwspi._spi->transfer(b);
+#endif
+ } else if(connection == TFT_SOFT_SPI) {
+ for(uint8_t bit=0; bit<8; bit++) {
+ if(b & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ b <<= 1;
+ SPI_SCK_LOW();
+ }
+ } else { // TFT_PARALLEL
+#if defined(__AVR__)
+ *tft8.writePort = b;
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) *tft8.writePort = b;
+ else *(volatile uint16_t *)tft8.writePort = b;
+#endif
+ TFT_WR_STROBE();
+ }
+}
+
+/*!
+ @brief Write a single command byte to the display. Chip-select and
+ transaction must have been previously set -- this ONLY sets
+ the device to COMMAND mode, issues the byte and then restores
+ DATA mode. There is no corresponding explicit writeData()
+ function -- just use spiWrite().
+ @param cmd 8-bit command to write.
+*/
+void Adafruit_SPITFT::writeCommand(uint8_t cmd) {
+ SPI_DC_LOW();
+ spiWrite(cmd);
+ SPI_DC_HIGH();
+}
+
+/*!
+ @brief Read a single 8-bit value from the display. Chip-select and
+ transaction must have been previously set -- this ONLY reads
+ the byte. This is another of those functions in the library
+ with a now-not-accurate name that's being maintained for
+ compatibility with outside code. This function is used even if
+ display connection is parallel.
+ @return Unsigned 8-bit value read (always zero if USE_FAST_PINIO is
+ not supported by the MCU architecture).
+*/
+uint8_t Adafruit_SPITFT::spiRead(void) {
+ uint8_t b = 0;
+ uint16_t w = 0;
+ if(connection == TFT_HARD_SPI) {
+ return hwspi._spi->transfer((uint8_t)0);
+ } else if(connection == TFT_SOFT_SPI) {
+ if(swspi._miso >= 0) {
+ for(uint8_t i=0; i<8; i++) {
+ SPI_SCK_HIGH();
+ b <<= 1;
+ if(SPI_MISO_READ()) b++;
+ SPI_SCK_LOW();
+ }
+ }
+ return b;
+ } else { // TFT_PARALLEL
+ if(tft8._rd >= 0) {
+#if defined(USE_FAST_PINIO)
+ TFT_RD_LOW(); // Read line LOW
+ #if defined(__AVR__)
+ *tft8.portDir = 0x00; // Set port to input state
+ w = *tft8.readPort; // Read value from port
+ *tft8.portDir = 0xFF; // Restore port to output
+ #else // !__AVR__
+ if(!tft8.wide) { // 8-bit TFT connection
+ #if defined(HAS_PORT_SET_CLR)
+ *tft8.dirClr = 0xFF; // Set port to input state
+ w = *tft8.readPort; // Read value from port
+ *tft8.dirSet = 0xFF; // Restore port to output
+ #else // !HAS_PORT_SET_CLR
+ *tft8.portDir = 0x00; // Set port to input state
+ w = *tft8.readPort; // Read value from port
+ *tft8.portDir = 0xFF; // Restore port to output
+ #endif // end HAS_PORT_SET_CLR
+ } else { // 16-bit TFT connection
+ #if defined(HAS_PORT_SET_CLR)
+ *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state
+ w = *(volatile uint16_t *)tft8.readPort; // 16-bit read
+ *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state
+ #else // !HAS_PORT_SET_CLR
+ *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state
+ w = *(volatile uint16_t *)tft8.readPort; // 16-bit read
+ *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state
+ #endif // end !HAS_PORT_SET_CLR
+ }
+ TFT_RD_HIGH(); // Read line HIGH
+ #endif // end !__AVR__
+#else // !USE_FAST_PINIO
+ w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO
+#endif // end !USE_FAST_PINIO
+ }
+ return w;
+ }
+}
+
+/*!
+ @brief Set the software (bitbang) SPI MOSI line HIGH.
+*/
+inline void Adafruit_SPITFT::SPI_MOSI_HIGH(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *swspi.mosiPortSet = 1;
+ #else // !KINETISK
+ *swspi.mosiPortSet = swspi.mosiPinMask;
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.mosiPort |= swspi.mosiPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._mosi, HIGH);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Set the software (bitbang) SPI MOSI line LOW.
+*/
+inline void Adafruit_SPITFT::SPI_MOSI_LOW(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *swspi.mosiPortClr = 1;
+ #else // !KINETISK
+ *swspi.mosiPortClr = swspi.mosiPinMask;
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.mosiPort &= swspi.mosiPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._mosi, LOW);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Set the software (bitbang) SPI SCK line HIGH.
+*/
+inline void Adafruit_SPITFT::SPI_SCK_HIGH(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *swspi.sckPortSet = 1;
+ #else // !KINETISK
+ *swspi.sckPortSet = swspi.sckPinMask;
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.sckPort |= swspi.sckPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._sck, HIGH);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Set the software (bitbang) SPI SCK line LOW.
+*/
+inline void Adafruit_SPITFT::SPI_SCK_LOW(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *swspi.sckPortClr = 1;
+ #else // !KINETISK
+ *swspi.sckPortClr = swspi.sckPinMask;
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.sckPort &= swspi.sckPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._sck, LOW);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Read the state of the software (bitbang) SPI MISO line.
+ @return true if HIGH, false if LOW.
+*/
+inline bool Adafruit_SPITFT::SPI_MISO_READ(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(KINETISK)
+ return *swspi.misoPort;
+ #else // !KINETISK
+ return *swspi.misoPort & swspi.misoPinMask;
+ #endif // end !KINETISK
+#else // !USE_FAST_PINIO
+ return digitalRead(swspi._miso);
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Issue a single 16-bit value to the display. Chip-select,
+ transaction and data/command selection must have been
+ previously set -- this ONLY issues the word. Despite the name,
+ this function is used even if display connection is parallel;
+ name was maintaned for backward compatibility. Naming is also
+ not consistent with the 8-bit version, spiWrite(). Sorry about
+ that. Again, staying compatible with outside code.
+ @param w 16-bit value to write.
+*/
+void Adafruit_SPITFT::SPI_WRITE16(uint16_t w) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(w >> 8);
+ AVR_WRITESPI(w);
+#elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->write16(w);
+#else
+ hwspi._spi->transfer(w >> 8);
+ hwspi._spi->transfer(w);
+#endif
+ } else if(connection == TFT_SOFT_SPI) {
+ for(uint8_t bit=0; bit<16; bit++) {
+ if(w & 0x8000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ w <<= 1;
+ }
+ } else { // TFT_PARALLEL
+#if defined(__AVR__)
+ *tft8.writePort = w >> 8;
+ TFT_WR_STROBE();
+ *tft8.writePort = w;
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ *tft8.writePort = w >> 8;
+ TFT_WR_STROBE();
+ *tft8.writePort = w;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = w;
+ }
+#endif
+ TFT_WR_STROBE();
+ }
+}
+
+/*!
+ @brief Issue a single 32-bit value to the display. Chip-select,
+ transaction and data/command selection must have been
+ previously set -- this ONLY issues the longword. Despite the
+ name, this function is used even if display connection is
+ parallel; name was maintaned for backward compatibility. Naming
+ is also not consistent with the 8-bit version, spiWrite().
+ Sorry about that. Again, staying compatible with outside code.
+ @param l 32-bit value to write.
+*/
+void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(l >> 24);
+ AVR_WRITESPI(l >> 16);
+ AVR_WRITESPI(l >> 8);
+ AVR_WRITESPI(l );
+#elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->write32(l);
+#else
+ hwspi._spi->transfer(l >> 24);
+ hwspi._spi->transfer(l >> 16);
+ hwspi._spi->transfer(l >> 8);
+ hwspi._spi->transfer(l);
+#endif
+ } else if(connection == TFT_SOFT_SPI) {
+ for(uint8_t bit=0; bit<32; bit++) {
+ if(l & 0x80000000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ l <<= 1;
+ }
+ } else { // TFT_PARALLEL
+#if defined(__AVR__)
+ *tft8.writePort = l >> 24;
+ TFT_WR_STROBE();
+ *tft8.writePort = l >> 16;
+ TFT_WR_STROBE();
+ *tft8.writePort = l >> 8;
+ TFT_WR_STROBE();
+ *tft8.writePort = l;
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ *tft8.writePort = l >> 24;
+ TFT_WR_STROBE();
+ *tft8.writePort = l >> 16;
+ TFT_WR_STROBE();
+ *tft8.writePort = l >> 8;
+ TFT_WR_STROBE();
+ *tft8.writePort = l;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = l >> 16;
+ TFT_WR_STROBE();
+ *(volatile uint16_t *)tft8.writePort = l;
+ }
+#endif
+ TFT_WR_STROBE();
+ }
+}
+
+/*!
+ @brief Set the WR line LOW, then HIGH. Used for parallel-connected
+ interfaces when writing data.
+*/
+inline void Adafruit_SPITFT::TFT_WR_STROBE(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *tft8.wrPortClr = 1;
+ *tft8.wrPortSet = 1;
+ #else // !KINETISK
+ *tft8.wrPortClr = tft8.wrPinMask;
+ *tft8.wrPortSet = tft8.wrPinMask;
+ #endif // end !KINETISK
+ #else // !HAS_PORT_SET_CLR
+ *tft8.wrPort &= tft8.wrPinMaskClr;
+ *tft8.wrPort |= tft8.wrPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(tft8._wr, LOW);
+ digitalWrite(tft8._wr, HIGH);
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Set the RD line HIGH. Used for parallel-connected interfaces
+ when reading data.
+*/
+inline void Adafruit_SPITFT::TFT_RD_HIGH(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ *tft8.rdPortSet = tft8.rdPinMask;
+ #else // !HAS_PORT_SET_CLR
+ *tft8.rdPort |= tft8.rdPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(tft8._rd, HIGH);
+#endif // end !USE_FAST_PINIO
+}
+
+/*!
+ @brief Set the RD line LOW. Used for parallel-connected interfaces
+ when reading data.
+*/
+inline void Adafruit_SPITFT::TFT_RD_LOW(void) {
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ *tft8.rdPortClr = tft8.rdPinMask;
+ #else // !HAS_PORT_SET_CLR
+ *tft8.rdPort &= tft8.rdPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(tft8._rd, LOW);
+#endif // end !USE_FAST_PINIO
+}
+
+#endif // end __AVR_ATtiny85__
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h
new file mode 100644
index 000000000..dcfc1646b
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h
@@ -0,0 +1,520 @@
+/*!
+ * @file Adafruit_SPITFT.h
+ *
+ * Part of Adafruit's GFX graphics library. Originally this class was
+ * written to handle a range of color TFT displays connected via SPI,
+ * but over time this library and some display-specific subclasses have
+ * mutated to include some color OLEDs as well as parallel-interfaced
+ * displays. The name's been kept for the sake of older code.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Limor "ladyada" Fried for Adafruit Industries,
+ * with contributions from the open source community.
+ *
+ * BSD license, all text here must be included in any redistribution.
+ */
+
+#ifndef _ADAFRUIT_SPITFT_H_
+#define _ADAFRUIT_SPITFT_H_
+
+#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
+
+#include
+#include "Adafruit_GFX.h"
+#include "renderer.h"
+
+// HARDWARE CONFIG ---------------------------------------------------------
+
+#if defined(__AVR__)
+ typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
+ #define USE_FAST_PINIO ///< Use direct PORT register access
+#elif defined(ARDUINO_STM32_FEATHER) // WICED
+ typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+#elif defined(__arm__)
+ #if defined(ARDUINO_ARCH_SAMD)
+ // Adafruit M0, M4
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+ #define USE_FAST_PINIO ///< Use direct PORT register access
+ #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers
+ #elif defined(CORE_TEENSY)
+ // PJRC Teensy 4.x
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+ // PJRC Teensy 3.x
+ #else
+ typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
+ #endif
+ #define USE_FAST_PINIO ///< Use direct PORT register access
+ #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers
+ #else
+ // Arduino Due?
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+ // USE_FAST_PINIO not available here (yet)...Due has a totally different
+ // GPIO register set and will require some changes elsewhere (e.g. in
+ // constructors especially).
+ #endif
+#else // !ARM
+ // Probably ESP8266 or ESP32. USE_FAST_PINIO is not available here (yet)
+ // but don't worry about it too much...the digitalWrite() implementation
+ // on these platforms is reasonably efficient and already RAM-resident,
+ // only gotcha then is no parallel connection support for now.
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+#endif // end !ARM
+typedef volatile ADAGFX_PORT_t* PORTreg_t; ///< PORT register type
+
+#if defined(__AVR__)
+ #define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed
+#else
+ #define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed
+#endif
+
+#if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
+ #define USE_SPI_DMA ///< Auto DMA if using PyPortal
+#else
+ //#define USE_SPI_DMA ///< If set, use DMA if available
+#endif
+// Another "oops" name -- this now also handles parallel DMA.
+// If DMA is enabled, Arduino sketch MUST #include
+// Estimated RAM usage:
+// 4 bytes/pixel on display major axis + 8 bytes/pixel on minor axis,
+// e.g. 320x240 pixels = 320 * 4 + 240 * 8 = 3,200 bytes.
+
+#if !defined(ARDUINO_ARCH_SAMD)
+ #undef USE_SPI_DMA ///< DMA currently for SAMD chips only
+#endif
+
+#if defined(USE_SPI_DMA)
+ #pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.")
+ #include
+#endif
+
+// This is kind of a kludge. Needed a way to disambiguate the software SPI
+// and parallel constructors via their argument lists. Originally tried a
+// bool as the first argument to the parallel constructor (specifying 8-bit
+// vs 16-bit interface) but the compiler regards this as equivalent to an
+// integer and thus still ambiguous. SO...the parallel constructor requires
+// an enumerated type as the first argument: tft8 (for 8-bit parallel) or
+// tft16 (for 16-bit)...even though 16-bit isn't fully implemented or tested
+// and might never be, still needed that disambiguation from soft SPI.
+enum tftBusWidth { tft8bitbus, tft16bitbus }; ///< For first arg to parallel constructor
+
+// CLASS DEFINITION --------------------------------------------------------
+
+/*!
+ @brief Adafruit_SPITFT is an intermediary class between Adafruit_GFX
+ and various hardware-specific subclasses for different displays.
+ It handles certain operations that are common to a range of
+ displays (address window, area fills, etc.). Originally these were
+ all color TFT displays interfaced via SPI, but it's since expanded
+ to include color OLEDs and parallel-interfaced TFTs. THE NAME HAS
+ BEEN KEPT TO AVOID BREAKING A LOT OF SUBCLASSES AND EXAMPLE CODE.
+ Many of the class member functions similarly live on with names
+ that don't necessarily accurately describe what they're doing,
+ again to avoid breaking a lot of other code. If in doubt, read
+ the comments.
+*/
+class Adafruit_SPITFT : public Renderer {
+
+ public:
+
+ // CONSTRUCTORS --------------------------------------------------------
+
+ // Software SPI constructor: expects width & height (at default rotation
+ // setting 0), 4 signal pins (cs, dc, mosi, sclk), 2 optional pins
+ // (reset, miso). cs argument is required but can be -1 if unused --
+ // rather than moving it to the optional arguments, it was done this way
+ // to avoid breaking existing code (-1 option was a later addition).
+ Adafruit_SPITFT(uint16_t w, uint16_t h,
+ int8_t cs, int8_t dc, int8_t mosi, int8_t sck,
+ int8_t rst = -1, int8_t miso = -1);
+
+ // Hardware SPI constructor using the default SPI port: expects width &
+ // height (at default rotation setting 0), 2 signal pins (cs, dc),
+ // optional reset pin. cs is required but can be -1 if unused -- rather
+ // than moving it to the optional arguments, it was done this way to
+ // avoid breaking existing code (-1 option was a later addition).
+ Adafruit_SPITFT(uint16_t w, uint16_t h,
+ int8_t cs, int8_t dc, int8_t rst = -1);
+
+#if !defined(ESP8266) // See notes in .cpp
+ // Hardware SPI constructor using an arbitrary SPI peripheral: expects
+ // width & height (rotation 0), SPIClass pointer, 2 signal pins (cs, dc)
+ // and optional reset pin. cs is required but can be -1 if unused.
+ Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass,
+ int8_t cs, int8_t dc, int8_t rst = -1);
+#endif // end !ESP8266
+
+ // Parallel constructor: expects width & height (rotation 0), flag
+ // indicating whether 16-bit (true) or 8-bit (false) interface, 3 signal
+ // pins (d0, wr, dc), 3 optional pins (cs, rst, rd). 16-bit parallel
+ // isn't even fully implemented but the 'wide' flag was added as a
+ // required argument to avoid ambiguity with other constructors.
+ Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth,
+ int8_t d0, int8_t wr, int8_t dc,
+ int8_t cs = -1, int8_t rst = -1, int8_t rd = -1);
+
+ // CLASS MEMBER FUNCTIONS ----------------------------------------------
+
+ // These first two functions MUST be declared by subclasses:
+
+ /*!
+ @brief Display-specific initialization function.
+ @param freq SPI frequency, in hz (or 0 for default or unused).
+ */
+ virtual void begin(uint32_t freq) = 0;
+
+ /*!
+ @brief Set up the specific display hardware's "address window"
+ for subsequent pixel-pushing operations.
+ @param x Leftmost pixel of area to be drawn (MUST be within
+ display bounds at current rotation setting).
+ @param y Topmost pixel of area to be drawn (MUST be within
+ display bounds at current rotation setting).
+ @param w Width of area to be drawn, in pixels (MUST be >0 and,
+ added to x, within display bounds at current rotation).
+ @param h Height of area to be drawn, in pixels (MUST be >0 and,
+ added to x, within display bounds at current rotation).
+ */
+ virtual void setAddrWindow(
+ uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0;
+
+ // Remaining functions do not need to be declared in subclasses
+ // unless they wish to provide hardware-specific optimizations.
+ // Brief comments here...documented more thoroughly in .cpp file.
+
+ // Subclass' begin() function invokes this to initialize hardware.
+ // freq=0 to use default SPI speed. spiMode must be one of the SPI_MODEn
+ // values defined in SPI.h, which are NOT the same as 0 for SPI_MODE0,
+ // 1 for SPI_MODE1, etc...use ONLY the SPI_MODEn defines! Only!
+ // Name is outdated (interface may be parallel) but for compatibility:
+ void initSPI(uint32_t freq = 0, uint8_t spiMode = SPI_MODE0);
+ // Chip select and/or hardware SPI transaction start as needed:
+ void startWrite(void);
+ // Chip deselect and/or hardware SPI transaction end as needed:
+ void endWrite(void);
+ void sendCommand(uint8_t commandByte, uint8_t *dataBytes = NULL, uint8_t numDataBytes = 0);
+ void sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes);
+ uint8_t readcommand8(uint8_t commandByte, uint8_t index = 0);
+
+ // These functions require a chip-select and/or SPI transaction
+ // around them. Higher-level graphics primitives might start a
+ // single transaction and then make multiple calls to these functions
+ // (e.g. circle or text rendering might make repeated lines or rects)
+ // before ending the transaction. It's more efficient than starting a
+ // transaction every time.
+ void writePixel(int16_t x, int16_t y, uint16_t color);
+ void writePixels(uint16_t *colors, uint32_t len,
+ bool block=true, bool bigEndian=false);
+ void writeColor(uint16_t color, uint32_t len);
+ void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
+ uint16_t color);
+ void writeFastHLine(int16_t x, int16_t y, int16_t w,
+ uint16_t color);
+ void writeFastVLine(int16_t x, int16_t y, int16_t h,
+ uint16_t color);
+ // This is a new function, similar to writeFillRect() except that
+ // all arguments MUST be onscreen, sorted and clipped. If higher-level
+ // primitives can handle their own sorting/clipping, it avoids repeating
+ // such operations in the low-level code, making it potentially faster.
+ // CALLING THIS WITH UNCLIPPED OR NEGATIVE VALUES COULD BE DISASTROUS.
+ inline void writeFillRectPreclipped(int16_t x, int16_t y,
+ int16_t w, int16_t h, uint16_t color);
+ // Another new function, companion to the new non-blocking
+ // writePixels() variant.
+ void dmaWait(void);
+
+
+ // These functions are similar to the 'write' functions above, but with
+ // a chip-select and/or SPI transaction built-in. They're typically used
+ // solo -- that is, as graphics primitives in themselves, not invoked by
+ // higher-level primitives (which should use the functions above).
+ void drawPixel(int16_t x, int16_t y, uint16_t color);
+ void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
+ uint16_t color);
+ void drawFastHLine(int16_t x, int16_t y, int16_t w,
+ uint16_t color);
+ void drawFastVLine(int16_t x, int16_t y, int16_t h,
+ uint16_t color);
+ // A single-pixel push encapsulated in a transaction. I don't think
+ // this is used anymore (BMP demos might've used it?) but is provided
+ // for backward compatibility, consider it deprecated:
+ void pushColor(uint16_t color);
+
+ using Adafruit_GFX::drawRGBBitmap; // Check base class first
+ void drawRGBBitmap(int16_t x, int16_t y,
+ uint16_t *pcolors, int16_t w, int16_t h);
+
+ void invertDisplay(bool i);
+ uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
+
+ // Despite parallel additions, function names kept for compatibility:
+ void spiWrite(uint8_t b); // Write single byte as DATA
+ void writeCommand(uint8_t cmd); // Write single byte as COMMAND
+ uint8_t spiRead(void); // Read single byte of data
+
+ // Most of these low-level functions were formerly macros in
+ // Adafruit_SPITFT_Macros.h. Some have been made into inline functions
+ // to avoid macro mishaps. Despite the addition of code for a parallel
+ // display interface, the names have been kept for backward
+ // compatibility (some subclasses may be invoking these):
+ void SPI_WRITE16(uint16_t w); // Not inline
+ void SPI_WRITE32(uint32_t l); // Not inline
+ // Old code had both a spiWrite16() function and SPI_WRITE16 macro
+ // in addition to the SPI_WRITE32 macro. The latter two have been
+ // made into functions here, and spiWrite16() removed (use SPI_WRITE16()
+ // instead). It looks like most subclasses had gotten comfortable with
+ // SPI_WRITE16 and SPI_WRITE32 anyway so those names were kept rather
+ // than the less-obnoxious camelcase variants, oh well.
+
+ // Placing these functions entirely in the class definition inlines
+ // them implicitly them while allowing their use in other code:
+
+ /*!
+ @brief Set the chip-select line HIGH. Does NOT check whether CS pin
+ is set (>=0), that should be handled in calling function.
+ Despite function name, this is used even if the display
+ connection is parallel.
+ */
+ void SPI_CS_HIGH(void) {
+ #if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *csPortSet = 1;
+ #else // !KINETISK
+ *csPortSet = csPinMask;
+ #endif // end !KINETISK
+ #else // !HAS_PORT_SET_CLR
+ *csPort |= csPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+ #else // !USE_FAST_PINIO
+ digitalWrite(_cs, HIGH);
+ #endif // end !USE_FAST_PINIO
+ }
+
+ /*!
+ @brief Set the chip-select line LOW. Does NOT check whether CS pin
+ is set (>=0), that should be handled in calling function.
+ Despite function name, this is used even if the display
+ connection is parallel.
+ */
+ void SPI_CS_LOW(void) {
+ #if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *csPortClr = 1;
+ #else // !KINETISK
+ *csPortClr = csPinMask;
+ #endif // end !KINETISK
+ #else // !HAS_PORT_SET_CLR
+ *csPort &= csPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+ #else // !USE_FAST_PINIO
+ digitalWrite(_cs, LOW);
+ #endif // end !USE_FAST_PINIO
+ }
+
+ /*!
+ @brief Set the data/command line HIGH (data mode).
+ */
+ void SPI_DC_HIGH(void) {
+ #if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *dcPortSet = 1;
+ #else // !KINETISK
+ *dcPortSet = dcPinMask;
+ #endif // end !KINETISK
+ #else // !HAS_PORT_SET_CLR
+ *dcPort |= dcPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+ #else // !USE_FAST_PINIO
+ digitalWrite(_dc, HIGH);
+ #endif // end !USE_FAST_PINIO
+ }
+
+ /*!
+ @brief Set the data/command line LOW (command mode).
+ */
+ void SPI_DC_LOW(void) {
+ #if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(KINETISK)
+ *dcPortClr = 1;
+ #else // !KINETISK
+ *dcPortClr = dcPinMask;
+ #endif // end !KINETISK
+ #else // !HAS_PORT_SET_CLR
+ *dcPort &= dcPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+ #else // !USE_FAST_PINIO
+ digitalWrite(_dc, LOW);
+ #endif // end !USE_FAST_PINIO
+ }
+
+ protected:
+
+ // A few more low-level member functions -- some may have previously
+ // been macros. Shouldn't have a need to access these externally, so
+ // they've been moved to the protected section. Additionally, they're
+ // declared inline here and the code is in the .cpp file, since outside
+ // code doesn't need to see these.
+ inline void SPI_MOSI_HIGH(void);
+ inline void SPI_MOSI_LOW(void);
+ inline void SPI_SCK_HIGH(void);
+ inline void SPI_SCK_LOW(void);
+ inline bool SPI_MISO_READ(void);
+ inline void SPI_BEGIN_TRANSACTION(void);
+ inline void SPI_END_TRANSACTION(void);
+ inline void TFT_WR_STROBE(void); // Parallel interface write strobe
+ inline void TFT_RD_HIGH(void); // Parallel interface read high
+ inline void TFT_RD_LOW(void); // Parallel interface read low
+
+ // CLASS INSTANCE VARIABLES --------------------------------------------
+
+ // Here be dragons! There's a big union of three structures here --
+ // one each for hardware SPI, software (bitbang) SPI, and parallel
+ // interfaces. This is to save some memory, since a display's connection
+ // will be only one of these. The order of some things is a little weird
+ // in an attempt to get values to align and pack better in RAM.
+
+#if defined(USE_FAST_PINIO)
+#if defined(HAS_PORT_SET_CLR)
+ PORTreg_t csPortSet; ///< PORT register for chip select SET
+ PORTreg_t csPortClr; ///< PORT register for chip select CLEAR
+ PORTreg_t dcPortSet; ///< PORT register for data/command SET
+ PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR
+#else // !HAS_PORT_SET_CLR
+ PORTreg_t csPort; ///< PORT register for chip select
+ PORTreg_t dcPort; ///< PORT register for data/command
+#endif // end HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+#if defined(__cplusplus) && (__cplusplus >= 201100)
+ union {
+#endif
+ struct { // Values specific to HARDWARE SPI:
+ SPIClass *_spi; ///< SPI class pointer
+#if defined(SPI_HAS_TRANSACTION)
+ SPISettings settings; ///< SPI transaction settings
+#else
+ uint32_t _freq; ///< SPI bitrate (if no SPI transactions)
+#endif
+ uint32_t _mode; ///< SPI data mode (transactions or no)
+ } hwspi; ///< Hardware SPI values
+ struct { // Values specific to SOFTWARE SPI:
+#if defined(USE_FAST_PINIO)
+ PORTreg_t misoPort; ///< PORT (PIN) register for MISO
+#if defined(HAS_PORT_SET_CLR)
+ PORTreg_t mosiPortSet; ///< PORT register for MOSI SET
+ PORTreg_t mosiPortClr; ///< PORT register for MOSI CLEAR
+ PORTreg_t sckPortSet; ///< PORT register for SCK SET
+ PORTreg_t sckPortClr; ///< PORT register for SCK CLEAR
+ #if !defined(KINETISK)
+ ADAGFX_PORT_t mosiPinMask; ///< Bitmask for MOSI
+ ADAGFX_PORT_t sckPinMask; ///< Bitmask for SCK
+ #endif // end !KINETISK
+#else // !HAS_PORT_SET_CLR
+ PORTreg_t mosiPort; ///< PORT register for MOSI
+ PORTreg_t sckPort; ///< PORT register for SCK
+ ADAGFX_PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR)
+ ADAGFX_PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND)
+ ADAGFX_PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask)
+ ADAGFX_PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND)
+#endif // end HAS_PORT_SET_CLR
+ #if !defined(KINETISK)
+ ADAGFX_PORT_t misoPinMask; ///< Bitmask for MISO
+ #endif // end !KINETISK
+#endif // end USE_FAST_PINIO
+ int8_t _mosi; ///< MOSI pin #
+ int8_t _miso; ///< MISO pin #
+ int8_t _sck; ///< SCK pin #
+ } swspi; ///< Software SPI values
+ struct { // Values specific to 8-bit parallel:
+#if defined(USE_FAST_PINIO)
+
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ volatile uint32_t *writePort; ///< PORT register for DATA WRITE
+ volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ
+ #else
+ volatile uint8_t *writePort; ///< PORT register for DATA WRITE
+ volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ
+ #endif
+#if defined(HAS_PORT_SET_CLR)
+ // Port direction register pointers are always 8-bit regardless of
+ // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits.
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ volatile uint32_t *dirSet; ///< PORT byte data direction SET
+ volatile uint32_t *dirClr; ///< PORT byte data direction CLEAR
+ #else
+ volatile uint8_t *dirSet; ///< PORT byte data direction SET
+ volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR
+ #endif
+ PORTreg_t wrPortSet; ///< PORT register for write strobe SET
+ PORTreg_t wrPortClr; ///< PORT register for write strobe CLEAR
+ PORTreg_t rdPortSet; ///< PORT register for read strobe SET
+ PORTreg_t rdPortClr; ///< PORT register for read strobe CLEAR
+ #if !defined(KINETISK)
+ ADAGFX_PORT_t wrPinMask; ///< Bitmask for write strobe
+ #endif // end !KINETISK
+ ADAGFX_PORT_t rdPinMask; ///< Bitmask for read strobe
+#else // !HAS_PORT_SET_CLR
+ // Port direction register pointer is always 8-bit regardless of
+ // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits.
+ volatile uint8_t *portDir; ///< PORT direction register
+ PORTreg_t wrPort; ///< PORT register for write strobe
+ PORTreg_t rdPort; ///< PORT register for read strobe
+ ADAGFX_PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR)
+ ADAGFX_PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND)
+ ADAGFX_PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR)
+ ADAGFX_PORT_t rdPinMaskClr; ///< Bitmask for read strobe CLEAR (AND)
+#endif // end HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+ int8_t _d0; ///< Data pin 0 #
+ int8_t _wr; ///< Write strobe pin #
+ int8_t _rd; ///< Read strobe pin # (or -1)
+ bool wide = 0; ///< If true, is 16-bit interface
+ } tft8; ///< Parallel interface settings
+#if defined(__cplusplus) && (__cplusplus >= 201100)
+ }; ///< Only one interface is active
+#endif
+#if defined(USE_SPI_DMA) // Used by hardware SPI and tft8
+ Adafruit_ZeroDMA dma; ///< DMA instance
+ DmacDescriptor *dptr = NULL; ///< 1st descriptor
+ DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list
+ uint16_t *pixelBuf[2]; ///< Working buffers
+ uint16_t maxFillLen; ///< Max pixels per DMA xfer
+ uint16_t lastFillColor = 0; ///< Last color used w/fill
+ uint32_t lastFillLen = 0; ///< # of pixels w/last fill
+ uint8_t onePixelBuf; ///< For hi==lo fill
+#endif
+#if defined(USE_FAST_PINIO)
+#if defined(HAS_PORT_SET_CLR)
+ #if !defined(KINETISK)
+ ADAGFX_PORT_t csPinMask; ///< Bitmask for chip select
+ ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command
+ #endif // end !KINETISK
+#else // !HAS_PORT_SET_CLR
+ ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR)
+ ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND)
+ ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR)
+ ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND)
+#endif // end HAS_PORT_SET_CLR
+#endif // end USE_FAST_PINIO
+ uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc.
+ int8_t _rst; ///< Reset pin # (or -1)
+ int8_t _cs; ///< Chip select pin # (or -1)
+ int8_t _dc; ///< Data/command pin #
+
+ int16_t _xstart = 0; ///< Internal framebuffer X offset
+ int16_t _ystart = 0; ///< Internal framebuffer Y offset
+ uint8_t invertOnCommand = 0; ///< Command to enable invert mode
+ uint8_t invertOffCommand = 0; ///< Command to disable invert mode
+
+ uint32_t _freq = 0; ///< Dummy var to keep subclasses happy
+};
+
+#endif // end __AVR_ATtiny85__
+#endif // end _ADAFRUIT_SPITFT_H_
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp
new file mode 100644
index 000000000..78d9901d6
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp
@@ -0,0 +1,190 @@
+/*!
+ * @file Adafruit_SSD1331.cpp
+ *
+ * @mainpage Adafruit SSD1331 Arduino Library
+ *
+ * @section intro_sec Introduction
+ *
+ * This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
+ *
+ * Pick one up today in the adafruit shop!
+ * ------> http://www.adafruit.com/products/684
+ *
+ * These displays use SPI to communicate, 4 or 5 pins are required to
+ * interface
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * @section author Author
+ *
+ * Written by Limor Fried/Ladyada for Adafruit Industries.
+ *
+ * @section license License
+ *
+ * BSD license, all text above must be included in any redistribution
+ */
+
+#include "Adafruit_SSD1331.h"
+#include "pins_arduino.h"
+#include "wiring_private.h"
+
+/***********************************/
+
+/*!
+ @brief SPI displays set an address window rectangle for blitting pixels
+ @param x Top left corner x coordinate
+ @param y Top left corner x coordinate
+ @param w Width of window
+ @param h Height of window
+*/
+void Adafruit_SSD1331::setAddrWindow(uint16_t x, uint16_t y, uint16_t w,
+ uint16_t h) {
+
+ uint8_t x1 = x;
+ uint8_t y1 = y;
+ if (x1 > 95)
+ x1 = 95;
+ if (y1 > 63)
+ y1 = 63;
+
+ uint8_t x2 = (x + w - 1);
+ uint8_t y2 = (y + h - 1);
+ if (x2 > 95)
+ x2 = 95;
+ if (y2 > 63)
+ y2 = 63;
+
+ if (x1 > x2) {
+ uint8_t t = x2;
+ x2 = x1;
+ x1 = t;
+ }
+ if (y1 > y2) {
+ uint8_t t = y2;
+ y2 = y1;
+ y1 = t;
+ }
+
+ sendCommand(0x15); // Column addr set
+ sendCommand(x1);
+ sendCommand(x2);
+
+ sendCommand(0x75); // Column addr set
+ sendCommand(y1);
+ sendCommand(y2);
+
+ startWrite();
+}
+
+/**************************************************************************/
+/*!
+ @brief Initialize SSD1331 chip
+ Connects to the SSD1331 over SPI and sends initialization procedure commands
+ @param freq Desired SPI clock frequency
+*/
+/**************************************************************************/
+void Adafruit_SSD1331::begin(uint32_t freq) {
+ initSPI(freq);
+
+ // Initialization Sequence
+ sendCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE
+ sendCommand(SSD1331_CMD_SETREMAP); // 0xA0
+#if defined SSD1331_COLORORDER_RGB
+ sendCommand(0x72); // RGB Color
+#else
+ sendCommand(0x76); // BGR Color
+#endif
+ sendCommand(SSD1331_CMD_STARTLINE); // 0xA1
+ sendCommand(0x0);
+ sendCommand(SSD1331_CMD_DISPLAYOFFSET); // 0xA2
+ sendCommand(0x0);
+ sendCommand(SSD1331_CMD_NORMALDISPLAY); // 0xA4
+ sendCommand(SSD1331_CMD_SETMULTIPLEX); // 0xA8
+ sendCommand(0x3F); // 0x3F 1/64 duty
+ sendCommand(SSD1331_CMD_SETMASTER); // 0xAD
+ sendCommand(0x8E);
+ sendCommand(SSD1331_CMD_POWERMODE); // 0xB0
+ sendCommand(0x0B);
+ sendCommand(SSD1331_CMD_PRECHARGE); // 0xB1
+ sendCommand(0x31);
+ sendCommand(SSD1331_CMD_CLOCKDIV); // 0xB3
+ sendCommand(0xF0); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio
+ // (A[3:0]+1 = 1..16)
+ sendCommand(SSD1331_CMD_PRECHARGEA); // 0x8A
+ sendCommand(0x64);
+ sendCommand(SSD1331_CMD_PRECHARGEB); // 0x8B
+ sendCommand(0x78);
+ sendCommand(SSD1331_CMD_PRECHARGEC); // 0x8C
+ sendCommand(0x64);
+ sendCommand(SSD1331_CMD_PRECHARGELEVEL); // 0xBB
+ sendCommand(0x3A);
+ sendCommand(SSD1331_CMD_VCOMH); // 0xBE
+ sendCommand(0x3E);
+ sendCommand(SSD1331_CMD_MASTERCURRENT); // 0x87
+ sendCommand(0x06);
+ sendCommand(SSD1331_CMD_CONTRASTA); // 0x81
+ sendCommand(0x91);
+ sendCommand(SSD1331_CMD_CONTRASTB); // 0x82
+ sendCommand(0x50);
+ sendCommand(SSD1331_CMD_CONTRASTC); // 0x83
+ sendCommand(0x7D);
+ sendCommand(SSD1331_CMD_DISPLAYON); //--turn on oled panel
+ _width = TFTWIDTH;
+ _height = TFTHEIGHT;
+}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with software SPI
+ @param cs Chip select pin #
+ @param dc Data/Command pin #
+ @param mosi SPI MOSI pin #
+ @param sclk SPI Clock pin #
+ @param rst Reset pin # (optional, pass -1 if unused)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi,
+ int8_t sclk, int8_t rst)
+ : Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, mosi, sclk, rst, -1) {}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with hardware SPI
+ @param cs Chip select pin #
+ @param dc Data/Command pin #
+ @param rst Reset pin # (optional, pass -1 if unused)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst)
+ : Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, rst) {}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with hardware SPI
+ @param spi Pointer to an existing SPIClass instance (e.g. &SPI, the
+ microcontroller's primary SPI bus).
+ @param cs Chip select pin #
+ @param dc Data/Command pin #
+ @param rst Reset pin # (optional, pass -1 if unused)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc,
+ int8_t rst)
+ :
+#if defined(ESP8266)
+ Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, cs, dc, rst) {
+#else
+ Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, spi, cs, dc, rst) {
+#endif
+}
+
+/**************************************************************************/
+/*!
+ @brief Change whether display is on or off
+ @param enable True if you want the display ON, false OFF
+*/
+/**************************************************************************/
+void Adafruit_SSD1331::enableDisplay(boolean enable) {
+ sendCommand(enable ? SSD1331_CMD_DISPLAYON : SSD1331_CMD_DISPLAYOFF);
+}
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h
new file mode 100644
index 000000000..7d9bc85a0
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h
@@ -0,0 +1,76 @@
+/*!
+ * @file Adafruit_SSD1331.h
+ */
+
+#include "Arduino.h"
+#include
+// Tasmota change: use custom version of Adafruit_SPITFT which extends Renderer instead of Adafruit_GFX
+#include
+#include
+#include
+
+/*!
+ * @brief Select one of these defines to set the pixel color order
+ */
+#define SSD1331_COLORORDER_RGB
+// #define SSD1331_COLORORDER_BGR
+
+#if defined SSD1331_COLORORDER_RGB && defined SSD1331_COLORORDER_BGR
+#error "RGB and BGR can not both be defined for SSD1331_COLORODER."
+#endif
+
+// Timing Delays
+#define SSD1331_DELAYS_HWFILL (3) //!< Fill delay
+#define SSD1331_DELAYS_HWLINE (1) //!< Line delay
+
+// SSD1331 Commands
+#define SSD1331_CMD_DRAWLINE 0x21 //!< Draw line
+#define SSD1331_CMD_DRAWRECT 0x22 //!< Draw rectangle
+#define SSD1331_CMD_FILL 0x26 //!< Fill enable/disable
+#define SSD1331_CMD_SETCOLUMN 0x15 //!< Set column address
+#define SSD1331_CMD_SETROW 0x75 //!< Set row adress
+#define SSD1331_CMD_CONTRASTA 0x81 //!< Set contrast for color A
+#define SSD1331_CMD_CONTRASTB 0x82 //!< Set contrast for color B
+#define SSD1331_CMD_CONTRASTC 0x83 //!< Set contrast for color C
+#define SSD1331_CMD_MASTERCURRENT 0x87 //!< Master current control
+#define SSD1331_CMD_SETREMAP 0xA0 //!< Set re-map & data format
+#define SSD1331_CMD_STARTLINE 0xA1 //!< Set display start line
+#define SSD1331_CMD_DISPLAYOFFSET 0xA2 //!< Set display offset
+#define SSD1331_CMD_NORMALDISPLAY 0xA4 //!< Set display to normal mode
+#define SSD1331_CMD_DISPLAYALLON 0xA5 //!< Set entire display ON
+#define SSD1331_CMD_DISPLAYALLOFF 0xA6 //!< Set entire display OFF
+#define SSD1331_CMD_INVERTDISPLAY 0xA7 //!< Invert display
+#define SSD1331_CMD_SETMULTIPLEX 0xA8 //!< Set multiplex ratio
+#define SSD1331_CMD_SETMASTER 0xAD //!< Set master configuration
+#define SSD1331_CMD_DISPLAYOFF 0xAE //!< Display OFF (sleep mode)
+#define SSD1331_CMD_DISPLAYON 0xAF //!< Normal Brightness Display ON
+#define SSD1331_CMD_POWERMODE 0xB0 //!< Power save mode
+#define SSD1331_CMD_PRECHARGE 0xB1 //!< Phase 1 and 2 period adjustment
+#define SSD1331_CMD_CLOCKDIV \
+ 0xB3 //!< Set display clock divide ratio/oscillator frequency
+#define SSD1331_CMD_PRECHARGEA 0x8A //!< Set second pre-charge speed for color A
+#define SSD1331_CMD_PRECHARGEB 0x8B //!< Set second pre-charge speed for color B
+#define SSD1331_CMD_PRECHARGEC 0x8C //!< Set second pre-charge speed for color C
+#define SSD1331_CMD_PRECHARGELEVEL 0xBB //!< Set pre-charge voltage
+#define SSD1331_CMD_VCOMH 0xBE //!< Set Vcomh voltge
+
+/// Class to manage hardware interface with SSD1331 chipset
+class Adafruit_SSD1331 : public Adafruit_SPITFT {
+public:
+ Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi, int8_t sclk, int8_t rst);
+ Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst);
+ // 3-4 args using hardware SPI (must specify peripheral) (reset optional)
+ Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc, int8_t rst = -1);
+
+ // commands
+ void begin(uint32_t begin = 8000000);
+
+ void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
+
+ void enableDisplay(boolean enable);
+
+ static const int16_t TFTWIDTH = 96; ///< The width of the display
+ static const int16_t TFTHEIGHT = 64; ///< The height of the display
+
+private:
+};
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md b/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md
new file mode 100644
index 000000000..24c404c59
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md
@@ -0,0 +1,24 @@
+# Adafruit SSD1331 Arduino Library [](https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/actions)[](http://adafruit.github.io/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/html/index.html)
+This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
+
+ Pick one up today in the adafruit shop!
+ ------> http://www.adafruit.com/products/684
+
+These displays use SPI to communicate, 4 or 5 pins are required to
+interface
+
+Adafruit invests time and resources providing this open source code,
+please support Adafruit and open-source hardware by purchasing
+products from Adafruit!
+
+Written by Limor Fried/Ladyada for Adafruit Industries.
+BSD license, check license.txt for more information
+All text above must be included in any redistribution
+
+To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_SSD1131. Check that the Adafruit_SSD1331 folder contains Adafruit_SSD1331.cpp and Adafruit_SSD1331.h
+
+Place the Adafruit_SSD1331 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.
+
+You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from
+https://github.com/adafruit/Adafruit-GFX-Library
+and download/install that library as well
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties b/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties
new file mode 100644
index 000000000..931f1aa38
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties
@@ -0,0 +1,10 @@
+name=Adafruit SSD1331 OLED Driver Library for Arduino
+version=1.2.0
+author=Adafruit
+maintainer=Adafruit
+sentence=For 0.96" OLEDs in the Adafruit shop
+paragraph=For 0.96" OLEDs in the Adafruit shop
+category=Display
+url=https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino
+architectures=*
+depends=Adafruit GFX Library
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt b/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt
new file mode 100644
index 000000000..f6a0f22b8
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt
@@ -0,0 +1,26 @@
+Software License Agreement (BSD License)
+
+Copyright (c) 2012, Adafruit Industries
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holders nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tasmota/xdsp_14_SSD1331.ino b/tasmota/xdsp_14_SSD1331.ino
new file mode 100644
index 000000000..031115beb
--- /dev/null
+++ b/tasmota/xdsp_14_SSD1331.ino
@@ -0,0 +1,182 @@
+/*
+ xdsp_14_SSD1331.ino - Display SSD1331 support for Tasmota
+
+ Copyright (C) 2020 Jeroen Vermeulen, Gerhard Mutz and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_SPI
+#ifdef USE_DISPLAY
+#ifdef USE_DISPLAY_SSD1331
+
+#define XDSP_14 14
+
+#define COLORED 1
+#define UNCOLORED 0
+#define USE_TINY_FONT
+
+#define SSD1331_BLACK 0x0000 // 0, 0, 0
+#define SSD1331_RED 0xF800 // 255, 0, 0
+#define SSD1331_WHITE 0xFFFF // 255, 255, 255
+
+#include
+
+extern uint8_t *buffer;
+extern uint8_t color_type;
+Adafruit_SSD1331 *ssd1331;
+
+/*********************************************************************************************/
+
+void SSD1331_InitDriver() {
+ if (!Settings.display_model) {
+ Settings.display_model = XDSP_14;
+ }
+
+ if (XDSP_14 == Settings.display_model) {
+
+ if (Settings.display_width != Adafruit_SSD1331::TFTWIDTH) {
+ Settings.display_width = Adafruit_SSD1331::TFTWIDTH;
+ }
+ if (Settings.display_height != Adafruit_SSD1331::TFTHEIGHT) {
+ Settings.display_height = Adafruit_SSD1331::TFTHEIGHT;
+ }
+
+ buffer=0;
+
+ // default colors
+ fg_color = SSD1331_WHITE;
+ bg_color = SSD1331_BLACK;
+
+ // init renderer
+ if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)){
+ ssd1331 = new Adafruit_SSD1331(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_DC),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),OLED_RESET);
+ } else {
+ if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) {
+ ssd1331 = new Adafruit_SSD1331(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),OLED_RESET);
+ } else {
+ return;
+ }
+ }
+
+ delay(100);
+ ssd1331->begin();
+ renderer = ssd1331;
+ // Rotation is currently broken, https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/issues/26
+ renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font);
+ renderer->dim(Settings.display_dimmer);
+
+#ifdef SHOW_SPLASH
+ // Welcome text
+ renderer->clearDisplay();
+ renderer->setTextFont(1);
+ renderer->DrawStringAt(24, 27, "SSD1331", SSD1331_RED, 0);
+ delay(1000);
+#endif
+
+ color_type = COLOR_COLOR;
+ }
+}
+
+#ifdef USE_DISPLAY_MODES1TO5
+
+void SSD1331PrintLog(void)
+{
+ disp_refresh--;
+ if (!disp_refresh) {
+ disp_refresh = Settings.display_refresh;
+ if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); }
+
+ char* txt = DisplayLogBuffer('\370');
+ if (txt != NULL) {
+ uint8_t last_row = Settings.display_rows -1;
+
+ renderer->clearDisplay();
+ renderer->setTextSize(Settings.display_size);
+ renderer->setCursor(0,0);
+ for (byte i = 0; i < last_row; i++) {
+ strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols);
+ renderer->println(disp_screen_buffer[i]);
+ }
+ strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols);
+ DisplayFillScreen(last_row);
+
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]);
+
+ renderer->println(disp_screen_buffer[last_row]);
+ renderer->Updateframe();
+ }
+ }
+}
+
+void SSD1331Time(void)
+{
+ char line[12];
+
+ renderer->clearDisplay();
+ renderer->setTextSize(2);
+ renderer->setCursor(0, 0);
+ snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ]
+ renderer->println(line);
+ snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018]
+ renderer->println(line);
+ renderer->Updateframe();
+}
+
+void SSD1331Refresh(void) // Every second
+{
+ if (Settings.display_mode) { // Mode 0 is User text
+ switch (Settings.display_mode) {
+ case 1: // Time
+ SSD1331Time();
+ break;
+ case 2: // Local
+ case 3: // Local
+ case 4: // Mqtt
+ case 5: // Mqtt
+ SSD1331PrintLog();
+ break;
+ }
+ }
+}
+
+#endif // USE_DISPLAY_MODES1TO5
+/*********************************************************************************************/
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+bool Xdsp14(uint8_t function)
+{
+ bool result = false;
+
+ if (FUNC_DISPLAY_INIT_DRIVER == function) {
+ SSD1331_InitDriver();
+ }
+ else if (XDSP_14 == Settings.display_model) {
+ switch (function) {
+ case FUNC_DISPLAY_MODEL:
+ result = true;
+ break;
+#ifdef USE_DISPLAY_MODES1TO5
+ case FUNC_DISPLAY_EVERY_SECOND:
+ SSD1331Refresh();
+ break;
+#endif // USE_DISPLAY_MODES1TO5
+ }
+ }
+ return result;
+}
+#endif // USE_DISPLAY_SSD1331
+#endif // USE_DISPLAY
+#endif // USE_SPI
From d69be912e9e0a87dfc761506c8cd9dd6865f9c34 Mon Sep 17 00:00:00 2001
From: Mike Harris
Date: Mon, 28 Dec 2020 15:43:59 -0800
Subject: [PATCH 025/255] Make all initial tabs two spaces for consistency.
Also clean up some trailing comments spacing.
This seems to be the common style.
---
tasmota/WiFiClientSecureLightBearSSL.cpp | 410 +++++++++++------------
1 file changed, 205 insertions(+), 205 deletions(-)
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index d7b53cf25..98cd5f4d6 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -57,8 +57,8 @@ extern "C" {
#include "coredecls.h"
#define LOG_HEAP_SIZE(a) _Log_heap_size(a)
void _Log_heap_size(const char *msg) {
- register uint32_t *sp asm("a1");
- int freestack = 4 * (sp - g_pcont->stack);
+ register uint32_t *sp asm("a1");
+ int freestack = 4 * (sp - g_pcont->stack);
Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n",
msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(),
freestack, ESP.getFreeContStack());
@@ -98,60 +98,60 @@ make_stack_thunk_light(br_ssl_engine_sendrec_buf);
// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep
// symetric processing on the stack
void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvapp_ack(cc, len);
- } else {
- return br_ssl_engine_recvapp_ack(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvapp_ack(cc, len);
+ } else {
+ return br_ssl_engine_recvapp_ack(cc, len);
+ }
}
unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvapp_buf(cc, len);
- } else {
- return br_ssl_engine_recvapp_buf(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvapp_buf(cc, len);
+ } else {
+ return br_ssl_engine_recvapp_buf(cc, len);
+ }
}
void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvrec_ack(cc, len);
- } else {
- return br_ssl_engine_recvrec_ack(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvrec_ack(cc, len);
+ } else {
+ return br_ssl_engine_recvrec_ack(cc, len);
+ }
}
unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvrec_buf(cc, len);
- } else {
- return br_ssl_engine_recvrec_buf(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvrec_buf(cc, len);
+ } else {
+ return br_ssl_engine_recvrec_buf(cc, len);
+ }
}
void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendapp_ack(cc, len);
- } else {
- return br_ssl_engine_sendapp_ack(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendapp_ack(cc, len);
+ } else {
+ return br_ssl_engine_sendapp_ack(cc, len);
+ }
}
unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendapp_buf(cc, len);
- } else {
- return br_ssl_engine_sendapp_buf(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendapp_buf(cc, len);
+ } else {
+ return br_ssl_engine_sendapp_buf(cc, len);
+ }
}
void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendrec_ack(cc, len);
- } else {
- return br_ssl_engine_sendrec_ack(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendrec_ack(cc, len);
+ } else {
+ return br_ssl_engine_sendrec_ack(cc, len);
+ }
}
unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendrec_buf(cc, len);
- } else {
- return br_ssl_engine_sendrec_buf(cc, len);
- }
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendrec_buf(cc, len);
+ } else {
+ return br_ssl_engine_sendrec_buf(cc, len);
+ }
}
// Use min_ instead of original thunk_
@@ -176,7 +176,7 @@ namespace BearSSL {
void WiFiClientSecure_light::_clear() {
// TLS handshake may take more than the 5 second default timeout
- _timeout = 10000; // 10 seconds max, it should never go over 6 seconds
+ _timeout = 10000; // 10 seconds max, it should never go over 6 seconds
_sc = nullptr;
_ctx_present = false;
@@ -185,17 +185,17 @@ void WiFiClientSecure_light::_clear() {
_iobuf_out = nullptr;
setBufferSizes(1024, 1024); // reasonable minimum
_handshake_done = false;
- _last_error = 0;
+ _last_error = 0;
_recvapp_buf = nullptr;
_recvapp_len = 0;
- _fingerprint_any = true; // by default accept all fingerprints
- _fingerprint1 = nullptr;
- _fingerprint2 = nullptr;
- _chain_P = nullptr;
- _sk_ec_P = nullptr;
- _ta_P = nullptr;
+ _fingerprint_any = true; // by default accept all fingerprints
+ _fingerprint1 = nullptr;
+ _fingerprint2 = nullptr;
+ _chain_P = nullptr;
+ _sk_ec_P = nullptr;
+ _ta_P = nullptr;
_ta_size = 0;
- _max_thunkstack_use = 0;
+ _max_thunkstack_use = 0;
}
// Constructor
@@ -221,24 +221,24 @@ WiFiClientSecure_light::~WiFiClientSecure_light() {
void WiFiClientSecure_light::allocateBuffers(void) {
// We prefer to allocate all buffers at start, rather than lazy allocation and deallocation
// in the long run it avoids heap fragmentation and improves stability
- LOG_HEAP_SIZE("allocateBuffers before");
+ LOG_HEAP_SIZE("allocateBuffers before");
_sc = std::make_shared();
- LOG_HEAP_SIZE("allocateBuffers ClientContext");
+ LOG_HEAP_SIZE("allocateBuffers ClientContext");
_iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete());
_iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete());
- LOG_HEAP_SIZE("allocateBuffers after");
+ LOG_HEAP_SIZE("allocateBuffers after");
}
void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
- unsigned allowed_usages, unsigned cert_issuer_key_type) {
- _chain_P = cert;
- _sk_ec_P = sk;
+ unsigned allowed_usages, unsigned cert_issuer_key_type) {
+ _chain_P = cert;
+ _sk_ec_P = sk;
_allowed_usages = allowed_usages;
_cert_issuer_key_type = cert_issuer_key_type;
}
void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta, size_t ta_size) {
- _ta_P = ta;
+ _ta_P = ta;
_ta_size = ta_size;
}
@@ -271,9 +271,9 @@ bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) {
int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
- clearLastError();
+ clearLastError();
if (!WiFiClient::connect(ip, port)) {
- setLastError(ERR_TCP_CONNECT);
+ setLastError(ERR_TCP_CONNECT);
return 0;
}
return _connectSSL(nullptr);
@@ -282,19 +282,19 @@ int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
DEBUG_BSSL("connect(%s,%d)\n", name, port);
IPAddress remote_addr;
- clearLastError();
+ clearLastError();
if (!WiFi.hostByName(name, remote_addr)) {
DEBUG_BSSL("connect: Name loopup failure\n");
- setLastError(ERR_CANT_RESOLVE_IP);
+ setLastError(ERR_CANT_RESOLVE_IP);
return 0;
}
DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port);
if (!WiFiClient::connect(remote_addr, port)) {
DEBUG_BSSL("connect: Unable to connect TCP socket\n");
- _last_error = ERR_TCP_CONNECT;
+ _last_error = ERR_TCP_CONNECT;
return 0;
}
- LOG_HEAP_SIZE("Before calling _connectSSL");
+ LOG_HEAP_SIZE("Before calling _connectSSL");
return _connectSSL(name);
}
@@ -355,7 +355,7 @@ size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem
}
} while (size);
- LOG_HEAP_SIZE("_write");
+ LOG_HEAP_SIZE("_write");
return sent_bytes;
}
@@ -399,7 +399,7 @@ int WiFiClientSecure_light::read(uint8_t *buf, size_t size) {
int avail = available();
bool conn = connected();
if (!avail && conn) {
- return 0; // We're still connected, but nothing to read
+ return 0; // We're still connected, but nothing to read
}
if (!avail && !conn) {
DEBUG_BSSL("read: Not connected, none left available\n");
@@ -434,7 +434,7 @@ int WiFiClientSecure_light::read() {
int WiFiClientSecure_light::available() {
if (_recvapp_buf) {
- return _recvapp_len; // Anything from last call?
+ return _recvapp_len; // Anything from last call?
}
_recvapp_buf = nullptr;
_recvapp_len = 0;
@@ -443,7 +443,7 @@ int WiFiClientSecure_light::available() {
}
int st = br_ssl_engine_current_state(_eng);
if (st == BR_SSL_CLOSED) {
- return 0; // Nothing leftover, SSL is closed
+ return 0; // Nothing leftover, SSL is closed
}
if (st & BR_SSL_RECVAPP) {
_recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len);
@@ -620,24 +620,24 @@ static uint8_t htoi (unsigned char c)
extern "C" {
- // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
- void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) {
- unsigned char * pin = in;
- static const char * hex = "0123456789ABCDEF";
- char * pout = out;
- for(; pin < in+insz; pout +=3, pin++){
- pout[0] = hex[(*pin>>4) & 0xF];
- pout[1] = hex[ *pin & 0xF];
- pout[2] = ':';
- if (pout + 3 - out > outsz){
- /* Better to truncate output string than overflow buffer */
- /* it would be still better to either return a status */
- /* or ensure the target buffer is large enough and it never happen */
- break;
- }
- }
- pout[-1] = 0;
- }
+ // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
+ void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) {
+ unsigned char * pin = in;
+ static const char * hex = "0123456789ABCDEF";
+ char * pout = out;
+ for(; pin < in+insz; pout +=3, pin++){
+ pout[0] = hex[(*pin>>4) & 0xF];
+ pout[1] = hex[ *pin & 0xF];
+ pout[2] = ':';
+ if (pout + 3 - out > outsz){
+ /* Better to truncate output string than overflow buffer */
+ /* it would be still better to either return a status */
+ /* or ensure the target buffer is large enough and it never happen */
+ break;
+ }
+ }
+ pout[-1] = 0;
+ }
// BearSSL doesn't define a true insecure decoder, so we make one ourselves
@@ -648,12 +648,12 @@ extern "C" {
// Private x509 decoder state
struct br_x509_pubkeyfingerprint_context {
const br_x509_class *vtable;
- bool done_cert; // did we parse the first cert already?
- bool fingerprint_all;
- uint8_t *pubkey_recv_fingerprint;
+ bool done_cert; // did we parse the first cert already?
+ bool fingerprint_all;
+ uint8_t *pubkey_recv_fingerprint;
const uint8_t *fingerprint1;
const uint8_t *fingerprint2;
- unsigned usages; // pubkey usage
+ unsigned usages; // pubkey usage
br_x509_decoder_context ctx; // defined in BearSSL
};
@@ -662,15 +662,15 @@ extern "C" {
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
// Don't process anything but the first certificate in the chain
if (!xc->done_cert) {
- br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr);
- }
- (void)server_name; // ignore server name
+ br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr);
+ }
+ (void)server_name; // ignore server name
}
// Callback for each certificate present in the chain (but only operates
// on the first one by design).
static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) {
- (void) ctx; // do nothing
+ (void) ctx; // do nothing
(void) length;
}
@@ -686,7 +686,7 @@ extern "C" {
// Callback on individual cert end.
static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) {
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
- xc->done_cert = true; // first cert already processed
+ xc->done_cert = true; // first cert already processed
}
// **** Start patch Castellucci
@@ -743,18 +743,18 @@ extern "C" {
pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa);
br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint
- if (!xc->fingerprint_all) {
- if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
- return 0;
- }
- if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) {
- return 0;
- }
- return 1; // no match, error
- } else {
- // Default (no validation at all) or no errors in prior checks = success.
- return 0;
- }
+ if (!xc->fingerprint_all) {
+ if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
+ return 0;
+ }
+ if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) {
+ return 0;
+ }
+ return 1; // no match, error
+ } else {
+ // Default (no validation at all) or no errors in prior checks = success.
+ return 0;
+ }
*/
// set fingerprint status byte to zero
// FIXME: find a better way to pass this information
@@ -796,7 +796,7 @@ extern "C" {
xc->pubkey_recv_fingerprint[20] |= 2; // mark for update
}
if (!xc->pubkey_recv_fingerprint[20]) {
- return 1; // not marked for update because no match, error
+ return 1; // not marked for update because no match, error
}
// the old fingerprint format matched, recompute new one for update
@@ -822,9 +822,9 @@ extern "C" {
// Set up the x509 insecure data structures for BearSSL core to use.
void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx,
- const uint8_t *fingerprint1, const uint8_t *fingerprint2,
- uint8_t *recv_fingerprint,
- bool fingerprint_all) {
+ const uint8_t *fingerprint1, const uint8_t *fingerprint2,
+ uint8_t *recv_fingerprint,
+ bool fingerprint_all) {
static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = {
sizeof(br_x509_pubkeyfingerprint_context),
pubkeyfingerprint_start_chain,
@@ -838,19 +838,19 @@ extern "C" {
memset(ctx, 0, sizeof * ctx);
ctx->vtable = &br_x509_pubkeyfingerprint_vtable;
ctx->done_cert = false;
- ctx->fingerprint1 = fingerprint1;
- ctx->fingerprint2 = fingerprint2;
- ctx->pubkey_recv_fingerprint = recv_fingerprint;
- ctx->fingerprint_all = fingerprint_all;
+ ctx->fingerprint1 = fingerprint1;
+ ctx->fingerprint2 = fingerprint2;
+ ctx->pubkey_recv_fingerprint = recv_fingerprint;
+ ctx->fingerprint_all = fingerprint_all;
}
- // We limit to a single cipher to reduce footprint
+ // We limit to a single cipher to reduce footprint
// we reference it, don't put in PROGMEM
static const uint16_t suites[] = {
#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
- BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
#else
- BR_TLS_RSA_WITH_AES_128_GCM_SHA256
+ BR_TLS_RSA_WITH_AES_128_GCM_SHA256
#endif
};
@@ -869,14 +869,14 @@ extern "C" {
br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
- // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways)
- br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable);
- br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
- br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
+ // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways)
+ br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable);
+ br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
+ br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
- // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
- br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO
+ // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
+ br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO
#endif
static const char * alpn_mqtt = "mqtt";
br_ssl_engine_set_protocol_names(&cc->eng, &alpn_mqtt, 1);
@@ -886,110 +886,110 @@ extern "C" {
// Called by connect() to do the actual SSL setup and handshake.
// Returns if the SSL handshake succeeded.
bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
- // Validation context, either full CA validation or checking only fingerprints
+ // Validation context, either full CA validation or checking only fingerprints
#ifdef USE_MQTT_TLS_CA_CERT
- br_x509_minimal_context *x509_minimal;
+ br_x509_minimal_context *x509_minimal;
#else
br_x509_pubkeyfingerprint_context *x509_insecure;
#endif
- LOG_HEAP_SIZE("_connectSSL.start");
+ LOG_HEAP_SIZE("_connectSSL.start");
- do { // used to exit on Out of Memory error and keep all cleanup code at the same place
- // ============================================================
- // allocate Thunk stack, move to alternate stack and initialize
- stack_thunk_light_add_ref();
- LOG_HEAP_SIZE("Thunk allocated");
- DEBUG_BSSL("_connectSSL: start connection\n");
- _freeSSL();
- clearLastError();
- if (!stack_thunk_light_get_stack_bot()) break;
+ do { // used to exit on Out of Memory error and keep all cleanup code at the same place
+ // ============================================================
+ // allocate Thunk stack, move to alternate stack and initialize
+ stack_thunk_light_add_ref();
+ LOG_HEAP_SIZE("Thunk allocated");
+ DEBUG_BSSL("_connectSSL: start connection\n");
+ _freeSSL();
+ clearLastError();
+ if (!stack_thunk_light_get_stack_bot()) break;
- _ctx_present = true;
- _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
+ _ctx_present = true;
+ _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
- br_ssl_client_base_init(_sc.get());
+ br_ssl_client_base_init(_sc.get());
- // ============================================================
- // Allocatte and initialize Decoder Context
- LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation");
- // Only failure possible in the installation is OOM
- #ifdef USE_MQTT_TLS_CA_CERT
- x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context));
- if (!x509_minimal) break;
- br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, _ta_size);
- br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng));
- br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable);
- br_ssl_engine_set_x509(_eng, &x509_minimal->vtable);
+ // ============================================================
+ // Allocatte and initialize Decoder Context
+ LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation");
+ // Only failure possible in the installation is OOM
+ #ifdef USE_MQTT_TLS_CA_CERT
+ x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context));
+ if (!x509_minimal) break;
+ br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, _ta_size);
+ br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng));
+ br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable);
+ br_ssl_engine_set_x509(_eng, &x509_minimal->vtable);
uint32_t now = UtcTime();
uint32_t cfg_time = CfgTime();
if (cfg_time > now) { now = cfg_time; }
br_x509_minimal_set_time(x509_minimal, now / 86400 + 719528, now % 86400);
- #else
- x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context));
- //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context);
- if (!x509_insecure) break;
- br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any);
- br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
- #endif
- LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation");
+ #else
+ x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context));
+ //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context);
+ if (!x509_insecure) break;
+ br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any);
+ br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
+ #endif
+ LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation");
- // ============================================================
- // Set send/receive buffers
- br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
+ // ============================================================
+ // Set send/receive buffers
+ br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
- // ============================================================
- // allocate Private key if needed, only if USE_MQTT_AWS_IOT
- LOG_HEAP_SIZE("_connectSSL before PrivKey allocation");
- #ifdef USE_MQTT_AWS_IOT
- // ============================================================
- // Set the EC Private Key, only USE_MQTT_AWS_IOT
- // limited to P256 curve
- br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1,
- _sk_ec_P, _allowed_usages,
- _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default());
- #endif // USE_MQTT_AWS_IOT
+ // ============================================================
+ // allocate Private key if needed, only if USE_MQTT_AWS_IOT
+ LOG_HEAP_SIZE("_connectSSL before PrivKey allocation");
+ #ifdef USE_MQTT_AWS_IOT
+ // ============================================================
+ // Set the EC Private Key, only USE_MQTT_AWS_IOT
+ // limited to P256 curve
+ br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1,
+ _sk_ec_P, _allowed_usages,
+ _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default());
+ #endif // USE_MQTT_AWS_IOT
- // ============================================================
- // Start TLS connection, ALL
- if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break;
+ // ============================================================
+ // Start TLS connection, ALL
+ if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break;
- auto ret = _wait_for_handshake();
- #ifdef DEBUG_ESP_SSL
- if (!ret) {
- DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError());
- } else {
- DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus());
- }
- #endif
- LOG_HEAP_SIZE("_connectSSL.end");
- _max_thunkstack_use = stack_thunk_light_get_max_usage();
- stack_thunk_light_del_ref();
- //stack_thunk_light_repaint();
- LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
+ auto ret = _wait_for_handshake();
+ #ifdef DEBUG_ESP_SSL
+ if (!ret) {
+ DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError());
+ } else {
+ DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus());
+ }
+ #endif
+ LOG_HEAP_SIZE("_connectSSL.end");
+ _max_thunkstack_use = stack_thunk_light_get_max_usage();
+ stack_thunk_light_del_ref();
+ //stack_thunk_light_repaint();
+ LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
- #ifdef USE_MQTT_TLS_CA_CERT
- free(x509_minimal);
- #else
- free(x509_insecure);
- #endif
- LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
- return ret;
- } while (0);
+ #ifdef USE_MQTT_TLS_CA_CERT
+ free(x509_minimal);
+ #else
+ free(x509_insecure);
+ #endif
+ LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
+ return ret;
+ } while (0);
- // ============================================================
- // if we arrived here, this means we had an OOM error, cleaning up
- setLastError(ERR_OOM);
- DEBUG_BSSL("_connectSSL: Out of memory\n");
- stack_thunk_light_del_ref();
+ // ============================================================
+ // if we arrived here, this means we had an OOM error, cleaning up
+ setLastError(ERR_OOM);
+ DEBUG_BSSL("_connectSSL: Out of memory\n");
+ stack_thunk_light_del_ref();
#ifdef USE_MQTT_TLS_CA_CERT
- free(x509_minimal);
+ free(x509_minimal);
#else
- free(x509_insecure);
+ free(x509_insecure);
#endif
- LOG_HEAP_SIZE("_connectSSL clean_on_error");
- return false;
+ LOG_HEAP_SIZE("_connectSSL clean_on_error");
+ return false;
}
};
From 00b3a9180a9de44af57a7954ee22d6edca5d0f2a Mon Sep 17 00:00:00 2001
From: Mike Harris
Date: Mon, 28 Dec 2020 15:54:28 -0800
Subject: [PATCH 026/255] Fix typo s/renegociation/renegotiation.
---
tasmota/WiFiClientSecureLightBearSSL.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index d7b53cf25..91f433c50 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -857,7 +857,7 @@ extern "C" {
// Default initializion for our SSL clients
static void br_ssl_client_base_init(br_ssl_client_context *cc) {
br_ssl_client_zero(cc);
- // forbid SSL renegociation, as we free the Private Key after handshake
+ // forbid SSL renegotiation, as we free the Private Key after handshake
br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION);
br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
From 525e55ff6ef86bf608cb19daf66ff50d7affb3a7 Mon Sep 17 00:00:00 2001
From: Mike Harris
Date: Tue, 29 Dec 2020 16:01:12 -0800
Subject: [PATCH 027/255] Change the pull pull request template branch to
development.
This was 'dev', I think it's supposed to be 'development'. This is
similar to PR #10297.
---
.github/PULL_REQUEST_TEMPLATE.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 1cb3f42cd..e542181b9 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -3,7 +3,7 @@
**Related issue (if applicable):** fixes #
## Checklist:
- - [ ] The pull request is done against the latest dev branch
+ - [ ] The pull request is done against the latest development branch
- [ ] Only relevant files were touched
- [ ] Only one feature/fix was added per PR and the code change compiles without warnings
- [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9
From a328f0e4de4d54bed62703d937911a64e66c2f98 Mon Sep 17 00:00:00 2001
From: Leon Wright
Date: Tue, 29 Dec 2020 15:35:43 +0800
Subject: [PATCH 028/255] Only apply mcp230xx_oldoutpincount when
USE_MCP230xx_OUTPUT set
---
tasmota/xsns_29_mcp230xx.ino | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino
index 6a960968a..27c91773b 100644
--- a/tasmota/xsns_29_mcp230xx.ino
+++ b/tasmota/xsns_29_mcp230xx.ino
@@ -196,6 +196,7 @@ void MCP230xx_ApplySettings(void)
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins);
#endif // USE_MCP230xx_OUTPUT
}
+#ifdef USE_MCP230xx_OUTPUT
TasmotaGlobal.devices_present -= mcp230xx_oldoutpincount;
mcp230xx_oldoutpincount = 0;
for (uint32_t idx=0;idx
Date: Wed, 30 Dec 2020 12:52:16 +0000
Subject: [PATCH 029/255] Allow up to 20s for an XHR request to succeed - only
cancel & retry after that. Move refresh timer inside successful response.
Applied to main menu and Console menu. Fixes times when commands are issued
from Web Console, but get the XHR gets cancelled after the default 2.345s
webrefresh time.
---
tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h | 67 ++++++-------
.../HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h | 42 ++++----
.../HTTP_SCRIPT_ROOT_WEB_DISPLAY.h | 54 +++++------
.../html_uncompressed/HTTP_SCRIPT_CONSOL.h | 97 ++++++++++---------
.../HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h | 16 +--
.../HTTP_SCRIPT_ROOT_WEB_DISPLAY.h | 27 +++---
6 files changed, 156 insertions(+), 147 deletions(-)
diff --git a/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h b/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h
index 91e1fab76..9138c4e8a 100644
--- a/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h
+++ b/tasmota/html_compressed/HTTP_SCRIPT_CONSOL.h
@@ -2,38 +2,39 @@
// compressed by tools/unishox/compress-html-uncompressed.py
/////////////////////////////////////////////////////////////////////
-const size_t HTTP_SCRIPT_CONSOL_SIZE = 853;
-const char HTTP_SCRIPT_CONSOL_COMPRESSED[] PROGMEM = "\x33\xBF\xAF\x71\xF0\xE3\x3A\x8B\x44\x3E\x1C\x67\x82\x30\x2F\x83\xAD\xCE\x41\x1D"
- "\xD1\x87\x78\xF6\x99\xDF\xD0\x67\x56\x1F\x0F\xB3\xEC\xEA\xA3\xC0\x61\x3B\xF9\x56"
- "\x8D\x78\x2E\x8E\xE8\x54\x77\x8F\x14\x7C\x63\x8E\xE9\xF7\x47\x21\xF6\x77\x8F\x05"
- "\xA6\x0E\xE8\xC3\xE1\xF0\xE4\x3B\xC7\xB4\x83\x3E\x31\xC7\x74\xFB\x0C\xE4\x3E\xCE"
- "\xF1\xE0\xB0\xF8\x7D\x9F\xA0\xCE\x43\xE1\xF6\x76\xC9\xF0\x78\x23\x21\x65\xF2\xD2"
- "\x0F\x06\x8C\xCE\x7D\x47\x74\x33\xA1\x9D\x84\x2D\x9D\xE3\xC0\x21\x45\x3E\x1F\x67"
- "\xD9\xE2\x8E\x9E\x0F\xF8\x10\x45\x58\x30\xF8\x71\x11\xBD\x2A\x01\xF1\xEE\x3E\x02"
- "\x35\x13\xC1\xEE\xD3\x07\x74\x11\xA6\x1F\x87\xCF\x71\xDE\x3D\xBA\x60\xEE\x9B\x0F"
- "\xE1\xF3\x85\x84\x11\xDE\x3D\xA6\xC3\xA5\x8E\xCF\xD1\xDD\x3B\xC7\x83\xDC\x6C\x3E"
- "\x73\x1F\x44\x6C\x21\xA4\x11\x0A\xAA\x18\x5F\x66\xA1\x6F\xD4\x77\x4E\xF1\xE0\xD8"
- "\x74\xCE\xFB\xB1\x0C\xBD\x57\x4C\x31\x57\xC3\xCC\xF8\x08\x7C\x28\x1D\xD0\x41\xCA"
- "\x8E\x9F\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F\xD1\xFA\x23\x61\xD3\xD5\x74\x2F"
- "\xC7\xC3\xE1\xCA\x6C\x81\x07\x87\x03\x69\xD4\x21\xE0\x43\xE1\xB0\xE9\xF7\xE1\x99"
- "\xDE\x65\x4C\xD9\x47\x4F\x0C\x0B\x68\xEE\x9D\x87\xB8\xE4\x3B\x0E\xF1\xE0\xB4\x43"
- "\xE0\x87\x4F\x0A\xD3\x14\x77\x4E\xF1\xE3\x4C\x1D\xD0\x44\x92\x7C\x3E\x1C\x67\x78"
- "\xF6\x95\x02\x2F\x0A\x27\xB8\xDA\x09\x38\x29\xB4\xE8\x13\xE1\xEA\x14\x7E\x02\x2E"
- "\x06\x76\xCF\x86\xD3\xC1\xEE\x05\xDE\x1E\x4F\x71\xE0\xD8\x74\xC1\x8F\x8E\xE9\xF6"
- "\x43\xC4\xCA\x8F\xB3\xA8\xFB\x0F\xC7\x68\x33\x94\x7C\x3E\xCE\xD9\x68\x87\x6F\x0E"
- "\xAA\xF8\xB6\x77\x8F\x06\xC3\xA7\x9F\x08\x77\x4E\xF1\xE0\xF7\x05\x47\xCF\x3A\x04"
- "\x4E\x4A\x4E\xA3\xE8\x43\xBC\x78\xFB\xA1\x7F\xE4\x62\xC2\xF3\x3C\x1E\xE1\xF0\x8E"
- "\xE8\x47\x78\xF0\x67\x7F\x42\x83\x3E\x1E\xF1\xEF\x9D\x41\xF0\x23\xF2\xF4\x28\xEE"
- "\x9D\xE3\xDA\x08\x7C\xA2\x9D\x2C\x41\x09\x99\xBE\xA2\x0B\x7D\x4F\x9F\xCE\xE9\xF6"
- "\x68\xCC\x84\xC1\xFE\x3E\xCE\xA0\x44\xE2\xDD\x82\x0F\x12\xA3\x81\x13\x97\xB3\xA8"
- "\x33\xE3\x3A\x1A\x33\x22\x0F\x04\x67\x8D\x30\x77\x4E\x5F\xCF\x87\xC2\x0C\xFF\x1F"
- "\xE3\x98\xCF\x87\xC2\x0C\xEF\x1E\xD1\xC7\x4B\x17\x58\x1E\x0D\x18\x13\xA6\x7C\x3E"
- "\xF0\xC1\x83\xEC\xF0\x7B\x8E\x5F\xCF\x87\xC2\x0C\xED\x1D\xD3\xB6\x76\xC3\xE3\xF0"
- "\x50\x60\x85\xC4\x31\xFA\x3F\x47\x74\x3E\x3E\x02\x24\xB3\xBC\x75\x0E\x04\x2E\x2E"
- "\x85\x06\x7B\xC1\xF1\xD6\x72\x1E\xF9\xFE\x3F\xC7\xD9\xF6\x77\x8F\x3C\x67\xC3\xE1"
- "\x06\x76\x8E\xE9\xC6\x7E\x1D\x67\x59\x07\xC0\x83\x88\x1C\x64\x0A\x78\x41\xC9\x67"
- "\xC3\xE1\x06\x7E\x8F\xD1\xDD\x04\x4C\xC4\xFC\x39\x11\xFA\x3F\x44\x28\x33\xA0\xCC"
- "\x18\x77\x4E\xF1\xD4\x28\x33\xA0\xBE\x04\x1E\x44\x01\x0B\x1C\x3B\xC7\x50\x7C\x7C"
- "\x38\xCE\xF1\xEE\x3B\xC7\x83\xDC\x43\xE1\x1D\xD1\x47\x78\xF0";
+const size_t HTTP_SCRIPT_CONSOL_SIZE = 913;
+const char HTTP_SCRIPT_CONSOL_COMPRESSED[] PROGMEM = "\x3D\xA1\x3A\x5E\xE3\xE1\xC6\x75\x16\x88\x7C\x38\xCE\xA2\x31\x47\x83\x02\xF8\x3A"
+ "\xDC\xE4\x11\xDD\x18\x77\x8F\x68\x4E\x90\x67\x56\x1F\x0F\xB3\xEC\xF0\x18\x4E\xFE"
+ "\x55\xA3\x5E\x0B\xA3\xBA\x15\x1D\xE0\x49\x9A\x62\x8E\xF1\xE2\x8F\x8C\x71\xDD\x3E"
+ "\xE8\xE4\x3E\xCE\xF1\xE0\xB4\xC1\xDD\x18\x7C\x3E\x1C\x87\x78\xF6\x90\x67\xC6\x38"
+ "\xEE\x9F\x61\x9C\x87\xD9\xDE\x3C\x16\x1F\x0F\xB3\xF4\x19\xC8\x7C\x3E\xCE\xD9\x3E"
+ "\x0F\x04\x64\x2C\xBE\x5A\x41\xE0\xD1\x99\xCF\xA8\xEE\x86\x74\x33\xB0\x85\xB3\xBC"
+ "\x78\x04\x28\xA7\xC3\xEC\xFB\x3C\x51\xD3\xC1\xFF\x02\x08\xAB\x06\x1F\x0E\x22\x37"
+ "\xA0\x1E\x3D\xC7\xC0\x46\xA2\x78\x3D\xDA\x60\xEE\x82\x34\xC3\xF0\xF9\xEE\x3B\xC7"
+ "\xB7\x4C\x1D\xD3\x61\xFC\x3E\x70\xB0\x82\x3B\xC7\xB4\xD8\x74\xB1\xD9\xFA\x3B\xA7"
+ "\x78\xF0\x7B\x8D\x87\xCE\x63\xE8\x8D\x84\x34\x82\x21\x55\x43\x0B\xEC\xD4\x2D\xFA"
+ "\x8E\xE9\xDE\x3C\x1B\x0E\x99\xDF\x76\x21\x97\xAA\xE9\x86\x2A\x2B\xF8\x79\x9F\x01"
+ "\x0F\x85\xF3\xBA\x08\x39\x51\xD3\xEE\xC4\x32\x2F\x55\xD3\x3E\x1F\x0E\x61\xFA\x3F"
+ "\x44\x6C\x3A\x7A\xAE\x85\xF8\xF8\x7C\x39\x4D\x90\x20\xF0\xFE\x6D\x3A\x84\x3C\x08"
+ "\x7C\x36\x1D\x3E\xFC\x33\x3B\xCC\xA9\x9B\x28\xE9\xE1\x81\x6D\x1D\xD3\xB0\xF7\x1C"
+ "\x87\x61\xDE\x3C\x16\x88\x7C\x10\xE9\xE1\x5A\x62\x8E\xE9\xDE\x3C\x69\x83\xBA\x08"
+ "\x92\x4F\x87\xC3\x8C\xEF\x1E\xD2\xA0\x45\xE1\x44\xF7\x1B\x41\x27\x05\x36\x9D\x02"
+ "\x7C\x3D\x42\x8F\xC0\x45\xC0\xCE\xD9\xF0\xDA\x78\x3D\xC0\xB9\xC3\xC8\x26\x71\xF4"
+ "\x15\x1F\x3C\xE8\x11\x39\x1D\x3A\x8F\xA1\x0E\xF1\xE0\xF7\x1E\xE3\xC1\xB0\xE9\x83"
+ "\x1F\x1D\xD3\xEC\x87\x89\x95\x1F\x67\x51\xF6\x1F\x8E\xD0\x67\x28\xF8\x7D\x9D\xB2"
+ "\xD1\x0E\xDE\x1D\x55\xF1\x6C\xEF\x1E\x0D\x87\x4F\x3E\x10\xEE\x9D\xE3\xC1\x80\x4A"
+ "\xC7\x4E\x53\x6D\x9D\xE3\xC1\xEE\x2F\xBA\x17\xFE\x46\x2C\x2F\x33\xC1\xEE\x1F\x08"
+ "\xEE\x84\x77\x8F\x01\x3A\x42\x83\x3E\x1E\xF1\xEF\x9D\x41\xF1\xF0\xE3\x20\x45\xE6"
+ "\xC4\x51\xDD\x3B\xC7\xB4\x10\xF9\x6D\x3A\x58\x82\x13\x33\x7D\x44\x16\xFA\x9F\x3F"
+ "\x9D\xD3\xEC\xD1\x99\x09\x83\xFC\x7D\x9D\x40\x89\xC6\xFB\x04\x1E\x2F\x47\x02\x27"
+ "\x34\x67\x50\x67\xC6\x74\x34\x66\x44\x1E\x08\xCF\x1A\x60\xEE\x9C\xBF\x9F\x0F\x84"
+ "\x19\xFE\x3F\xC7\x31\x9F\x0F\x84\x19\xDE\x3D\xA3\x8E\x96\x2E\xB0\x3C\x1A\x30\x27"
+ "\x4C\xF8\x7D\xE1\x83\x07\xD9\xE0\xF7\x1C\xBF\x9F\x0F\x84\x19\xDA\x3B\xA7\x6C\xED"
+ "\x87\xC7\xE0\xA0\xC1\x0B\x8A\xE3\xF4\x7E\x8E\xE8\x7C\x7C\x04\x49\x67\x78\xEA\x1C"
+ "\x08\x5C\x71\x0A\x0C\xF7\x83\xE3\xAC\xE4\x3D\xF3\xFC\x7F\x8F\xB3\xEC\xEF\x1E\x78"
+ "\xCF\x87\xC2\x0C\xED\x1D\xD3\x8C\xFC\x3A\xCE\xB2\x0F\x81\x07\x10\x38\xC8\x14\xF0"
+ "\x83\x92\xCF\x87\xC2\x0C\xFD\x1F\xA3\xBA\x08\x99\x89\xF8\x72\x23\xF4\x7E\x88\x50"
+ "\x67\x41\x98\x30\xEE\x9D\xE3\xA8\x50\x67\x41\x7C\x08\x3C\x9C\x02\x16\x38\x77\x8E"
+ "\xA0\xF8\xF8\x71\x9D\xE3\xDC\x77\x8F\x07\xB8\x87\xC2\x3B\xA2\x8E\xF1\xE0\xF7\x1B";
#define HTTP_SCRIPT_CONSOL Decompress(HTTP_SCRIPT_CONSOL_COMPRESSED,HTTP_SCRIPT_CONSOL_SIZE).c_str()
\ No newline at end of file
diff --git a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
index 2f61ce116..d7a530b43 100644
--- a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
+++ b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
@@ -2,26 +2,26 @@
// compressed by tools/unishox/compress-html-uncompressed.py
/////////////////////////////////////////////////////////////////////
-const size_t HTTP_SCRIPT_ROOT_SIZE = 524;
-const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x30\x2F\x83\xAD\xCE\x41\x59\xDD\x18\x77\x8F\x69\x9D\xFD\x59\xF0\xFB\x3E\xCF\x1A"
- "\x60\xEE\x85\x67\x4B\xF8\xF0\xB1\xAF\xAB\xC7\x40\x9F\x0F\x50\xA3\xE1\xF0\xE4\x3B"
- "\xC7\xB4\xAC\xF8\x30\xF0\x18\x4E\xFE\x55\xA3\x5E\x0B\xA3\xBA\x15\x1D\xE3\xC1\xEE"
- "\xD3\x07\x74\xD8\x7F\x0F\x9C\x2C\x20\x8E\xF1\xED\x36\x1D\x2C\x76\x7E\x8E\xE9\xDE"
- "\x3C\x1E\xE3\x61\xF3\x98\xFA\x23\x61\x0D\x20\x88\x55\x50\xC2\xFB\x35\x0B\x7E\xA3"
- "\xBA\x77\x8F\x06\xC3\xA6\x77\xDD\x88\x65\xEA\xBA\x61\x8A\xBE\x1E\x67\xC0\x43\xDA"
- "\x0E\xE9\xDE\x3D\xBA\x60\xEE\x9B\x0E\x9F\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F"
- "\xD1\xFA\x23\x61\xD3\xD5\x74\x2F\xC7\xC3\xE1\xCA\x6C\x81\x07\x80\x7F\x1F\x0D\x87"
- "\x4F\xBF\x0C\xCE\xF3\x2A\x2B\x66\xCA\x3A\x7D\x8C\x0A\xC3\x67\x74\xEC\x3D\xB4\x7B"
- "\x8E\xC1\xE3\xA8\xF6\x1E\x95\x63\x82\x6B\xD4\x64\x13\x3E\x1F\x63\xFA\x25\x0A\x3C"
- "\xCE\x46\xCF\xA3\xE8\xFB\x3F\x0F\x61\xDE\x20\x46\xC2\xBC\x08\x58\x57\xCF\xC3\xD2"
- "\x85\x02\x4D\x71\xA0\x83\x5C\xEC\xA1\x47\xE1\xE9\x42\x02\x4E\x4E\x72\x99\x0C\x36"
- "\x1E\x07\xC5\x6D\x33\xAF\xC3\x2C\x36\x79\xF6\x0F\xFE\xC6\x02\x56\x72\xC1\x0F\x1E"
- "\x10\xFC\x3D\x0E\xCA\xF8\x24\xD9\x0C\xF7\x1D\x83\xC7\x51\xEC\x3E\x8F\xA3\xEC\xFC"
- "\x3D\x04\xD3\x30\x43\xCE\xE9\x9B\x28\xEB\xB0\xB4\x7B\x8F\x30\xDF\x53\xF9\xE0\xC6"
- "\x75\x1D\x63\xEF\x47\x85\x51\xE6\x7B\x0E\xF1\xE1\x8E\x3B\xA7\xD8\x47\x21\xF6\x77"
- "\x8E\x85\xBD\xCF\xE4\x28\xA8\x86\x90\x47\xCF\x1E\x0F\x71\xEE\x3C\x1B\x0E\x98\x31"
- "\xF1\xDD\x3E\xC8\x78\x99\x51\xF6\x75\x1F\x67\x43\xB4\x34\xF8\x72\x1F\x67\x6C\xAC"
- "\xEA\xAF\x8B\x67\x78\xF0\x6C\x3A\x79\xF0\x87\x74\xEF\x1E\x02\xA3\xE7\x9D\x02\x27"
- "\x23\x96\x75\x1F\x42\x1D\xE3\xC1\xEE";
+const size_t HTTP_SCRIPT_ROOT_SIZE = 574;
+const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x3D\xA1\x3A\x46\x28\xF0\x60\x5F\x07\x5B\x9C\x82\xB3\xBA\x30\xEF\x1E\xDB\x3E\x0C"
+ "\x3F\xC7\xF8\xFB\x3E\xCF\x01\x84\xEF\xE5\x5A\x35\xE0\xBA\x3B\xA6\x28\xEF\x02\x4C"
+ "\xD0\xA8\xEF\x1E\x34\xC1\xDD\x36\x1F\xC3\xE7\x0B\x08\x23\xBC\x7B\x4D\x87\x4B\x1D"
+ "\x9F\xA3\xBA\x77\x8F\x71\xB0\xF9\xCC\x7D\x11\xB0\x86\x90\x44\x2A\x2B\xA8\x61\x7D"
+ "\x9A\x85\xBF\x51\xDD\x3B\xC7\x83\x61\xD3\x3B\xEE\xC4\x32\xF5\x5D\x30\xC5\x5F\x0F"
+ "\x33\xE0\x21\xEA\xE7\x74\xEF\x1E\xDD\x30\x77\x4D\x87\x4F\xBB\x10\xC8\xBD\x57\x4C"
+ "\xF8\x7C\x39\x87\xE8\xFD\x11\xB0\xE9\xEA\xBA\x17\xE3\xE1\xF0\xE5\x36\x77\x8F\x69"
+ "\x19\xDF\xD7\x8F\x86\xC3\xA7\xDF\x86\x67\x79\x95\x33\x65\x1D\x3E\xC6\x05\x61\xB3"
+ "\xBA\x76\x1E\xDA\x3D\xC7\x60\xF1\xD4\x7B\x0F\x4A\xB1\xC1\x35\xEA\x32\x09\x9F\x0F"
+ "\xB1\xFD\x12\x85\x1E\x67\x23\x67\xD1\xF4\x7D\x9F\x87\xB0\xEF\x10\x23\x61\x5E\x04"
+ "\x2C\x2B\xE7\xE1\xE9\x42\x81\x26\xB8\xD0\x41\xAE\x76\x50\xA3\xF0\xF4\xA1\x01\x27"
+ "\x27\x39\x4C\x86\x1B\x0F\x03\xE2\xB6\x99\xD7\xE1\x96\x1B\x3C\xFB\x07\xFF\x63\x01"
+ "\x2B\x39\x60\x87\x8F\x08\x7E\x1E\x87\x65\x7C\x12\x6C\x86\x7B\x8E\xC1\xE3\xA8\xF6"
+ "\x1F\x47\xD1\xF6\x7E\x1E\x82\x69\x98\x21\xE7\x74\xCD\x94\x75\xD8\x5A\x3D\xC7\x98"
+ "\x6F\xA9\xFC\xF0\x63\x3A\x8E\xB1\xF7\xA3\xC2\xA8\xF3\x3D\x87\x78\xF0\xC7\x1D\xD3"
+ "\xEC\x23\x90\xFB\x3B\xC7\x42\xDE\xE7\xF2\x14\x54\x43\x48\x23\xE7\x81\x7B\x90\x10"
+ "\xA8\xF9\xE7\x40\x89\x3A\xCE\xA3\xE8\x43\xBC\x78\x3D\xC7\xB8\xF0\x6C\x3A\x60\xC7"
+ "\xC7\x74\xFB\x21\xE2\x65\x47\xD9\xD4\x7D\x9D\x0E\xD0\xD3\xE1\xC8\x7D\x9D\xB2\xB3"
+ "\xAA\xBE\x2D\x9D\xE3\xC1\xB0\xE9\xE7\xC2\x1D\xD3\xBC\x78\x30\x09\x78\xD1\xCA\x6D"
+ "\xB3\xBC\x78\x3D\xC7\xB8";
#define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str()
\ No newline at end of file
diff --git a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h
index bb52fbbcd..1481a609c 100644
--- a/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h
+++ b/tasmota/html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h
@@ -2,32 +2,32 @@
// compressed by tools/unishox/compress-html-uncompressed.py
/////////////////////////////////////////////////////////////////////
-const size_t HTTP_SCRIPT_ROOT_SIZE = 744;
-const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x33\xBF\xAF\x98\xF0\xA3\xE1\xC8\x78\x23\x02\xF8\x3A\xDC\xE4\x15\x9D\xD1\x87\x78"
- "\xF6\x99\xDF\xD5\x9F\x0F\xB3\xEC\xF1\xA6\x0E\xE8\x56\x74\xBF\x8F\x0B\x1A\xFA\xBC"
- "\x74\x09\xF0\xF5\x0A\x3E\x1F\x0E\x43\xBC\x7B\x4A\xCF\x83\x0F\x01\x84\xEF\xE5\x5A"
- "\x35\xE0\xBA\x3B\xA1\x51\xDE\x3C\x1E\xED\x30\x77\x4D\x87\xF0\xF9\xC2\xC2\x08\xEF"
- "\x1E\xD3\x61\xD2\xC7\x67\xE8\xEE\x9D\xE3\xC1\xEE\x36\x1F\x39\x8F\xA2\x36\x10\xD2"
- "\x08\x85\x55\x0C\x2F\xB3\x50\xB7\xEA\x3B\xA7\x78\xF0\x6C\x3A\x67\x7D\xD8\x86\x5E"
- "\xAB\xA6\x18\xAB\xE1\xE6\x7C\x04\x3D\xA0\xEE\x9D\xE3\xDB\xA6\x0E\xE9\xB0\xE9\xF7"
- "\x62\x19\x17\xAA\xE9\x9F\x0F\x87\x30\xFD\x1F\xA2\x36\x1D\x3D\x57\x42\xFC\x7C\x3E"
- "\x1C\xA6\xC8\x10\x78\x07\xF1\xF0\xD8\x74\xFB\xF0\xCC\xEF\x32\xA6\x6C\xA3\xA7\xD8"
- "\xC0\xAC\x36\x77\x4E\xC3\xDB\x47\xB8\xEC\x1E\x3A\x8F\x61\xE9\x56\x38\x26\xBD\x46"
- "\x41\x33\xE1\xF6\x3F\xA2\x50\xA3\xCC\xE4\x6C\xFA\x3E\x8F\xB3\xF0\xF6\x1D\xE2\x04"
- "\x6C\x2B\xC0\x85\x85\x7C\xFC\x3D\x28\x50\x24\xD7\x1A\x08\x35\xCE\xCA\x14\x7E\x1E"
- "\x94\x20\x24\xE4\xE7\x29\x90\xC3\x61\xE0\x7C\x56\xD3\x3A\xFC\x32\xC3\x67\x9F\x60"
- "\xFF\xEC\x60\x25\x67\x2C\x10\xF1\xE1\x0F\xC3\xD0\xEC\xAF\x82\x4D\x90\xCF\x71\xD8"
- "\x3C\x75\x1E\xC3\xE8\xFA\x3E\xCF\xC3\xD0\x4D\x33\x04\x3C\xEE\x99\xB2\x8E\xBB\x0B"
- "\x47\xB8\xF3\x0D\xF5\x3F\x9E\x0C\x67\x51\xD6\x3E\xF4\x78\x55\x1E\x67\xB0\xEF\x1E"
- "\x18\xE3\xBA\x7D\x84\x72\x1F\x67\x78\xE8\x5B\xDC\xFE\x42\x8A\x88\x69\x04\x7C\xF1"
- "\xE0\xF7\x1E\xE3\xC6\x98\x47\x77\xE6\x3C\x28\xEF\x23\xDA\x6C\x3A\x60\xC7\xC7\x74"
- "\xFB\x21\xE2\x65\x47\xD9\xD4\x7D\x9D\x0E\xD0\xD3\xE1\xC8\x7D\x9D\xB2\xB3\xAA\xBE"
- "\x2D\x9D\xE3\xC1\xB0\xE9\xE7\xC2\x1D\xD3\xBC\x78\x0A\x8F\x9E\x74\x08\x9C\x93\xD9"
- "\xD4\x7D\x08\x77\x8F\x07\xB8\xF7\x02\x27\x2E\x9E\x66\x76\x77\x46\x5F\xCE\xAD\x33"
- "\xBF\x9D\xE3\xDA\x15\x9D\xD3\xEC\xFD\x78\xCC\xF8\x7D\x9D\xBD\x33\xBF\x9D\xB3\xEC"
- "\xFD\x9F\x67\x6C\x65\xFC\xEF\x1E\x01\x1B\x0D\xD0\x48\xC3\x41\x0B\x9C\x40\x53\xC5"
- "\x3E\x63\xC2\x8F\x87\x19\x02\x36\x36\x33\xE7\x74\xC1\xDE\x3D\xBA\x61\x1D\xD3\x07"
- "\x79\x1E\xD0\x50\xDE\x81\x0B\x2F\x3D\xC9\x85\xE6\x8F\x68\x26\x73\xD0\x08\x79\x81"
- "\xEE";
+const size_t HTTP_SCRIPT_ROOT_SIZE = 844;
+const char HTTP_SCRIPT_ROOT_COMPRESSED[] PROGMEM = "\x09\xD2\xF9\x8F\x0A\x3E\x1C\x87\x51\x18\xA3\xC1\x81\x7C\x1D\x6E\x72\x0A\xCE\xE8"
+ "\xC3\xBC\x7B\x6C\xF8\x30\xFF\x1F\xE3\xEC\xFB\x3C\x06\x13\xBF\x95\x68\xD7\x82\xE8"
+ "\xEE\x98\xA3\xBC\x09\x33\x42\xA3\xBC\x78\xD3\x07\x74\xD8\x7F\x0F\x9C\x2C\x20\x8E"
+ "\xF1\xED\x36\x1D\x2C\x76\x7E\x8E\xE9\xDE\x3C\x1E\xE3\x61\xF3\x98\xFA\x23\x61\x0D"
+ "\x20\x88\x55\x50\xC2\xFB\x35\x0B\x7E\xA3\xBA\x77\x8F\x06\xC3\xA6\x77\xDD\x88\x65"
+ "\xEA\xBA\x61\x8A\xBE\x1E\x67\xC0\x43\xD6\x0E\xE9\xDE\x3D\xBA\x60\xEE\x9B\x0E\x9F"
+ "\x76\x21\x91\x7A\xAE\x99\xF0\xF8\x73\x0F\xD1\xFA\x23\x61\xD3\xD5\x74\x2F\xC7\xC3"
+ "\xE1\xCA\x6C\xEF\x1E\xD2\x33\xBF\xAF\x1F\x0D\x87\x4F\xBF\x0C\xCE\xF3\x2A\x2B\x66"
+ "\xCA\x3A\x7D\x8C\x0A\xC3\x67\x74\xEC\x3D\xB4\x7B\x8E\xC1\xE3\xA8\xF6\x1E\x95\x63"
+ "\x82\x6B\xD4\x64\x13\x3E\x1F\x63\xFA\x25\x0A\x3C\xCE\x46\xCF\xA3\xE8\xFB\x3F\x0F"
+ "\x61\xDE\x20\x46\xC2\xBC\x08\x58\x57\xCF\xC3\xD2\x85\x02\x4D\x71\xA0\x83\x5C\xEC"
+ "\xA1\x47\xE1\xE9\x42\x02\x4E\x4E\x72\x99\x0C\x36\x1E\x07\xC5\x6D\x33\xAF\xC3\x2C"
+ "\x36\x79\xF6\x0F\xFE\xC6\x02\x56\x72\xC1\x0F\x1E\x10\xFC\x3D\x0E\xCA\xF8\x24\xD9"
+ "\x0C\xF7\x1D\x83\xC7\x51\xEC\x3E\x8F\xA3\xEC\xFC\x3D\x04\xD3\x30\x43\xCE\xE9\x9B"
+ "\x28\xEB\xB0\xB4\x7B\x8F\x30\xDF\x53\xF9\xE0\xC6\x75\x1D\x63\xEF\x47\x85\x51\xE6"
+ "\x7B\x0E\xF1\xE1\x8E\x3B\xA7\xD8\x47\x21\xF6\x77\x8E\x85\xBD\xCF\xE4\x28\xA8\x86"
+ "\x90\x47\xCF\x03\x01\xE4\x0B\xE6\x3C\x28\xEF\x1E\xD0\xA8\xF9\xE7\x40\x89\x5E\xCE"
+ "\xA3\xE8\x43\xBC\x78\x3D\xC7\xB8\xF7\x02\x2D\xE3\x61\xD3\x06\x3E\x3B\xA7\xD9\x0F"
+ "\x13\x2A\x2B\x3E\xCE\xA3\xEC\xE8\x76\x86\x9F\x0E\x43\xEC\xED\x95\x9D\x55\xF1\x6C"
+ "\xEF\x1E\x0D\x87\x4F\x3E\x10\xEE\x9D\xE3\xC1\x80\x4B\xC9\x0E\x53\x6D\x9D\xE3\xC1"
+ "\xEE\x3D\xC4\x08\x9C\xD3\x79\x99\xD9\xDD\x19\x7F\x3A\xB4\xCE\xFE\x77\x8F\x68\x56"
+ "\x77\x4F\xB3\xF5\xE3\x33\xE1\xF6\x76\xF4\xCE\xFE\x76\xCF\xB3\xF6\x7D\x9D\xB1\x97"
+ "\xF3\xBC\x78\x04\x6C\x37\x41\x23\x0D\x04\x1E\x7E\x4F\x2A\x01\xA7\x8A\x7C\xC7\x85"
+ "\x1F\x0E\x32\x04\x6C\x6C\x67\xCE\xE9\x83\xBC\x7B\x74\xC1\xDD\x30\x77\x8F\x68\x26"
+ "\x70\xBA\x09\x9C\x3F\x82\x87\x0C\xA0\x85\xA7\x9E\xE6\x17\x98\x2F\x64\x2A\x01\x87"
+ "\xAF\x9E\xE3";
#define HTTP_SCRIPT_ROOT Decompress(HTTP_SCRIPT_ROOT_COMPRESSED,HTTP_SCRIPT_ROOT_SIZE).c_str()
\ No newline at end of file
diff --git a/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h b/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h
index 51cfb66a3..0351e828e 100644
--- a/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h
+++ b/tasmota/html_uncompressed/HTTP_SCRIPT_CONSOL.h
@@ -1,49 +1,54 @@
const char HTTP_SCRIPT_CONSOL[] PROGMEM =
- "var sn=0,id=0;" // Scroll position, Get most of weblog initially
- "function l(p){" // Console log and command service
- "var c,o='',t;"
- "clearTimeout(lt);"
- "t=eb('t1');"
- "if(p==1){"
- "c=eb('c1');" // Console command id
- "o='&c1='+encodeURIComponent(c.value);"
- "c.value='';"
- "t.scrollTop=99999;"
- "sn=t.scrollTop;"
+ "{"
+ "let sn=0,id=0,ft;" // Scroll position, Get most of weblog initially
+ "function l(p){" // Console log and command service
+ "let c,o='';"
+ "clearTimeout(lt);"
+ "clearTimeout(ft);"
+ "t=eb('t1');"
+ "if(p==1){"
+ "c=eb('c1');" // Console command id
+ "o='&c1='+encodeURIComponent(c.value);"
+ "c.value='';"
+ "t.scrollTop=99999;"
+ "sn=t.scrollTop;"
+ "}"
+ "if(t.scrollTop>=sn){" // User scrolled back so no updates
+ "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
+ "x=new XMLHttpRequest();"
+ "x.onreadystatechange=function(){"
+ "if(x.readyState==4&&x.status==200){"
+ "let z,d;"
+ "d=x.responseText.split(/}1/);" // Field separator
+ "id=d.shift();"
+ "if(d.shift()==0){t.value='';}"
+ "z=d.shift();"
+ "if(z.length>0){t.value+=z;}"
+ "t.scrollTop=99999;"
+ "sn=t.scrollTop;"
+ "clearTimeout(ft);"
+ "lt=setTimeout(l,%d);" // webrefresh timer....
+ "}"
+ "};"
+ "x.open('GET','cs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp))
+ "x.send();"
+ "ft=setTimeout(l,20000);" // fail timeout, triggered 20s after asking for XHR
+ "}"
+ "return false;"
"}"
- "if(t.scrollTop>=sn){" // User scrolled back so no updates
- "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
- "x=new XMLHttpRequest();"
- "x.onreadystatechange=function(){"
- "if(x.readyState==4&&x.status==200){"
- "var z,d;"
- "d=x.responseText.split(/}1/);" // Field separator
- "id=d.shift();"
- "if(d.shift()==0){t.value='';}"
- "z=d.shift();"
- "if(z.length>0){t.value+=z;}"
- "t.scrollTop=99999;"
- "sn=t.scrollTop;"
- "}"
- "};"
- "x.open('GET','cs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp))
- "x.send();"
- "}"
- "lt=setTimeout(l,%d);"
- "return false;"
- "}"
- "wl(l);" // Load initial console text
+ "wl(l);" // Load initial console text
- // Console command history
- "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown
- "function h(){"
-// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete
- "eb('c1').addEventListener('keydown',function(e){"
- "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id
- "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete
- "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp
- "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown
- "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history
- "});"
- "}"
- "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd))
+ // Console command history
+ "let hc=[],cn=0;" // hc = History commands, cn = Number of history being shown
+ "function h(){"
+ // "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete
+ "eb('c1').addEventListener('keydown',function(e){"
+ "let b=eb('c1'),c=e.keyCode;" // c1 = Console command id
+ "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete
+ "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp
+ "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown
+ "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history
+ "});"
+ "}"
+ "wl(h);" // Add console command key eventlistener after name has been synced with id (= wl(jd))
+ "}";
diff --git a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
index 729bb54a5..dd2cdc796 100644
--- a/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
+++ b/tasmota/html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h
@@ -1,11 +1,9 @@
const char HTTP_SCRIPT_ROOT[] PROGMEM =
+ "{let ft;"
"function la(p){"
- "var a='';"
- "if(la.arguments.length==1){"
- "a=p;"
- "clearTimeout(lt);"
- "}"
- "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
+ "a=p||'';"
+ "clearTimeout(ft);clearTimeout(lt);"
+ "if(x!=null){x.abort()}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){"
@@ -16,9 +14,11 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM =
".replace(/{e}/g,\" |