/* Espruino HDMI EDID fuzzer: Fakes a 'screen' ( slave ) when being attached to a real 'source' & send EDID infos when requested R: a connected macbookpro seems to read the main EDID bytes ( 128 starting at 0 ) & then EDID Ext ( 128 starting at 128 ) by writing 0 & requesting bytes, then writing 128 & requesting more bytes hence, a 128 bytes buffer should be enough for i2c slave Tx Bat -> +V Gnd -> HDMI Gnd pin 17 A0 -> HDMI Hot Plug pin 19 -> HDMI SDA pin 16 -> HDMI SCL pin 15 disabling Wire lib's use of internal pull-up resistors & external pull-up resistors of 4.7k is advised on SDA and/or SCL may be necessary: look for the following in twi.cpp digitalWrite(SDA, 1); // old "sbi(PORTC, 4)" digitalWrite(SCL, 1); // old "sbi(PORTC, 5)" R: space available is soo tiny :/ using TWI_BUFFER_LENGTH & BUFFER_LENGTH of 256 bytes: sketch: 3474 bytes (10%) of program storage global variables: 1414 bytes (69%) of dynamic memory 634 bytes free for local variables using modded Wire.h, Wire.cpp, twi.h & twi.cpp for asymetric buffer lengths: sketch: 3484 bytes (10%) of program storage global variables: 906 bytes (44%) of dynamic memory 1142 bytes free for local variables */ // Twi.h L32: TWI_BUFFER_LENGTH --> no longer relevant* #include // Wire.h L29: BUFFER_LENGTH = 256 --> no longer relevant* // * Wire.h, Wire.cpp, twi.h & twi.cpp have been modded to support asymetric buffer lengths for master, rx & tx :) // ( the goal was to reduce the size taken by the lib's buffers that wouldn't be used ) // Since I couldn't get reliable 256 bytes in Espruino, both master & Tx buffers are currently set back to 128 bytes // the byte received from a 'source' acting as master on the i2c line - used as start point when updated before being requested bytes byte data = -1; // if the byte received has changed from -1 to stg else before being reset to -1 boolean startByteChanged = false; // our slave i2c addr as a 'screen' const int SCREEN_I2C_ADDR = 0x50; // our pins mapping const int HDMI_HOTPLUG_PIN = 4; // D4 // our request counter: if being requested bytes & 'byte' is still == -1 ( no start point have been passed ), alternate between 1 & 2 int reqCntr = 1; // an EDID previously received over serial or spi from Espruino ;) // Nb: we don't use 'const' sine it may be updated by Espruino :p //const uint8_t edid[256] = {}; // alternative ;) // | /* // Samsung TV screen - modded so that the manufacturer id is no longer "SAM" for Samsung but "TEF" for Stephane ;) const byte edid[256] = { 0x0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x50, 0xA6, 0x70, 0x4, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x1, 0x3, 0x80, 0x10, 0x9, 0x78, 0xA, 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0xF, 0x50, 0x54, 0x21, 0x8, 0x0, 0x81, 0x80, 0xA9, 0x40, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x66, 0x21, 0x50, 0xB0, 0x51, 0x0, 0x1B, 0x30, 0x40, 0x70, 0x36, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0xFD, 0x0, 0x17, 0x3D, 0x1A, 0x4C, 0x17, 0x0, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x53, 0x41, 0x4D, 0x53, 0x55, 0x4E, 0x47, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1, 0xB9, 0x2, 0x3, 0x27, 0xF1, 0x4B, 0x90, 0x1F, 0x4, 0x13, 0x5, 0x14, 0x3, 0x12, 0x20, 0x21, 0x22, 0x23, 0x9, 0x7, 0x7, 0x83, 0x1, 0x0, 0x0, 0xE2, 0x0, 0xF, 0xE3, 0x5, 0x3, 0x1, 0x67, 0x3, 0xC, 0x0, 0x20, 0x0, 0xB8, 0x2D, 0x1, 0x1D, 0x0, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28, 0x55, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x0, 0xBC, 0x52, 0xD0, 0x1E, 0x20, 0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x80, 0x18, 0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x1, 0x1D, 0x80, 0xD0, 0x72, 0x1C, 0x16, 0x20, 0x10, 0x2C, 0x25, 0x80, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD }; */ // Samsung TV screen - EISA ID: TEF / Product Code: 4687 / Serial Number: 17042401 / Manufacture Date: 39/2015 / Monitor Name: VIRTUOSA const byte edid[256] = { 0x0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x50, 0xA6, 0x87, 0x46, 0xE1, 0x0B, 0x04, 0x01, 0x27, 0x19, 0x1, 0x3, 0x80, 0x10, 0x9, 0x78, 0xA, 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0xF, 0x50, 0x54, 0x21, 0x8, 0x0, 0x81, 0x80, 0xA9, 0x40, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x66, 0x21, 0x50, 0xB0, 0x51, 0x0, 0x1B, 0x30, 0x40, 0x70, 0x36, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0xFD, 0x0, 0x17, 0x3D, 0x1A, 0x4C, 0x17, 0x0, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x56, 0x49, 0x52, 0x54, 0x55, 0x4F, 0x53, 0x41, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x1, 0x18, 0x2, 0x3, 0x27, 0xF1, 0x4B, 0x90, 0x1F, 0x4, 0x13, 0x5, 0x14, 0x3, 0x12, 0x20, 0x21, 0x22, 0x23, 0x9, 0x7, 0x7, 0x83, 0x1, 0x0, 0x0, 0xE2, 0x0, 0xF, 0xE3, 0x5, 0x3, 0x1, 0x67, 0x3, 0xC, 0x0, 0x20, 0x0, 0xB8, 0x2D, 0x1, 0x1D, 0x0, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28, 0x55, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x0, 0xBC, 0x52, 0xD0, 0x1E, 0x20, 0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x80, 0x18, 0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x1, 0x1D, 0x80, 0xD0, 0x72, 0x1C, 0x16, 0x20, 0x10, 0x2C, 0x25, 0x80, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD }; const byte edidExt[128] = { 0x2, 0x3, 0x27, 0xF1, 0x4B, 0x90, 0x1F, 0x4, 0x13, 0x5, 0x14, 0x3, 0x12, 0x20, 0x21, 0x22, 0x23, 0x9, 0x7, 0x7, 0x83, 0x1, 0x0, 0x0, 0xE2, 0x0, 0xF, 0xE3, 0x5, 0x3, 0x1, 0x67, 0x3, 0xC, 0x0, 0x20, 0x0, 0xB8, 0x2D, 0x1, 0x1D, 0x0, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28, 0x55, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x0, 0xBC, 0x52, 0xD0, 0x1E, 0x20, 0xB8, 0x28, 0x55, 0x40, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x1, 0x1D, 0x80, 0x18, 0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x1, 0x1D, 0x80, 0xD0, 0x72, 0x1C, 0x16, 0x20, 0x10, 0x2C, 0x25, 0x80, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x9E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD }; /* // Samsung TV screen - without extended infos ( additional 128 bytes I can't afford ? .. ) const byte edid[128] = { 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x4c, 0x2d, 0x70, 0x4, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x1, 0x3, 0x80, 0x10, 0x9, 0x78, 0xa, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0xf, 0x50, 0x54, 0x21, 0x8, 0x0, 0x81, 0x80, 0xa9, 0x40, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x0, 0xa0, 0x5a, 0x0, 0x0, 0x0, 0x1e, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x0, 0x1b, 0x30, 0x40, 0x70, 0x36, 0x0, 0xa0, 0x5a, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x17, 0x3d, 0x1a, 0x4c, 0x17, 0x0, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1, 0x36 }; */ /* // Samsung TV screen - with TEF manufacturer & without extended infos ( additional 128 bytes I can't afford ? .. ) const byte edid[128] = { 0x0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x50, 0xA6, 0x70, 0x4, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x1, 0x3, 0x80, 0x10, 0x9, 0x78, 0xA, 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0xF, 0x50, 0x54, 0x21, 0x8, 0x0, 0x81, 0x80, 0xA9, 0x40, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x66, 0x21, 0x50, 0xB0, 0x51, 0x0, 0x1B, 0x30, 0x40, 0x70, 0x36, 0x0, 0xA0, 0x5A, 0x0, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x0, 0xFD, 0x0, 0x17, 0x3D, 0x1A, 0x4C, 0x17, 0x0, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x53, 0x41, 0x4D, 0x53, 0x55, 0x4E, 0x47, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1, 0xB9 }; */ // == events handlers tied to interrupts == // executed when data is received from the 'source' acting as i2c master void receiveEvent(int receivedByte){ // receivedByte /* if(receivedByte == 1){ int receivedValue = Wire.read() << 1; receivedValue |= Wire.read(); Serial.print("Read byte: "); Serial.println(receivedValue); } else { Serial.println("not the expected amount of bytes: "); Serial.println(receivedByte); } */ if( receivedByte < 2 ){ // std Serial.print("std: 1 byte received: "); data = Wire.read(); Serial.println(data); } else { Serial.println("more than one byte received: "); while ( Wire.available() ){ //data = Wire.read(); Serial.println(data); // seems delayed for 1st transmition then works if Espruino after .. //Wire.read(); Serial.println( Wire.read() ); } } /* -- curr way */ /* while ( Wire.available() ){ data = Wire.read(); Serial.println(data); // seems delayed for 1st transmition then works if Espruino after .. //Wire.read(); } */ return; /**/ /* while ( Wire.available() ){ // loop through all but last ? int x = Wire.read(); // from deprecated Wire.receive(); //Serial.println(x); } int x = Wire.read(); // from deprecated Wire.receive(); Serial.println(x); */ } // executed when data is requested by the 'source' acting as i2c master void requestEvent(){ //Wire.write(edid, 256); // never seem to work .. // return manufacturer bytes whatever - seems to work ! //Wire.write(edid[8]); //Wire.write(edid[9]); /* for(int i=0; i < 256; i++){ Wire.write(edid[i]); //delay(20); } */ //Wire.write(edid, 128); // seems to work ( even with a buffer twice that size .. ) //Wire.write(edid, 128); /* seem to work as well .. for(int i=0; i < 128; i++){ Wire.write(edid[i]); } */ //for(int i=0; i < 200; i++){ // 200 works ( end is 0xb8, 0x28, 0x55, 0x40, 0xa0, 0x5a, 0x0 ) //for(int i=0; i < 220; i++){ // 220 works ( end is 0x25, 0x0, 0xa0, 0x5a, 0x0, 0x0, 0x0 ) //for(int i=0; i < 230; i++){ // 230 works ( end is 0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20, 0x10 ) //for(int i=0; i < 240; i++){ // 240 works ( end is 0xa0, 0x5a, 0x0, 0x0, 0x0, 0x9e, 0x0 ) //for(int i=0; i < 250; i++){ // 250 works ( end is 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ) //for(int i=0; i < 254; i++){ // 254 works ( end is 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ) //for(int i=0; i < 255; i++){ // 256 works ( end is 0x9e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ) //for(int i=0; i <= 254; i++){ // 256 DOESN'T work :/ /* for(int i=0; i < 128; i++){ Wire.write(edid[i]); } */ //delay(200); //Wire.write(edid[255]); //Wire.write(edidExt, 120); // works //Wire.write(edidExt, 126); // works - I need two more bytes !!! //Wire.write(edidExt, 127); // works - I need ONE more byte !!! //Wire.write(edidExt, 128); // NO LONGER ANYTHING !! :/ .. ==> seems really to come from the Espruino side -> 'll try with another Ar // check if we were passed a byte to start from ( data ) if( data != -1){ if(data < 0x7F){ // start byte is < 127: use EDID for(int i=data; i < 128; i++){ Wire.write(edid[i]); } // write [startByte..EDID end] range for(int i=0; i < data; i++){ Wire.write(edid[i]); } // write [0..startByte] range } else if(data > 0x7F){ // start byte is > 127: use EDID Ext for(int i=data; i < 256; i++){ Wire.write(edid[i]); } // write [startByte..EDID end] range for(int i=128; i < data; i++){ Wire.write(edid[i]); } // write [0..startByte] range -- or i=127 ? .. } //startByteChanged = true; data = -1; } else { //if(startByteChanged == true){ //reqCntr = 1; //Wire.write(edid, 128); //reqCntr = 2; //startByteChanged = false; //} else { if(reqCntr == 1){ Wire.write(edid, 128); reqCntr = 2; } else if(reqCntr == 2){ Wire.write(edidExt, 128); reqCntr = 1; } else { Serial.println('weird ?!'); } //} } /* worked fine to alternate between EDID & EDID Ext if(reqCntr == 1){ Wire.write(edid, 128); reqCntr = 2; } else { Wire.write(edidExt, 128); reqCntr = 1; } */ } // to be executed whenever a device connects to this screen ( for std screens & hdmi switcher, 'd be when a source has been selected for display ) void fakeHotPlug(){ digitalWrite(HDMI_HOTPLUG_PIN, LOW); delay(20); digitalWrite(HDMI_HOTPLUG_PIN, HIGH); } void setup() { //Wire.setClock(400000); /* -- curr way Wire.begin(SCREEN_I2C_ADDR); // join i2c bus as slave with addr 0x50 Wire.onRequest(requestEvent); // event handler for requests from i2c master 'source' Wire.onReceive(receiveEvent); // event handler when receving from i2c master 'source' Serial.begin(9600); Serial.println("**** HDMI slave ****"); pinMode(HDMI_HOTPLUG_PIN, OUTPUT); // setup hdmi hot plug pin fakeHotPlug(); // notify the i2c master 'source' device that an i2c slave 'screen' is present */ pinMode(HDMI_HOTPLUG_PIN, OUTPUT); // setup hdmi hot plug pin digitalWrite(HDMI_HOTPLUG_PIN, LOW); Wire.begin(SCREEN_I2C_ADDR); // join i2c bus as slave with addr 0x50 Wire.onReceive(receiveEvent); // event handler when receving from i2c master 'source' Wire.onRequest(requestEvent); // event handler for requests from i2c master 'source' Serial.begin(9600); digitalWrite(HDMI_HOTPLUG_PIN, HIGH); } void loop() {}