Merge branch 'master' into fixhidapi

rebase020124
Jürgen Weigert 10 months ago committed by GitHub
commit 12d86b6a7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      .github/FUNDING.yml
  2. 47
      README.md
  3. 495
      led-badge-11x44.py
  4. BIN
      photos/accentuated.gif
  5. BIN
      photos/blueBadge.jpg
  6. BIN
      photos/m2ishm.gif

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [jnweiger]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

@ -1,7 +1,11 @@
# Led-Badge-44x11
Upload tool for an led name tag with USB-HID interface
![LED Mini Board](photos/green_badge.jpg)
![LED Mini Board](photos/blueBadge.jpg)
Added Accentuated french Characters
![French LED Mini Board](photos/accentuated.gif)
## Hardware
@ -129,6 +133,10 @@ shows a bicycle crossing the display in left-to-right and right-to-left (as a se
shows a simple animation of a slowly beating heart on the first message, and a blinking heart on the second message.
./led-badge-11x44.py -B 50 -m 0 -s 8 "Bonjour à toutes et à tous" "Bienvenu(e)s en Master 2 EEA ISHM" "Ingénierie des systèmes Humains Machines" "Bonne réussite à votre promotion 2023-2024"
![M2 ishm](photos/m2ishm.gif)
python3 ./led-badge-11x44.py --list-names
prints the list of builtin icon names, including :happy: :happy2: :heart: :HEART: :heart2: :HEART2: :fablab: :bicycle: :bicycle_r: :owncloud: ::
@ -138,36 +146,27 @@ prints the list of builtin icon names, including :happy: :happy2: :heart: :HEART
prints some condensed help:
<pre>
usage: led-badge-11x44.py [-h] [-t TYPE] [-s SPEED] [-m MODE] [-b BLINK]
[-a ANTS] [-p FILE] [-l]
MESSAGE [MESSAGE ...]
usage: led-badge-11x44.py [-h] [-t TYPE] [-s SPEED] [-B BRIGHTNESS] [-m MODE] [-b BLINK] [-a ANTS] [-l] MESSAGE [MESSAGE ...]
Upload messages or graphics to a 44x11 led badge via USB HID.
Version 0.6 from https://github.com/jnweiger/led-badge-44x11
Upload messages or graphics to a 11x44 led badge via USB HID.
Version 0.12 from https://github.com/jnweiger/led-name-badge-ls32
-- see there for more examples and for updates.
positional arguments:
MESSAGE Up to 8 message texts with embedded builtin icons or
loaded images within colons(:) -- See -l for a list of
builtins
MESSAGE Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins
optional arguments:
options:
-h, --help show this help message and exit
-t TYPE, --type TYPE
Type of display: supported values are 12x48 or
(default) 11x44. Rename the program to led-badge-12x48,
to switch the default.
-t TYPE, --type TYPE Type of display: supported values are 12x48 or (default) 11x44. Rename the program to led-badge-12x48, to switch the default.
-s SPEED, --speed SPEED
Scroll speed (Range 1..8). Up to 8 comma-seperated
values
-m MODE, --mode MODE Up to 8 mode values: Scroll-left(0) -right(1) -up(2)
-down(3); still-centered(4); animation(5); drop-
down(6); curtain(7); laser(8); See '--mode-help' for
more details.
Scroll speed (Range 1..8). Up to 8 comma-separated values
-B BRIGHTNESS, --brightness BRIGHTNESS
Brightness for the display in percent: 25, 50, 75, or 100
-m MODE, --mode MODE Up to 8 mode values: Scroll-left(0) -right(1) -up(2) -down(3); still-centered(4); animation(5); drop-down(6); curtain(7);
laser(8); See '--mode-help' for more details.
-b BLINK, --blink BLINK
1: blinking, 0: normal. Up to 8 comma-seperated values
-a ANTS, --ants ANTS 1: animated border, 0: normal. Up to 8 comma-seperated
values
1: blinking, 0: normal. Up to 8 comma-separated values
-a ANTS, --ants ANTS 1: animated border, 0: normal. Up to 8 comma-separated values
-l, --list-names list named icons to be embedded in messages and exit
Example combining image and text:
@ -175,6 +174,8 @@ Example combining image and text:
</pre>
### Animations
See the gfx/starfield folder for examples. An animation of N frames is provided as an image N*48 pixels wide, for both 48 and 44 pixel wide devices.
## Related References (for USB-Serial devices)
* https://github.com/Caerbannog/led-mini-board

@ -52,39 +52,41 @@
import sys, os, re, time, argparse
from datetime import datetime
from array import array
try:
if sys.version_info[0] < 3: raise Exception("prefer usb.core with python-2.x because of https://github.com/jnweiger/led-badge-ls32/issues/9")
import pyhidapi
pyhidapi.hid_init()
have_pyhidapi = True
if sys.version_info[0] < 3: raise Exception(
"prefer usb.core with python-2.x because of https://github.com/jnweiger/led-badge-ls32/issues/9")
import pyhidapi
pyhidapi.hid_init()
have_pyhidapi = True
except:
have_pyhidapi = False
try:
import usb.core
except:
print("ERROR: Need the pyhidapi or usb.core module.")
if sys.platform == "darwin":
print("""Please try
have_pyhidapi = False
try:
import usb.core
except:
print("ERROR: Need the pyhidapi or usb.core module.")
if sys.platform == "darwin":
print("""Please try
pip3 install pyhidapi
pip install pyhidapi
brew install hidapi""")
elif sys.platform == "linux":
print("""Please try
elif sys.platform == "linux":
print("""Please try
sudo pip3 install pyhidapi
sudo pip install pyhidapi
sudo apt-get install libhidapi-hidraw0
sudo ln -s /usr/lib/x86_64-linux-gnu/libhidapi-hidraw.so.0 /usr/local/lib/
or
sudo apt-get install python3-usb""")
else: # windows?
print("""Please with Linux or MacOS or help us implement support for """ + sys.platform)
sys.exit(1)
else: # windows?
print("""Please with Linux or MacOS or help us implement support for """ + sys.platform)
sys.exit(1)
__version = "0.12"
font_11x44 = (
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00,
0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0xc6, 0x7c, 0x00,
@ -201,94 +203,129 @@ font_11x44 = (
0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x3c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x6c, 0x60,
# "àäòöùüèéêëôöûîïÿç"
0x00, 0x60, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
0x00, 0x60, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x60, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
0x00, 0x60, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
0x00, 0x18, 0x60, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
0x00, 0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00,
0x00, 0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x10, 0x6c, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
0x00, 0x10, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8,
0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x10, 0x30,
# "ÀÅÄÉÈÊËÖÔÜÛÙŸ"
0x60, 0x18, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
0x10, 0x6c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
0x6c, 0x6c, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
0x18, 0x60, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00,
0x60, 0x18, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00,
0x10, 0x6c, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00,
0x6c, 0x6c, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00,
0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ö
0x10, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ô
0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ü
0x10, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Û
0x60, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, # Ù
0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, # Ÿ
)
charmap = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + \
u'abcdefghijklmnopqrstuvwxyz' + \
u'0987654321^ !"\0$%&/()=?` °\\}][{' + \
u"@ ~ |<>,;.:-_#'+* " + \
u"äöüÄÖÜß"
u"äöüÄÖÜß" + \
u"àäòöùüèéêëôöûîïÿç" + \
u"ÀÅÄÉÈÊËÖÔÜÛÙŸ"
char_offset = {}
for i in range(len(charmap)):
char_offset[charmap[i]] = 11 * i
# print(i, charmap[i], char_offset[charmap[i]])
char_offset[charmap[i]] = 11 * i
# print(i, charmap[i], char_offset[charmap[i]])
bitmap_preloaded = [ ([],0) ]
bitmap_preloaded = [([], 0)]
bitmaps_preloaded_unused = False
bitmap_named = {
'ball': (array('B', (
0b00000000,
0b00000000,
0b00111100,
0b01111110,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00000000
)), 1, '\x1e'),
'happy': (array('B', (
0b00000000, # 0x00
0b00000000, # 0x00
0b00111100, # 0x3c
0b01000010, # 0x42
0b10100101, # 0xa5
0b10000001, # 0x81
0b10100101, # 0xa5
0b10011001, # 0x99
0b01000010, # 0x42
0b00111100, # 0x3c
0b00000000 # 0x00
)), 1, '\x1d'),
'happy2': (array('B', (0x00, 0x08, 0x14, 0x08, 0x01, 0x00, 0x00, 0x61, 0x30, 0x1c, 0x07,
'ball': (array('B', (
0b00000000,
0b00000000,
0b00111100,
0b01111110,
0b11111111,
0b11111111,
0b11111111,
0b11111111,
0b01111110,
0b00111100,
0b00000000
)), 1, '\x1e'),
'happy': (array('B', (
0b00000000, # 0x00
0b00000000, # 0x00
0b00111100, # 0x3c
0b01000010, # 0x42
0b10100101, # 0xa5
0b10000001, # 0x81
0b10100101, # 0xa5
0b10011001, # 0x99
0b01000010, # 0x42
0b00111100, # 0x3c
0b00000000 # 0x00
)), 1, '\x1d'),
'happy2': (array('B', (0x00, 0x08, 0x14, 0x08, 0x01, 0x00, 0x00, 0x61, 0x30, 0x1c, 0x07,
0x00, 0x20, 0x50, 0x20, 0x00, 0x80, 0x80, 0x86, 0x0c, 0x38, 0xe0)), 2, '\x1c'),
'heart': (array('B', (0x00, 0x00, 0x6c, 0x92, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00, 0x00)), 1, '\x1b'),
'HEART': (array('B', (0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00)), 1, '\x1a'),
'heart2': (array('B', (0x00, 0x0c, 0x12, 0x21, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
'heart': (array('B', (0x00, 0x00, 0x6c, 0x92, 0x82, 0x82, 0x44, 0x28, 0x10, 0x00, 0x00)), 1, '\x1b'),
'HEART': (array('B', (0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00)), 1, '\x1a'),
'heart2': (array('B', (0x00, 0x0c, 0x12, 0x21, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
0x00, 0x60, 0x90, 0x08, 0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00)), 2, '\x19'),
'HEART2': (array('B', (0x00, 0x0c, 0x1e, 0x3f, 0x3f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01,
'HEART2': (array('B', (0x00, 0x0c, 0x1e, 0x3f, 0x3f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01,
0x00, 0x60, 0xf0, 0xf8, 0xf8, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00)), 2, '\x18'),
'fablab': (array('B', (0x07, 0x0e, 0x1b, 0x03, 0x21, 0x2c, 0x2e, 0x26, 0x14, 0x1c, 0x06,
'fablab': (array('B', (0x07, 0x0e, 0x1b, 0x03, 0x21, 0x2c, 0x2e, 0x26, 0x14, 0x1c, 0x06,
0x80, 0x60, 0x30, 0x80, 0x88, 0x38, 0xe8, 0xc8, 0x10, 0x30, 0xc0)), 2, '\x17'),
'bicycle': (array('B', (0x01, 0x02, 0x00, 0x01, 0x07, 0x09, 0x12, 0x12, 0x10, 0x08, 0x07,
0x00, 0x87, 0x81, 0x5f, 0x22, 0x94, 0x49, 0x5f, 0x49, 0x80, 0x00,
0x00, 0x80, 0x00, 0x80, 0x70, 0xc8, 0x24, 0xe4, 0x04, 0x88, 0x70)), 3, '\x16'),
'bicycle_r':(array('B', (0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x12, 0x13, 0x10, 0x08, 0x07,
0x00, 0xf0, 0x40, 0xfd, 0x22, 0x94, 0x49, 0xfd, 0x49, 0x80, 0x00,
0x40, 0xa0, 0x80, 0x40, 0x70, 0xc8, 0x24, 0x24, 0x04, 0x88, 0x70)), 3, '\x15'),
'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f,
0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd,
0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'),
'bicycle': (array('B', (0x01, 0x02, 0x00, 0x01, 0x07, 0x09, 0x12, 0x12, 0x10, 0x08, 0x07,
0x00, 0x87, 0x81, 0x5f, 0x22, 0x94, 0x49, 0x5f, 0x49, 0x80, 0x00,
0x00, 0x80, 0x00, 0x80, 0x70, 0xc8, 0x24, 0xe4, 0x04, 0x88, 0x70)), 3, '\x16'),
'bicycle_r': (array('B', (0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x12, 0x13, 0x10, 0x08, 0x07,
0x00, 0xf0, 0x40, 0xfd, 0x22, 0x94, 0x49, 0xfd, 0x49, 0x80, 0x00,
0x40, 0xa0, 0x80, 0x40, 0x70, 0xc8, 0x24, 0x24, 0x04, 0x88, 0x70)), 3, '\x15'),
'owncloud': (array('B', (0x00, 0x01, 0x02, 0x03, 0x06, 0x0c, 0x1a, 0x13, 0x11, 0x19, 0x0f,
0x78, 0xcc, 0x87, 0xfc, 0x42, 0x81, 0x81, 0x81, 0x81, 0x43, 0xbd,
0x00, 0x00, 0x00, 0x80, 0x80, 0xe0, 0x30, 0x10, 0x28, 0x28, 0xd0)), 3, '\x14'),
}
bitmap_builtin = {}
for i in bitmap_named:
bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i]
bitmap_builtin[bitmap_named[i][2]] = bitmap_named[i]
def bitmap_char(ch):
""" Returns a tuple of 11 bytes,
""" Returns a tuple of 11 bytes,
ch = '_' returns (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255)
The bits in each byte are horizontal, highest bit is left.
"""
if ord(ch) < 32:
if ch in bitmap_builtin:
return bitmap_builtin[ch][:2]
if ord(ch) < 32:
if ch in bitmap_builtin:
return bitmap_builtin[ch][:2]
global bitmaps_preloaded_unused
bitmaps_preloaded_unused = False
return bitmap_preloaded[ord(ch)]
global bitmaps_preloaded_unused
bitmaps_preloaded_unused = False
return bitmap_preloaded[ord(ch)]
o = char_offset[ch]
return (font_11x44[o:o+11],1)
o = char_offset[ch]
return (font_11x44[o:o + 11], 1)
def bitmap_text(text):
""" returns a tuple of (buffer, length_in_byte_columns_aka_chars)
""" returns a tuple of (buffer, length_in_byte_columns_aka_chars)
We preprocess the text string for substitution patterns
"::" is replaced with a single ":"
":1: is replaced with CTRL-A referencing the first preloaded or loaded image.
@ -296,138 +333,146 @@ def bitmap_text(text):
":heart:" is replaced with a reference to a builtin heart glyph
":gfx/logo.png:" preloads the file gfx/logo.png and is replaced the corresponding control char.
"""
def colonrepl(m):
name = m.group(1)
if name == '':
return ':'
if re.match('^[0-9]*$', name): # py3 name.isdecimal()
return chr(int(name))
if '.' in name:
bitmap_preloaded.append(bitmap_img(name))
return chr(len(bitmap_preloaded)-1)
b = bitmap_named[name]
return b[2]
text = re.sub(r':([^:]*):', colonrepl, text)
buf = array('B')
cols = 0
for c in text:
(b,n) = bitmap_char(c)
buf.extend(b)
cols += n
return (buf, cols)
def colonrepl(m):
name = m.group(1)
if name == '':
return ':'
if re.match('^[0-9]*$', name): # py3 name.isdecimal()
return chr(int(name))
if '.' in name:
bitmap_preloaded.append(bitmap_img(name))
return chr(len(bitmap_preloaded) - 1)
b = bitmap_named[name]
return b[2]
text = re.sub(r':([^:]*):', colonrepl, text)
buf = array('B')
cols = 0
for c in text:
(b, n) = bitmap_char(c)
buf.extend(b)
cols += n
return (buf, cols)
def bitmap_img(file):
""" returns a tuple of (buffer, length_in_byte_columns)
""" returns a tuple of (buffer, length_in_byte_columns)
"""
from PIL import Image
im = Image.open(file)
print("fetching bitmap from file %s -> (%d x %d)" % (file, im.width, im.height))
if im.height != 11:
sys.exit("%s: image height must be 11px. Seen %d" % (file, im.height))
buf = array('B')
cols = int((im.width+7)/8)
for col in range(cols):
for row in range(11): # [0..10]
byte_val = 0
for bit in range(8): # [0..7]
bit_val = 0
x = 8*col+bit
if x < im.width and row < im.height:
pixel_color = im.getpixel( (x, row) )
if isinstance(pixel_color, tuple):
monochrome_color = sum(pixel_color[:3]) / len(pixel_color[:3])
elif isinstance(pixel_color, int):
monochrome_color = pixel_color
else:
sys.exit("%s: Unknown pixel format detected (%s)!" % (file, pixel_color))
if monochrome_color > 127:
bit_val = 1 << (7-bit)
byte_val += bit_val
buf.append(byte_val)
im.close()
return (buf, cols)
from PIL import Image
im = Image.open(file)
print("fetching bitmap from file %s -> (%d x %d)" % (file, im.width, im.height))
if im.height != 11:
sys.exit("%s: image height must be 11px. Seen %d" % (file, im.height))
buf = array('B')
cols = int((im.width + 7) / 8)
for col in range(cols):
for row in range(11): # [0..10]
byte_val = 0
for bit in range(8): # [0..7]
bit_val = 0
x = 8 * col + bit
if x < im.width and row < im.height:
pixel_color = im.getpixel((x, row))
if isinstance(pixel_color, tuple):
monochrome_color = sum(pixel_color[:3]) / len(pixel_color[:3])
elif isinstance(pixel_color, int):
monochrome_color = pixel_color
else:
sys.exit("%s: Unknown pixel format detected (%s)!" % (file, pixel_color))
if monochrome_color > 127:
bit_val = 1 << (7 - bit)
byte_val += bit_val
buf.append(byte_val)
im.close()
return (buf, cols)
def bitmap(arg):
""" if arg is a valid and existing path name, we load it as an image.
""" if arg is a valid and existing path name, we load it as an image.
Otherwise we take it as a string.
"""
if os.path.exists(arg):
return bitmap_img(arg)
return bitmap_text(arg)
if os.path.exists(arg):
return bitmap_img(arg)
return bitmap_text(arg)
proto_header = (
0x77, 0x61, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x77, 0x61, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
)
def header(lengths, speeds, modes, blink, ants, brightness=100):
""" lengths[0] is the number of chars of the first text
""" lengths[0] is the number of chars of the first text
Speeds come in as 1..8, but are needed 0..7 here.
"""
a = [int(x) for x in re.split(r'[\s,]+', ants)]
a = a + [a[-1]]*(8-len(a)) # repeat last element
b = [int(x) for x in re.split(r'[\s,]+', blink)]
b = b + [b[-1]]*(8-len(b)) # repeat last element
a = [int(x) for x in re.split(r'[\s,]+', ants)]
a = a + [a[-1]] * (8 - len(a)) # repeat last element
s = [int(x)-1 for x in re.split(r'[\s,]+', speeds)]
s = s + [s[-1]]*(8-len(s)) # repeat last element
b = [int(x) for x in re.split(r'[\s,]+', blink)]
b = b + [b[-1]] * (8 - len(b)) # repeat last element
m = [int(x) for x in re.split(r'[\s,]+', modes)]
m = m + [m[-1]]*(8-len(m)) # repeat last element
s = [int(x) - 1 for x in re.split(r'[\s,]+', speeds)]
s = s + [s[-1]] * (8 - len(s)) # repeat last element
h = list(proto_header)
m = [int(x) for x in re.split(r'[\s,]+', modes)]
m = m + [m[-1]] * (8 - len(m)) # repeat last element
if brightness <= 25:
h[5] = 0x40
elif brightness <= 50:
h[5] = 0x20
elif brightness <= 75:
h[5] = 0x10
h = list(proto_header)
for i in range(8):
h[6] += b[i]<<i
h[7] += a[i]<<i
if brightness <= 25:
h[5] = 0x40
elif brightness <= 50:
h[5] = 0x20
elif brightness <= 75:
h[5] = 0x10
for i in range(8):
h[8+i] = 16*s[i] + m[i]
for i in range(8):
h[6] += b[i] << i
h[7] += a[i] << i
for i in range(len(lengths)):
h[17+(2*i)-1] = lengths[i] // 256
h[17+(2*i)] = lengths[i] % 256
for i in range(8):
h[8 + i] = 16 * s[i] + m[i]
for i in range(len(lengths)):
h[17 + (2 * i) - 1] = lengths[i] // 256
h[17 + (2 * i)] = lengths[i] % 256
cdate = datetime.now()
h[38+0] = cdate.year % 100
h[38+1] = cdate.month
h[38+2] = cdate.day
h[38+3] = cdate.hour
h[38+4] = cdate.minute
h[38+5] = cdate.second
cdate = datetime.now()
h[38 + 0] = cdate.year % 100
h[38 + 1] = cdate.month
h[38 + 2] = cdate.day
h[38 + 3] = cdate.hour
h[38 + 4] = cdate.minute
h[38 + 5] = cdate.second
return h
return h
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description='Upload messages or graphics to a 11x44 led badge via USB HID.\nVersion %s from https://github.com/jnweiger/led-badge-ls32\n -- see there for more examples and for updates.' % __version, epilog='Example combining image and text:\n sudo %s "I:HEART2:you"' % sys.argv[0])
parser.add_argument('-t', '--type', default='11x44', help="Type of display: supported values are 12x48 or (default) 11x44. Rename the program to led-badge-12x48, to switch the default.")
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description='Upload messages or graphics to a 11x44 led badge via USB HID.\nVersion %s from https://github.com/jnweiger/led-badge-ls32\n -- see there for more examples and for updates.' % __version,
epilog='Example combining image and text:\n sudo %s "I:HEART2:you"' % sys.argv[0])
parser.add_argument('-t', '--type', default='11x44',
help="Type of display: supported values are 12x48 or (default) 11x44. Rename the program to led-badge-12x48, to switch the default.")
parser.add_argument('-H', '--hid', default='0', help="Set to 1 to ensure connect via HID API, program will then not fallback to usb.core library")
parser.add_argument('-s', '--speed', default='4', help="Scroll speed (Range 1..8). Up to 8 comma-separated values")
parser.add_argument('-B', '--brightness', default='100', help="Brightness for the display in percent: 25, 50, 75, or 100")
parser.add_argument('-m', '--mode', default='0', help="Up to 8 mode values: Scroll-left(0) -right(1) -up(2) -down(3); still-centered(4); animation(5); drop-down(6); curtain(7); laser(8); See '--mode-help' for more details.")
parser.add_argument('-B', '--brightness', default='100',
help="Brightness for the display in percent: 25, 50, 75, or 100")
parser.add_argument('-m', '--mode', default='0',
help="Up to 8 mode values: Scroll-left(0) -right(1) -up(2) -down(3); still-centered(4); animation(5); drop-down(6); curtain(7); laser(8); See '--mode-help' for more details.")
parser.add_argument('-b', '--blink', default='0', help="1: blinking, 0: normal. Up to 8 comma-separated values")
parser.add_argument('-a', '--ants', default='0', help="1: animated border, 0: normal. Up to 8 comma-separated values")
parser.add_argument('-p', '--preload', metavar='FILE', action='append', help=argparse.SUPPRESS) # "Load bitmap images. Use ^A, ^B, ^C, ... in text messages to make them visible. Deprecated, embed within ':' instead")
parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit", version=':'+': :'.join(bitmap_named.keys())+': :: or e.g. :path/to/some_icon.png:')
parser.add_argument('message', metavar='MESSAGE', nargs='+', help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins")
parser.add_argument('-a', '--ants', default='0', help="1: animated border, 0: normal. Up to 8 comma-separated values")
parser.add_argument('-p', '--preload', metavar='FILE', action='append',
help=argparse.SUPPRESS) # "Load bitmap images. Use ^A, ^B, ^C, ... in text messages to make them visible. Deprecated, embed within ':' instead")
parser.add_argument('-l', '--list-names', action='version', help="list named icons to be embedded in messages and exit",
version=':' + ': :'.join(bitmap_named.keys()) + ': :: or e.g. :path/to/some_icon.png:')
parser.add_argument('message', metavar='MESSAGE', nargs='+',
help="Up to 8 message texts with embedded builtin icons or loaded images within colons(:) -- See -l for a list of builtins")
parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, version="""
-m 5 "Animation"
@ -451,84 +496,86 @@ parser.add_argument('--mode-help', action='version', help=argparse.SUPPRESS, ver
""" % sys.argv[0])
args = parser.parse_args()
if have_pyhidapi:
devinfo = pyhidapi.hid_enumerate(0x0416, 0x5020)
#dev = pyhidapi.hid_open(0x0416, 0x5020)
devinfo = pyhidapi.hid_enumerate(0x0416, 0x5020)
# dev = pyhidapi.hid_open(0x0416, 0x5020)
else:
if args.hid != "0":
sys.exit("HID API access is needed but not initialized. Fix your setup")
dev = usb.core.find(idVendor=0x0416, idProduct=0x5020)
if args.hid != "0":
sys.exit("HID API access is needed but not initialized. Fix your setup")
dev = usb.core.find(idVendor=0x0416, idProduct=0x5020)
if have_pyhidapi:
if devinfo:
dev = pyhidapi.hid_open_path(devinfo[0].path)
print("using [%s %s] int=%d page=%s via pyHIDAPI" % (devinfo[0].manufacturer_string, devinfo[0].product_string, devinfo[0].interface_number, devinfo[0].usage_page))
else:
print("No led tag with vendorID 0x0416 and productID 0x5020 found.")
print("Connect the led tag and run this tool as root.")
sys.exit(1)
if devinfo:
dev = pyhidapi.hid_open_path(devinfo[0].path)
print("using [%s %s] int=%d page=%s via pyHIDAPI" % (
devinfo[0].manufacturer_string, devinfo[0].product_string, devinfo[0].interface_number, devinfo[0].usage_page))
else:
print("No led tag with vendorID 0x0416 and productID 0x5020 found.")
print("Connect the led tag and run this tool as root.")
sys.exit(1)
else:
if dev is None:
print("No led tag with vendorID 0x0416 and productID 0x5020 found.")
print("Connect the led tag and run this tool as root.")
sys.exit(1)
try:
# win32: NotImplementedError: is_kernel_driver_active
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
except:
pass
dev.set_configuration()
print("using [%s %s] bus=%d dev=%d" % (dev.manufacturer, dev.product, dev.bus, dev.address))
if dev is None:
print("No led tag with vendorID 0x0416 and productID 0x5020 found.")
print("Connect the led tag and run this tool as root.")
sys.exit(1)
try:
# win32: NotImplementedError: is_kernel_driver_active
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
except:
pass
dev.set_configuration()
print("using [%s %s] bus=%d dev=%d" % (dev.manufacturer, dev.product, dev.bus, dev.address))
if args.preload:
for file in args.preload:
bitmap_preloaded.append(bitmap_img(file))
bitmaps_preloaded_unused = True
for file in args.preload:
bitmap_preloaded.append(bitmap_img(file))
bitmaps_preloaded_unused = True
msgs = []
for arg in args.message:
msgs.append(bitmap(arg))
msgs.append(bitmap(arg))
if bitmaps_preloaded_unused == True:
print("\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n")
print(
"\nWARNING:\n Your preloaded images are not used.\n Try without '-p' or embed the control character '^A' in your message.\n")
if '12' in args.type or '12' in sys.argv[0]:
print("Type: 12x48")
for msg in msgs:
# trivial hack to support 12x48 badges:
# patch extra empty lines into the message stream.
for o in reversed(range(1, int(len(msg[0])/11)+1)):
msg[0][o*11:o*11] = array('B', [0])
print("Type: 12x48")
for msg in msgs:
# trivial hack to support 12x48 badges:
# patch extra empty lines into the message stream.
for o in reversed(range(1, int(len(msg[0]) / 11) + 1)):
msg[0][o * 11:o * 11] = array('B', [0])
else:
print("Type: 11x44")
print("Type: 11x44")
buf = array('B')
buf.extend(header(list(map(lambda x: x[1], msgs)), args.speed, args.mode, args.blink, args.ants, int(args.brightness)))
for msg in msgs:
buf.extend(msg[0])
buf.extend(msg[0])
needpadding = len(buf)%64
needpadding = len(buf) % 64
if needpadding:
buf.extend( (0,) * (64-needpadding) )
buf.extend((0,) * (64 - needpadding))
# print(buf) # array('B', [119, 97, 110, 103, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 126, 255, 255, 255, 255, 126, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
if len(buf) > 8192:
print ("Writing more than 8192 bytes damages the display!")
sys.exit(1)
print("Writing more than 8192 bytes damages the display!")
sys.exit(1)
if have_pyhidapi:
for i in range(int(len(buf)/64)):
# sendbuf must contain "report ID" as first byte. "0" does the job here.
sendbuf=array('B',[0])
# Then, put the 64 payload bytes into the buffer
sendbuf.extend(buf[i*64:i*64+64])
pyhidapi.hid_write(dev,sendbuf)
for i in range(int(len(buf)/64)):
# sendbuf must contain "report ID" as first byte. "0" does the job here.
sendbuf=array('B',[0])
# Then, put the 64 payload bytes into the buffer
sendbuf.extend(buf[i*64:i*64+64])
pyhidapi.hid_write(dev,sendbuf)
else:
for i in range(int(len(buf)/64)):
time.sleep(0.1)
dev.write(1, buf[i*64:i*64+64])
for i in range(int(len(buf) / 64)):
time.sleep(0.1)
dev.write(1, buf[i * 64:i * 64 + 64])
if have_pyhidapi:
pyhidapi.hid_close(dev)
pyhidapi.hid_close(dev)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Loading…
Cancel
Save