For some reason I always pick up these dumb scrolling message things at Fry’s, first one had an embedded uC that Furan had hacked by RE’ing the wires and installing a new uC, this one has USB but doesn’t work without the program running on the Host PC….
So lets hack it..
First thing is that it is a USB HID , so that is fairly straightforward. HID Write comes into play here. Jan Axelson is pretty much the standard for USB/serial etc http://www.lvr.com/hidpage.htm
A quick poke around the bytes look like, brightness, location, data. 0xFF is all off, 0×00 all on. right most byte is left most on the display at 0×00 0×00 0xFF 0xFF 0xFF 0xFF 0×00
First byte is brightness
Second byte is vertical row 0×5 is the bottom, two lines are written at once. If you give it 0×6 it’ll write the first line to the last row, then wrap after a few that disappear.
It takes 0×9 message length, and no read back.
That’s it on the hardware side really!
Using usbhido_vc6 as an example, all you have to do is FindHid() with the VID/PID set, and then do
I just threw in some junk code to show it working
if( MyDeviceDetected ) {
// always
OutputReport[0 ] = 0×0;
// brightness 0 .. 15
OutputReport[1 ] = 15;
OutputReport[2 ] = row;
row+=2;
row%=8;
// fill in some random data
for( int i= 3 ; i < 10 ;i ++ ) {
OutputReport[i]++;;
}
if (WriteHandle != INVALID_HANDLE_VALUE)
{
HRESULT Result = HidD_SetOutputReport
(WriteHandle,
OutputReport,
9); // fixed length (comes from device report)
}
}
It’s trivial to treat the output as a frame buffer, 21×7
so the header of the OutputReport[] looks like
offset 0, always 00
offset 1, brightness, 0-15
offset 2, row *2
then the remaining bytes are
offset 3, 0×00 – right hand side 5 LEDS on
offset 4, 0×00 – middle 8 LEDS all on
offset 5, 0×00 – left 8 LEDS all on
offset 6, 0×00 – right hand side 5 leds on , second row
offset 7, 0×00 – middle 8 LEDS all on, second row
offset 8, 0×00 – left 8 LEDS all on, second row
0xfe in an 8 LED cell, would mean the leftmost LED was ON and the others off
0xaa would be ON,OFF,ON,OFF,ON,OFF since the leds are reversed, since 1 is LED off.
a quick #define in c for the 5 LED’s
#define LED_5(a,b,c,d,e) ( (!e<<4) + (!d<<3) + (!c<<2) + (!b<<1) + (!a) )
The 8’s
#define LED_8(a,b,c,d,e,f,g,h) ( (!h<<7) + (!g<<6) + (!f<<5) + (!e<<4) + (!d<<3) + (!c<<2) + (!b<<1) + (!a) )
You can use the 8 macro for the 5 LEDs, since it ignores the top ones, so LED_8(1,1,1,1,1,0,0,0) is equivalent to LED_5(1,1,1,1,1)
One whole row, left to right would be
OutputReport[5] = LED_8(1,1,1,1 ,1,1,1,1) ;
OutputReport[4] = LED_8(1,1,1,1, 1,1,1,1) ;
OutputReport[3] = LED_8(1,1,1,1 ,1,0,0,0) ;
And some equally hacky code just to see if the above all works in principle.
#define LED_8(a,b,c,d,e,f,g,h) ( (!h<<7) + (!g<<6) + (!f<<5) + (!e<<4) + (!d<<3) + (!c<<2) + (!b<<1) + (!a) )
#define FB(x) (framebuffer[x + (y*21)]?1:0)
#define _ 0
#define W 1
unsigned char framebuffer[21 * 7] = {
W, _, W, _, W, W, _, W, _, _, W, _, _, W, W, W, _, W, _, W, _,
W, _, W, _, W, _, _, W, _, _, W, _, _, W, _, W, _, W, _, W, _,
W, _, W, _, W, _, _, W, _, _, W, _, _, W, _, W, _, W, _, W, _,
W, W, W, _, W, W, _, W, _, _, W, _, _, W, _, W, _, W, _, W, _,
W, _, W, _, W, _, _, W, _, _, W, _, _, W, _, W, _, W, _, W, _,
W, _, W, _, W, _, _, W, _, _, W, _, _, W, _, W, _, _, _, _, _,
W, _, W, _, W, W, _, W, W, _, W, W, _, W, W, W, _, W, _, W, _
};
#undef _
#undef W
void CCreamDeekyDlg::OnTimer(UINT_PTR nIDEvent)
{
int y;
CDialog::OnTimer(nIDEvent);
if( MyDeviceDetected ) {
for( y = 0 ; y < 7 ; y++ ) {
// always
OutputReport[0 ] = 0×0;
// brightness 0 .. 15
OutputReport[1 ] = 1;
OutputReport[2 ] = y;
OutputReport[5] = LED_8(FB( 0),FB( 1),FB( 2),FB( 3),FB( 4),FB( 5),FB( 6),FB( 7));
OutputReport[4] = LED_8(FB( 8),FB( 9),FB(10),FB(11),FB(12),FB(13),FB(14),FB(15));
OutputReport[3] = LED_8(FB(16),FB(17),FB(18),FB(19),FB(20),0,0,0);
y++;
OutputReport[8] = LED_8(FB( 0),FB( 1),FB( 2),FB( 3),FB( 4),FB( 5),FB( 6),FB( 7));
OutputReport[7] = LED_8(FB( 8),FB( 9),FB(10),FB(11),FB(12),FB(13),FB(14),FB(15));
OutputReport[6] = LED_8(FB(16),FB(17),FB(18),FB(19),FB(20),0,0,0);
if (WriteHandle != INVALID_HANDLE_VALUE)
{
HRESULT Result = HidD_SetOutputReport
(WriteHandle,
OutputReport,
9);
}
}
}
}
and it does , that’s brightness level 1, but its an iPhone..
Next i think i’ll see if i can implement per LED brightness, I’m hoping POV will let me write multiple times on the same row.