-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdotnote.py
More file actions
197 lines (157 loc) · 6.55 KB
/
Copy pathdotnote.py
File metadata and controls
197 lines (157 loc) · 6.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python3
"""
DotNote - LED Matrix Message Display
A simple web-based interface to display scrolling messages on MAX7219 LED matrix displays
"""
import time
import threading
from flask import Flask, request, jsonify, render_template
from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.legacy import show_message
from luma.core.legacy.font import CP437_FONT, proportional
from gpiozero import Buzzer
# ============================================================================
# CONFIGURATION PARAMETERS - Customize these to fit your setup
# ============================================================================
# LED Matrix Configuration
CASCADED_DEVICES = 4 # Number of 8x8 LED matrices connected in series
BLOCK_ORIENTATION = -90 # Rotation of individual blocks (-90, 0, 90, 180)
ROTATE = 0 # Overall display rotation (0, 1, 2, 3)
BRIGHTNESS = 5 # LED brightness level (0-15, where 15 is brightest)
# Display Behavior
SCROLL_SPEED = 0.05 # Scroll delay in seconds (lower = faster scrolling)
DEFAULT_MESSAGE = "Welcome to DotNote! " # Message shown on startup
MESSAGE_SPACING = " " # Spaces added at the end of messages for separation
# Buzzer Configuration
BUZZER_ENABLED = True # Set to False to disable buzzer functionality
BUZZER_PIN = 23 # GPIO pin number for the buzzer
BEEP_DURATION = 0.2 # Duration of each beep in seconds
BEEP_COUNT = 2 # Number of beeps when message is updated
# Server Configuration
SERVER_HOST = '0.0.0.0' # Listen on all network interfaces
SERVER_PORT = 5000 # Port for the web server
# Logging Configuration
LOG_FILE = "messages.log" # File where messages will be logged
LOG_ENABLED = True # Set to False to disable message logging
# ============================================================================
# APPLICATION INITIALIZATION
# ============================================================================
app = Flask(__name__)
# Initialize LED matrix device
serial = spi(port=0, device=0, gpio=noop())
device = max7219(
serial,
cascaded=CASCADED_DEVICES,
block_orientation=BLOCK_ORIENTATION,
rotate=ROTATE
)
device.contrast(BRIGHTNESS)
# Initialize buzzer if enabled
buzzer = Buzzer(BUZZER_PIN) if BUZZER_ENABLED else None
# Global variable for current message and thread lock to prevent race conditions
current_message = DEFAULT_MESSAGE
message_lock = threading.Lock()
# ============================================================================
# CORE FUNCTIONS
# ============================================================================
def display_loop():
"""
Continuous loop that displays the current message on the LED matrix.
Runs in a separate thread and handles character encoding errors gracefully.
"""
global current_message
while True:
with message_lock:
msg = current_message
try:
# Display the message with scrolling effect using lower scroll_delay for faster scrolling
show_message(
device,
msg,
fill="white",
font=proportional(CP437_FONT),
scroll_delay=SCROLL_SPEED
)
except Exception as e:
print(f"Error displaying message: {e}")
# Set error message if display fails (e.g., unsupported character)
with message_lock:
current_message = "Error: Unsupported character "
time.sleep(0.1)
def beep():
"""
Emits a beep pattern using the buzzer.
Two short beeps (0.2s on and 0.2s off between them)
"""
if BUZZER_ENABLED and buzzer:
buzzer.beep(
on_time=BEEP_DURATION,
off_time=BEEP_DURATION,
n=BEEP_COUNT,
background=False
)
def log_message(message, ip):
"""
Logs the received message along with timestamp and sender's IP address.
Saves to the log file specified in configuration.
Args:
message (str): The message to log
ip (str): IP address of the sender
"""
if not LOG_ENABLED:
return
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
log_entry = f"{timestamp} - {ip} - {message.strip()}\n"
try:
with open(LOG_FILE, "a", encoding="utf-8") as log_file:
log_file.write(log_entry)
except Exception as e:
print(f"Error writing to log: {e}")
# ============================================================================
# WEB ROUTES
# ============================================================================
@app.route('/')
def index():
"""Render the main DotNote web interface"""
return render_template('index.html')
@app.route('/update', methods=['GET'])
def update_message():
"""
API endpoint to update the displayed message via URL parameter.
Usage: /update?message=Your%20Message%20Here
Returns:
JSON response with success/error status
- On success: {'message': 'Message updated'}, HTTP 200
- On error: {'error': 'No valid message provided'}, HTTP 400
Side effects:
- Updates the global current_message variable
- Logs the message with timestamp and sender IP
- Triggers buzzer beep pattern (if enabled)
"""
global current_message
new_message = request.args.get('message')
# Validate message
if not new_message or new_message.strip() == "":
return jsonify({'error': 'No valid message provided'}), 400
# Add spacing at the end for smooth scrolling separation
new_message = new_message + MESSAGE_SPACING
# Update the message thread-safely
with message_lock:
current_message = new_message
# Log the message with sender's IP
ip = request.remote_addr
log_message(new_message, ip)
# Trigger beep notification in separate thread to avoid blocking response
if BUZZER_ENABLED:
threading.Thread(target=beep, daemon=True).start()
return jsonify({'message': 'Message updated'}), 200
# ============================================================================
# MAIN EXECUTION
# ============================================================================
if __name__ == '__main__':
# Start the display loop in a daemon thread
display_thread = threading.Thread(target=display_loop, daemon=True)
display_thread.start()
# Start the Flask web server
app.run(host=SERVER_HOST, port=SERVER_PORT)