A Chinese Hub75 LED panel reincarnated as a fancy marquee used at DJO.
# clone the git repo
git clone https://github.com/djoamersfoort/lichtkrant
cd lichtkrant
# automatic installation for a new device
# this will create a systemctl service!
chmod +x *.sh
./setup.sh
usage: index.py [-h] [-m MODULE] [-s STATE_DIR] [-r RECURSIVE] [-d] [-o]
A driver for the DJO Lichtkrant project.
optional arguments:
-h, --help show this help message and exit
-m MODULE, --module MODULE
load a specific module by name
-s STATE_DIR, --state-dir STATE_DIR
path to the states directory
-r RECURSIVE, --recursive RECURSIVE
whether to search recursively
-d, --dry do not spew out pixel data
-o, --offline disable MQTT connectivity
Since the display makes use of the protocol Hub75, ledcat will be used. Conveniently, ledcat also has a previewing option which prints the display in your terminal, since I'm not too fond of carrying around a bulky led panel.
# use preview mode for testing purposes
./debug.sh # [extra arguments]
# driving the display using rpi-led-matrix
./run.sh # [extra arguments]
# see run.py for a 'finetuned' configuration
The system works based on state modules. These are Python files ending in .mod.py
An example state file:
import sys
from time import sleep
from states.base import BaseState
# The state class must be called 'State' and extend BaseState for things to work
class State(BaseState):
# give the state a name
# this name should be unique!
name = "example"
# the state with the highest index is shown
# if multiple states have the same index it will be randomly selected
index = 0
# delay in seconds to wait until the next update
delay = 60
# this function decides if the module will be shown
# the check function must return a boolean
# the spacestate is available as the first argument
def check(self, states):
return states['djo'] is False
# a function that is called when running the module
# in this case it just fills the screen with white
def run(self):
while not self.killed:
sys.stdout.buffer.write(bytes([0xFF] * 3072))
sleep(1)
By default, all state files are included, even in subdirectories. If you don't want this, set the flag -r, --recursive
to false.
Some states are games that can be controlled with Lichtkrant Client or Lichtkrant Client CLI.
These states use socket.io for communication.
Every game is based on 2 files, Game state and Game meta. Game State is a regular state file modified with a player class and the file location of the Game meta. Game meta is the configuration for Lichtkrant clients.
An example Game state file:
import sys
from time import sleep
from states.base import BaseState
from states.socket import BasePlayer
class Player(BasePlayer):
def __init__(self, data):
super().__init__(data)
self.movement = [0, 0]
def on_press(self, key):
if key == "w":
self.movement[1] = 1
elif key == "a":
self.movement[0] = -1
elif key == "s":
self.movement[1] = -1
elif key == "d":
self.movement[0] = 1
def on_release(self, key):
if key == "w":
self.movement[1] = 0
elif key == "a":
self.movement[0] = 0
elif key == "s":
self.movement[1] = 0
elif key == "d":
self.movement[0] = 0
def on_leave(self):
self.game.leave(self)
# The state class must be called 'State' and extend BaseState for things to work
class State(BaseState):
# give the state a name
# this name should be unique!
name = "example"
player_class = Player
game_meta = "static/game_meta/GAME_NAME.json"
players = []
# the state with the highest index is shown
# if multiple states have the same index it will be randomly selected
index = 0
# delay in seconds to wait until the next update
delay = 60
# this function decides if the module will be shown
# the check function must return a boolean
# the spacestate is available as the first argument
def check(self, states):
return len(self.players) > 0
def add_player(self, player):
self.players.append(player)
def leave(self, player):
self.players.remove(player)
# a function that is called when running the module
# in this case it just fills the screen with white
def run(self):
while not self.killed:
sys.stdout.buffer.write(bytes([0xFF] * 3072))
sleep(1)
An example Game meta file:
{
"name": "My game",
"id": "state_id",
"description": "My game for lichtkrant",
"color": {
"from": "#E0C122",
"to": "#9B8618"
},
"keys": ["w", "a", "s", "d"],
"explanation": [
{
"type": "grid",
"layout": [
[null, "W", null],
["A", null, "D"],
[null, "S", null]
],
"description": "Movement keys"
}
],
"colors": {
"visible": false,
"configurable": false
}
}
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.