Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a21816e42 | ||
|
|
197c3c20e2 | ||
|
|
91d4f59a9f | ||
|
|
b4cfbbe60b | ||
|
|
bda937a889 | ||
|
|
6bf9ede793 | ||
|
|
f3c2529394 | ||
|
|
ef12927410 | ||
|
|
89677ac283 | ||
|
|
1819bbfa2e | ||
|
|
ebb25aee9c | ||
|
|
c27e3d661d | ||
| ce0639863c | |||
| bddc6c99fc | |||
| d79fc79519 | |||
| 2aaffd8ba1 | |||
| 54a02ad149 | |||
| 208643a286 | |||
| f258fced41 | |||
|
|
b7cd8d18e8 | ||
|
|
528ca5da5b | ||
|
|
cf9a3d7c93 | ||
|
|
4e4adeb581 | ||
|
|
7fa219a7f3 | ||
|
|
20d65d5b56 | ||
|
|
ed9270663d | ||
|
|
245eb45ff1 | ||
| 2a30a98144 | |||
| f5c2200e79 | |||
| 9f03a3c477 | |||
|
|
785a4212f5 | ||
|
|
b2e0851c77 | ||
| 359eb64f30 | |||
|
|
703a3e0c38 | ||
| 2aad32f6f3 | |||
|
|
8f601ec030 | ||
|
|
8b2a97b364 | ||
| a7cf62b9eb |
@ -1,2 +1,5 @@
|
|||||||
# pupilometer
|
### Pupilometer
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This repository houses programs and documents related to Pupilometer project by Vietnam Academy of Science and Technology. The project aims to introduce a benchmark and researches into the interaction between light intensity and temperature to the eye strain disorder.
|
||||||
|
|||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
bleak>="1.0.0"
|
||||||
|
flask>="3.1.1"
|
||||||
298
src/controllerSoftware/app.py
Normal file
298
src/controllerSoftware/app.py
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
from flask import Flask, render_template, request, jsonify
|
||||||
|
import asyncio
|
||||||
|
from bleak import BleakScanner, BleakClient
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import os
|
||||||
|
|
||||||
|
# =================================================================================================
|
||||||
|
# APP CONFIGURATION
|
||||||
|
# =================================================================================================
|
||||||
|
|
||||||
|
# Set to True to run without a physical BLE device for testing purposes.
|
||||||
|
# Set to False to connect to the actual lamp matrix.
|
||||||
|
DEBUG_MODE = False
|
||||||
|
|
||||||
|
# --- BLE Device Configuration (Ignored in DEBUG_MODE) ---
|
||||||
|
DEVICE_NAME = "Pupilometer LED Billboard"
|
||||||
|
global ble_client
|
||||||
|
global ble_characteristics
|
||||||
|
ble_client = None
|
||||||
|
ble_characteristics = None
|
||||||
|
ble_event_loop = None # Will be initialized if not in debug mode
|
||||||
|
|
||||||
|
# =================================================================================================
|
||||||
|
# BLE HELPER FUNCTIONS (Used in LIVE mode)
|
||||||
|
# =================================================================================================
|
||||||
|
|
||||||
|
lampAmount = 25
|
||||||
|
|
||||||
|
def create_spiral_map(n=5):
|
||||||
|
if n % 2 == 0:
|
||||||
|
raise ValueError("Matrix size must be odd for a unique center point.")
|
||||||
|
spiral_map = [[0] * n for _ in range(n)]
|
||||||
|
r, c = n // 2, n // 2
|
||||||
|
address = 0
|
||||||
|
spiral_map[r][c] = address
|
||||||
|
|
||||||
|
# Updated directions to start moving UP first instead of right
|
||||||
|
dr = [-1, 0, 1, 0] # Change in row: Up, Right, Down, Left
|
||||||
|
dc = [0, 1, 0, -1] # Change in col: Up, Right, Down, Left
|
||||||
|
|
||||||
|
direction = 0
|
||||||
|
segment_length = 1
|
||||||
|
steps = 0
|
||||||
|
while address < n * n - 1:
|
||||||
|
for _ in range(segment_length):
|
||||||
|
address += 1
|
||||||
|
r += dr[direction]
|
||||||
|
c += dc[direction]
|
||||||
|
if 0 <= r < n and 0 <= c < n:
|
||||||
|
spiral_map[r][c] = address
|
||||||
|
direction = (direction + 1) % 4
|
||||||
|
steps += 1
|
||||||
|
if steps % 2 == 0:
|
||||||
|
segment_length += 1
|
||||||
|
return spiral_map
|
||||||
|
|
||||||
|
|
||||||
|
def get_spiral_address(row, col, spiral_map):
|
||||||
|
n = len(spiral_map)
|
||||||
|
if 0 <= row < n and 0 <= col < n:
|
||||||
|
return spiral_map[row][col]
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
SPIRAL_MAP_5x5 = create_spiral_map(5)
|
||||||
|
|
||||||
|
async def set_full_matrix_on_ble(colorSeries):
|
||||||
|
global ble_client
|
||||||
|
global ble_characteristics
|
||||||
|
|
||||||
|
if not ble_client or not ble_client.is_connected:
|
||||||
|
print("BLE client not connected. Attempting to reconnect...")
|
||||||
|
await connect_to_ble_device()
|
||||||
|
if not ble_client or not ble_client.is_connected:
|
||||||
|
print("Failed to reconnect to BLE client.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print("Confirmed BLE connection status. Proceeding with lamp update.")
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# SNIPPET TO PATCH SWAPPED LAMP POSITIONS
|
||||||
|
# =====================================================================
|
||||||
|
#print("Patching lamp positions 3 <-> 7 and 12 <-> 24.")
|
||||||
|
|
||||||
|
# Swap data for lamps at positions 3 and 7
|
||||||
|
#temp_color_3 = colorSeries[3]
|
||||||
|
#colorSeries[3] = colorSeries[7]
|
||||||
|
#colorSeries[7] = temp_color_3
|
||||||
|
|
||||||
|
# Swap data for lamps at positions 12 and 24
|
||||||
|
#temp_color_12 = colorSeries[12]
|
||||||
|
#colorSeries[12] = colorSeries[24]
|
||||||
|
#colorSeries[24] = temp_color_12
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
if DEBUG_MODE:
|
||||||
|
# Ensure all characteristics are available before writing
|
||||||
|
print(f"Confirmed DEBUG set to true.")
|
||||||
|
if len(ble_characteristics) != lampAmount:
|
||||||
|
print(f"Mismatch in lamp amount. Expected {lampAmount}, got {len(ble_characteristics)}.")
|
||||||
|
return
|
||||||
|
print(f"Constructed the following matrix data: {colorSeries}")
|
||||||
|
# Write each byte string to its corresponding characteristic
|
||||||
|
for i, char in enumerate(ble_characteristics):
|
||||||
|
value_to_write = colorSeries[i]
|
||||||
|
print(f"Setting Lamp {i} ({char.uuid}) to {value_to_write.hex()}")
|
||||||
|
await ble_client.write_gatt_char(char.uuid, value_to_write)
|
||||||
|
else:
|
||||||
|
print(f"Confirmed DEBUG set to false.")
|
||||||
|
value_to_write = b"".join([color for color in colorSeries])
|
||||||
|
print(value_to_write)
|
||||||
|
print(f"Setting lamps to {value_to_write.hex()}")
|
||||||
|
await ble_client.write_gatt_char(ble_characteristics[0].uuid, value_to_write)
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_to_ble_device():
|
||||||
|
global ble_client
|
||||||
|
global ble_characteristics
|
||||||
|
|
||||||
|
print(f"Scanning for device: {DEVICE_NAME}...")
|
||||||
|
devices = await BleakScanner.discover()
|
||||||
|
target_device = next((d for d in devices if d.name == DEVICE_NAME), None)
|
||||||
|
|
||||||
|
if not target_device:
|
||||||
|
print(f"Device '{DEVICE_NAME}' not found.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Found device: {target_device.name} ({target_device.address})")
|
||||||
|
|
||||||
|
try:
|
||||||
|
ble_client = BleakClient(target_device.address)
|
||||||
|
await ble_client.connect()
|
||||||
|
if ble_client.is_connected:
|
||||||
|
print(f"Connected to {target_device.name}")
|
||||||
|
services = [service for service in ble_client.services if service.handle != 1]
|
||||||
|
|
||||||
|
# The previous logic for filtering services seems incorrect; let's grab all characteristics
|
||||||
|
characteristics = [
|
||||||
|
char for service in services for char in service.characteristics
|
||||||
|
]
|
||||||
|
ble_characteristics = sorted(characteristics, key=lambda char: char.handle)
|
||||||
|
print(f"Found {len(ble_characteristics)} characteristics for lamps.")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Failed to connect to {target_device.name}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred during BLE connection: {e}")
|
||||||
|
return False
|
||||||
|
# =================================================================================================
|
||||||
|
# COLOR MIXING
|
||||||
|
# =================================================================================================
|
||||||
|
|
||||||
|
def calculate_rgb(ww, cw, blue):
|
||||||
|
"""
|
||||||
|
Calculates the combined RGB color from warm white, cool white, and blue light values.
|
||||||
|
This function is a Python equivalent of the JavaScript color mixer in index.html.
|
||||||
|
"""
|
||||||
|
# Define the RGB components for each light source based on slider track colors
|
||||||
|
warm_white_r, warm_white_g, warm_white_b = 255, 192, 128
|
||||||
|
cool_white_r, cool_white_g, cool_white_b = 192, 224, 255
|
||||||
|
blue_r, blue_g, blue_b = 0, 0, 255
|
||||||
|
|
||||||
|
# Normalize the slider values (0-255) and apply them to the base colors
|
||||||
|
r = (ww / 255) * warm_white_r + (cw / 255) * cool_white_r + (blue / 255) * blue_r
|
||||||
|
g = (ww / 255) * warm_white_g + (cw / 255) * cool_white_g + (blue / 255) * blue_g
|
||||||
|
b = (ww / 255) * warm_white_b + (cw / 255) * cool_white_b + (blue / 255) * blue_b
|
||||||
|
|
||||||
|
# Clamp the values to 255 and convert to integer
|
||||||
|
r = int(min(255, round(r)))
|
||||||
|
g = int(min(255, round(g)))
|
||||||
|
b = int(min(255, round(b)))
|
||||||
|
|
||||||
|
return r, g, b
|
||||||
|
|
||||||
|
def rgb_to_hex(r, g, b):
|
||||||
|
"""
|
||||||
|
Converts RGB color values to a hex color string.
|
||||||
|
"""
|
||||||
|
# Ensure values are within the valid range (0-255) and are integers
|
||||||
|
r = int(max(0, min(255, r)))
|
||||||
|
g = int(max(0, min(255, g)))
|
||||||
|
b = int(max(0, min(255, b)))
|
||||||
|
|
||||||
|
# Convert each component to a two-digit hexadecimal string
|
||||||
|
return f'#{r:02x}{g:02x}{b:02x}'
|
||||||
|
|
||||||
|
# =================================================================================================
|
||||||
|
# FLASK APPLICATION
|
||||||
|
# =================================================================================================
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# In-memory matrix for DEBUG_MODE
|
||||||
|
lamp_matrix = [['#000000' for _ in range(5)] for _ in range(5)]
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
print(f"Getting current lamp matrix info: {lamp_matrix}")
|
||||||
|
if DEBUG_MODE:
|
||||||
|
return render_template('index.html', matrix=lamp_matrix)
|
||||||
|
else:
|
||||||
|
# In live mode, we'll pass a default black matrix.
|
||||||
|
initial_matrix = [['#000000' for _ in range(5)] for _ in range(5)]
|
||||||
|
return render_template('index.html', matrix=initial_matrix)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/set_matrix', methods=['POST'])
|
||||||
|
def set_matrix():
|
||||||
|
data = request.get_json()
|
||||||
|
full_matrix = data.get('matrix', [])
|
||||||
|
matrixDataSerialized = []
|
||||||
|
if not full_matrix or len(full_matrix) != 5 or len(full_matrix[0]) != 5:
|
||||||
|
return jsonify(success=False, message="Invalid matrix data received"), 400
|
||||||
|
else:
|
||||||
|
print(f"Received the following matrix data: {full_matrix}")
|
||||||
|
|
||||||
|
#Creating empty byte array
|
||||||
|
serial_colors = [b'\x00\x00\x00'] * lampAmount
|
||||||
|
|
||||||
|
try:
|
||||||
|
for row in range(5):
|
||||||
|
for col in range(5):
|
||||||
|
lamp_data = full_matrix[row][col]
|
||||||
|
ww = int(lamp_data['ww'])
|
||||||
|
cw = int(lamp_data['cw'])
|
||||||
|
blue = int(lamp_data['blue'])
|
||||||
|
|
||||||
|
#Preparing byte data for control command
|
||||||
|
color_bytes = bytes([ww, cw, blue])
|
||||||
|
spiral_pos = get_spiral_address(row, col, SPIRAL_MAP_5x5)
|
||||||
|
print(f"Constructed data for {spiral_pos}: {color_bytes}")
|
||||||
|
if spiral_pos != -1:
|
||||||
|
serial_colors[spiral_pos] = color_bytes
|
||||||
|
#Preparing hex color data for frontend
|
||||||
|
lampColorR, lampColorG, lampColorB = calculate_rgb(ww,cw,blue)
|
||||||
|
lamp_matrix[row][col] = rgb_to_hex(lampColorR, lampColorG, lampColorB)
|
||||||
|
if DEBUG_MODE:
|
||||||
|
# === DEBUG MODE: Update in-memory matrix ===
|
||||||
|
return jsonify(success=True)
|
||||||
|
else:
|
||||||
|
# === LIVE MODE: Communicate with the BLE device ===
|
||||||
|
asyncio.run_coroutine_threadsafe(
|
||||||
|
set_full_matrix_on_ble(serial_colors),
|
||||||
|
ble_event_loop
|
||||||
|
)
|
||||||
|
return jsonify(success=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error in set_matrix route: {e}")
|
||||||
|
return jsonify(success=False, message=str(e)), 500
|
||||||
|
|
||||||
|
print(f"Getting current lamp matrix info: {lamp_matrix}")
|
||||||
|
|
||||||
|
# =================================================================================================
|
||||||
|
# APP STARTUP
|
||||||
|
# =================================================================================================
|
||||||
|
|
||||||
|
def signal_handler(signum, frame):
|
||||||
|
print("Received shutdown signal, gracefully shutting down...")
|
||||||
|
if not DEBUG_MODE and ble_client and ble_client.is_connected:
|
||||||
|
print("Disconnecting BLE client...")
|
||||||
|
disconnect_future = asyncio.run_coroutine_threadsafe(ble_client.disconnect(), ble_event_loop)
|
||||||
|
try:
|
||||||
|
# Wait for the disconnect to complete with a timeout
|
||||||
|
disconnect_future.result(timeout=5)
|
||||||
|
print("BLE client disconnected successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during BLE disconnect: {e}")
|
||||||
|
|
||||||
|
if not DEBUG_MODE and ble_event_loop and ble_event_loop.is_running():
|
||||||
|
print("Stopping BLE event loop...")
|
||||||
|
# Schedule a stop and wait for the thread to finish
|
||||||
|
ble_event_loop.call_soon_threadsafe(ble_event_loop.stop)
|
||||||
|
ble_thread.join(timeout=1)
|
||||||
|
print("BLE event loop stopped.")
|
||||||
|
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Register the signal handler before running the app
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
|
||||||
|
if not DEBUG_MODE:
|
||||||
|
print("Starting BLE event loop in background thread...")
|
||||||
|
ble_event_loop = asyncio.new_event_loop()
|
||||||
|
ble_thread = threading.Thread(target=ble_event_loop.run_forever, daemon=True)
|
||||||
|
ble_thread.start()
|
||||||
|
|
||||||
|
# Connect to the device as soon as the app starts
|
||||||
|
future = asyncio.run_coroutine_threadsafe(connect_to_ble_device(), ble_event_loop)
|
||||||
|
future.result(timeout=10) # Wait up to 10 seconds for connection
|
||||||
|
|
||||||
|
app.run(debug=True, use_reloader=False, host="0.0.0.0")
|
||||||
151
src/controllerSoftware/static/style.css
Normal file
151
src/controllerSoftware/static/style.css
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
:root {
|
||||||
|
--matrix-width: calc(5 * 70px + 4 * 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.main-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
.matrix-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 70px);
|
||||||
|
grid-template-rows: repeat(5, 70px);
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.lamp {
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 10%;
|
||||||
|
background-color: #000;
|
||||||
|
transition: box-shadow 0.2s, transform 0.1s;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
.lamp.on {
|
||||||
|
box-shadow: 0 0 15px currentColor, 0 0 25px currentColor;
|
||||||
|
}
|
||||||
|
.lamp.selected {
|
||||||
|
border: 2px solid #fff;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.region-control {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.region-control select {
|
||||||
|
padding: 10px 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.control-panel, .center-lamp-control {
|
||||||
|
background-color: #444;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: var(--matrix-width); /* Fixed width for consistency */
|
||||||
|
max-width: var(--matrix-width);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.control-panel.inactive-control {
|
||||||
|
background-color: #333;
|
||||||
|
filter: saturate(0.2);
|
||||||
|
}
|
||||||
|
.control-panel.inactive-control .slider-row {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.control-panel h2, .center-lamp-control h2 {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.slider-group {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.slider-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr 50px;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.slider-group input[type="range"] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 5px;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.slider-group input[type="number"] {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.slider-group input[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0 5px rgba(0,0,0,0.5);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
.slider-group input[type="range"]::-webkit-slider-runnable-track {
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
input.white-3000k::-webkit-slider-runnable-track { background: linear-gradient(to right, #000, #ffc080); }
|
||||||
|
input.white-6500k::-webkit-slider-runnable-track { background: linear-gradient(to right, #000, #c0e0ff); }
|
||||||
|
input.blue::-webkit-slider-runnable-track { background: linear-gradient(to right, #000, #00f); }
|
||||||
|
.slider-label {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.inactive-control .slider-label {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.main-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
342
src/controllerSoftware/templates/index.html
Normal file
342
src/controllerSoftware/templates/index.html
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lamp Matrix Control</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// State for the entire 5x5 matrix, storing {ww, cw, blue} for each lamp
|
||||||
|
var lampMatrixState = Array(5).fill(null).map(() => Array(5).fill({ww: 0, cw: 0, blue: 0}));
|
||||||
|
var selectedLamps = [];
|
||||||
|
|
||||||
|
// Function to calculate a visual RGB color from the three light values using a proper additive model
|
||||||
|
function calculateRgb(ww, cw, blue) {
|
||||||
|
// Define the RGB components for each light source based on slider track colors
|
||||||
|
const warmWhiteR = 255;
|
||||||
|
const warmWhiteG = 192;
|
||||||
|
const warmWhiteB = 128;
|
||||||
|
|
||||||
|
const coolWhiteR = 192;
|
||||||
|
const coolWhiteG = 224;
|
||||||
|
const coolWhiteB = 255;
|
||||||
|
|
||||||
|
const blueR = 0;
|
||||||
|
const blueG = 0;
|
||||||
|
const blueB = 255;
|
||||||
|
|
||||||
|
// Normalize the slider values (0-255) and apply them to the base colors
|
||||||
|
var r = (ww / 255) * warmWhiteR + (cw / 255) * coolWhiteR + (blue / 255) * blueR;
|
||||||
|
var g = (ww / 255) * warmWhiteG + (cw / 255) * coolWhiteG + (blue / 255) * blueG;
|
||||||
|
var b = (ww / 255) * warmWhiteB + (cw / 255) * coolWhiteB + (blue / 255) * blueB;
|
||||||
|
|
||||||
|
// Clamp the values to 255 and convert to integer
|
||||||
|
r = Math.min(255, Math.round(r));
|
||||||
|
g = Math.min(255, Math.round(g));
|
||||||
|
b = Math.min(255, Math.round(b));
|
||||||
|
|
||||||
|
// Convert to hex string
|
||||||
|
var toHex = (c) => ('0' + c.toString(16)).slice(-2);
|
||||||
|
return '#' + toHex(r) + toHex(g) + toHex(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLampUI(lamp, colorState) {
|
||||||
|
var newColor = calculateRgb(colorState.ww, colorState.cw, colorState.blue);
|
||||||
|
var lampElement = $(`.lamp[data-row="${lamp.row}"][data-col="${lamp.col}"]`);
|
||||||
|
lampElement.css('background-color', newColor);
|
||||||
|
if (newColor === '#000000') {
|
||||||
|
lampElement.removeClass('on');
|
||||||
|
lampElement.css('box-shadow', `inset 0 0 5px rgba(0,0,0,0.5)`);
|
||||||
|
} else {
|
||||||
|
lampElement.addClass('on');
|
||||||
|
lampElement.css('box-shadow', `0 0 15px ${newColor}, 0 0 25px ${newColor}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update the UI and send the full matrix state to the backend
|
||||||
|
function sendFullMatrixUpdate(lampsToUpdate, isRegionUpdate = false) {
|
||||||
|
var fullMatrixData = lampMatrixState.map(row => row.map(lamp => ({
|
||||||
|
ww: lamp.ww,
|
||||||
|
cw: lamp.cw,
|
||||||
|
blue: lamp.blue
|
||||||
|
})));
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/set_matrix',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ matrix: fullMatrixData }),
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
if (isRegionUpdate) {
|
||||||
|
// On a region button click, update the entire matrix UI
|
||||||
|
for (var r = 0; r < 5; r++) {
|
||||||
|
for (var c = 0; c < 5; c++) {
|
||||||
|
updateLampUI({row: r, col: c}, lampMatrixState[r][c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, just update the lamps that changed
|
||||||
|
lampsToUpdate.forEach(function(lamp) {
|
||||||
|
updateLampUI(lamp, lampMatrixState[lamp.row][lamp.col]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSliders(ww, cw, blue, prefix = '') {
|
||||||
|
$(`#${prefix}ww-slider`).val(ww);
|
||||||
|
$(`#${prefix}cw-slider`).val(cw);
|
||||||
|
$(`#${prefix}blue-slider`).val(blue);
|
||||||
|
$(`#${prefix}ww-number`).val(ww);
|
||||||
|
$(`#${prefix}cw-number`).val(cw);
|
||||||
|
$(`#${prefix}blue-number`).val(blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var regionMaps = {
|
||||||
|
'Upper': [
|
||||||
|
{row: 0, col: 0}, {row: 0, col: 1}, {row: 0, col: 2}, {row: 0, col: 3}, {row: 0, col: 4},
|
||||||
|
{row: 1, col: 0}, {row: 1, col: 1}, {row: 1, col: 2}, {row: 1, col: 3}, {row: 1, col: 4},
|
||||||
|
],
|
||||||
|
'Lower': [
|
||||||
|
{row: 3, col: 0}, {row: 3, col: 1}, {row: 3, col: 2}, {row: 3, col: 3}, {row: 3, col: 4},
|
||||||
|
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
||||||
|
],
|
||||||
|
'Left': [
|
||||||
|
{row: 0, col: 0}, {row: 1, col: 0}, {row: 2, col: 0}, {row: 3, col: 0}, {row: 4, col: 0},
|
||||||
|
{row: 0, col: 1}, {row: 1, col: 1}, {row: 2, col: 1}, {row: 3, col: 1}, {row: 4, col: 1},
|
||||||
|
],
|
||||||
|
'Right': [
|
||||||
|
{row: 0, col: 3}, {row: 1, col: 3}, {row: 2, col: 3}, {row: 3, col: 3}, {row: 4, col: 3},
|
||||||
|
{row: 0, col: 4}, {row: 1, col: 4}, {row: 2, col: 4}, {row: 3, col: 4}, {row: 4, col: 4},
|
||||||
|
],
|
||||||
|
'Inner ring': [
|
||||||
|
{row: 1, col: 1}, {row: 1, col: 2}, {row: 1, col: 3},
|
||||||
|
{row: 2, col: 1}, {row: 2, col: 3},
|
||||||
|
{row: 3, col: 1}, {row: 3, col: 2}, {row: 3, col: 3}
|
||||||
|
],
|
||||||
|
'Outer ring': [
|
||||||
|
{row: 0, col: 0}, {row: 0, col: 1}, {row: 0, col: 2}, {row: 0, col: 3}, {row: 0, col: 4},
|
||||||
|
{row: 1, col: 0}, {row: 1, col: 4},
|
||||||
|
{row: 2, col: 0}, {row: 2, col: 4},
|
||||||
|
{row: 3, col: 0}, {row: 3, col: 4},
|
||||||
|
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
||||||
|
],
|
||||||
|
'All': [
|
||||||
|
{row: 0, col: 0}, {row: 0, col: 1}, {row: 0, col: 2}, {row: 0, col: 3}, {row: 0, col: 4},
|
||||||
|
{row: 1, col: 0}, {row: 1, col: 1}, {row: 1, col: 2}, {row: 1, col: 3}, {row: 1, col: 4},
|
||||||
|
{row: 2, col: 0}, {row: 2, col: 1}, {row: 2, col: 3}, {row: 2, col: 4},
|
||||||
|
{row: 3, col: 0}, {row: 3, col: 1}, {row: 3, col: 2}, {row: 3, col: 3}, {row: 3, col: 4},
|
||||||
|
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exclude the center lamp from the 'All' region
|
||||||
|
var allRegionWithoutCenter = regionMaps['All'].filter(lamp => !(lamp.row === 2 && lamp.col === 2));
|
||||||
|
regionMaps['All'] = allRegionWithoutCenter;
|
||||||
|
|
||||||
|
// Initialize lampMatrixState from the initial HTML colors
|
||||||
|
$('.lamp').each(function() {
|
||||||
|
var row = $(this).data('row');
|
||||||
|
var col = $(this).data('col');
|
||||||
|
var color = $(this).css('background-color');
|
||||||
|
var rgb = color.match(/\d+/g);
|
||||||
|
lampMatrixState[row][col] = {
|
||||||
|
ww: rgb[0], cw: rgb[1], blue: rgb[2]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#region-select').on('change', function() {
|
||||||
|
var region = $(this).val();
|
||||||
|
|
||||||
|
// Toggle the inactive state of the control panel based on selection
|
||||||
|
if (region) {
|
||||||
|
$('.control-panel').removeClass('inactive-control');
|
||||||
|
} else {
|
||||||
|
$('.control-panel').addClass('inactive-control');
|
||||||
|
}
|
||||||
|
|
||||||
|
var newlySelectedLamps = regionMaps[region];
|
||||||
|
|
||||||
|
// Clear selected class from all lamps
|
||||||
|
$('.lamp').removeClass('selected');
|
||||||
|
|
||||||
|
// Get the current slider values to use as the new default
|
||||||
|
var ww = parseInt($('#ww-slider').val());
|
||||||
|
var cw = parseInt($('#cw-slider').val());
|
||||||
|
var blue = parseInt($('#blue-slider').val());
|
||||||
|
|
||||||
|
// Reset all lamps except the center to black in our state
|
||||||
|
var lampsToUpdate = [];
|
||||||
|
var centerLampState = lampMatrixState[2][2];
|
||||||
|
lampMatrixState = Array(5).fill(null).map(() => Array(5).fill({ww: 0, cw: 0, blue: 0}));
|
||||||
|
lampMatrixState[2][2] = centerLampState; // Preserve center lamp state
|
||||||
|
|
||||||
|
// Set newly selected lamps to the current slider values
|
||||||
|
selectedLamps = newlySelectedLamps;
|
||||||
|
selectedLamps.forEach(function(lamp) {
|
||||||
|
$(`.lamp[data-row="${lamp.row}"][data-col="${lamp.col}"]`).addClass('selected');
|
||||||
|
lampMatrixState[lamp.row][lamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedLamps.length > 0) {
|
||||||
|
// Update sliders to reflect the state of the first selected lamp
|
||||||
|
var firstLamp = selectedLamps[0];
|
||||||
|
var firstLampState = lampMatrixState[firstLamp.row][firstLamp.col];
|
||||||
|
updateSliders(firstLampState.ww, firstLampState.cw, firstLampState.blue, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the full matrix state
|
||||||
|
sendFullMatrixUpdate(lampsToUpdate, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event listener for the region sliders and number inputs
|
||||||
|
$('.region-slider-group input').on('input', function() {
|
||||||
|
if (selectedLamps.length === 0) return;
|
||||||
|
|
||||||
|
var target = $(this);
|
||||||
|
var originalVal = target.val();
|
||||||
|
var value = parseInt(originalVal, 10);
|
||||||
|
|
||||||
|
// Clamp value
|
||||||
|
if (isNaN(value) || value < 0) { value = 0; }
|
||||||
|
if (value > 255) { value = 255; }
|
||||||
|
|
||||||
|
if (target.is('[type="number"]') && value.toString() !== originalVal) {
|
||||||
|
target.val(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = target.attr('id');
|
||||||
|
if (target.is('[type="range"]')) {
|
||||||
|
$(`#${id.replace('-slider', '-number')}`).val(value);
|
||||||
|
} else if (target.is('[type="number"]')) {
|
||||||
|
$(`#${id.replace('-number', '-slider')}`).val(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ww = parseInt($('#ww-slider').val());
|
||||||
|
var cw = parseInt($('#cw-slider').val());
|
||||||
|
var blue = parseInt($('#blue-slider').val());
|
||||||
|
|
||||||
|
var lampsToUpdate = [];
|
||||||
|
selectedLamps.forEach(function(lamp) {
|
||||||
|
lampMatrixState[lamp.row][lamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
|
lampsToUpdate.push(lamp);
|
||||||
|
});
|
||||||
|
|
||||||
|
sendFullMatrixUpdate(lampsToUpdate);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event listener for the center lamp sliders and number inputs
|
||||||
|
$('.center-slider-group input').on('input', function() {
|
||||||
|
var target = $(this);
|
||||||
|
var originalVal = target.val();
|
||||||
|
var value = parseInt(originalVal, 10);
|
||||||
|
|
||||||
|
// Clamp value
|
||||||
|
if (isNaN(value) || value < 0) { value = 0; }
|
||||||
|
if (value > 255) { value = 255; }
|
||||||
|
|
||||||
|
if (target.is('[type="number"]') && value.toString() !== originalVal) {
|
||||||
|
target.val(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = target.attr('id');
|
||||||
|
if (target.is('[type="range"]')) {
|
||||||
|
$(`#${id.replace('-slider', '-number')}`).val(value);
|
||||||
|
} else if (target.is('[type="number"]')) {
|
||||||
|
$(`#${id.replace('-number', '-slider')}`).val(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ww = parseInt($('#center-ww-slider').val());
|
||||||
|
var cw = parseInt($('#center-cw-slider').val());
|
||||||
|
var blue = parseInt($('#center-blue-slider').val());
|
||||||
|
|
||||||
|
var centerLamp = {row: 2, col: 2};
|
||||||
|
|
||||||
|
lampMatrixState[centerLamp.row][centerLamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
|
|
||||||
|
sendFullMatrixUpdate([centerLamp]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial check to set the inactive state
|
||||||
|
if (!$('#region-select').val()) {
|
||||||
|
$('.control-panel').addClass('inactive-control');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Lamp Matrix Control</h1>
|
||||||
|
<div class="region-control">
|
||||||
|
<label for="region-select">Select Region:</label>
|
||||||
|
<select id="region-select">
|
||||||
|
<option value="" disabled selected>-- Select a region --</option>
|
||||||
|
<option value="Upper">Upper</option>
|
||||||
|
<option value="Lower">Lower</option>
|
||||||
|
<option value="Left">Left</option>
|
||||||
|
<option value="Right">Right</option>
|
||||||
|
<option value="Inner ring">Inner ring</option>
|
||||||
|
<option value="Outer ring">Outer ring</option>
|
||||||
|
<option value="All">All</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="matrix-grid">
|
||||||
|
{% for row in range(5) %}
|
||||||
|
{% for col in range(5) %}
|
||||||
|
<div class="lamp" data-row="{{ row }}" data-col="{{ col }}" style="background-color: {{ matrix[row][col] }}; box-shadow: {{ '0 0 15px ' + matrix[row][col] + ', 0 0 25px ' + matrix[row][col] if matrix[row][col] != '#000000' else 'inset 0 0 5px rgba(0,0,0,0.5)' }}"></div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slider-controls">
|
||||||
|
<div class="center-lamp-control">
|
||||||
|
<h2>Center Lamp</h2>
|
||||||
|
<div class="slider-group center-slider-group">
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Warm White (3000K)</span>
|
||||||
|
<input type="range" id="center-ww-slider" min="0" max="255" value="0" class="white-3000k">
|
||||||
|
<input type="number" id="center-ww-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Cool White (6500K)</span>
|
||||||
|
<input type="range" id="center-cw-slider" min="0" max="255" value="0" class="white-6500k">
|
||||||
|
<input type="number" id="center-cw-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Blue</span>
|
||||||
|
<input type="range" id="center-blue-slider" min="0" max="255" value="0" class="blue">
|
||||||
|
<input type="number" id="center-blue-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-panel">
|
||||||
|
<h2>Selected Region</h2>
|
||||||
|
<div class="slider-group region-slider-group">
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Warm White (3000K)</span>
|
||||||
|
<input type="range" id="ww-slider" min="0" max="255" value="0" class="white-3000k">
|
||||||
|
<input type="number" id="ww-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Cool White (6500K)</span>
|
||||||
|
<input type="range" id="cw-slider" min="0" max="255" value="0" class="white-6500k">
|
||||||
|
<input type="number" id="cw-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Blue</span>
|
||||||
|
<input type="range" id="blue-slider" min="0" max="255" value="0" class="blue">
|
||||||
|
<input type="number" id="blue-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,257 +0,0 @@
|
|||||||
// Include Section
|
|
||||||
|
|
||||||
#include "esp_dmx.h"
|
|
||||||
|
|
||||||
// Define Section
|
|
||||||
|
|
||||||
#define ledPin 2
|
|
||||||
#define INTERRUPT_PIN 0
|
|
||||||
|
|
||||||
struct Button {
|
|
||||||
const uint8_t PIN;
|
|
||||||
uint32_t numberKeyPresses;
|
|
||||||
bool pressed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defining BOOT button on ESP32 as our built-in button.
|
|
||||||
Button button1 = {INTERRUPT_PIN, 0, false};
|
|
||||||
|
|
||||||
int mode = 0;
|
|
||||||
const int modeAmount = 16;
|
|
||||||
uint8_t brightnessMax = 20;
|
|
||||||
uint8_t universalBrightness = 10;
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t data[DMX_PACKET_SIZE] = {0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t dataSeq[modeAmount][DMX_PACKET_SIZE] =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,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,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
0,universalBrightness,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,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,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,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
universalBrightness,0,0,0, // Orange
|
|
||||||
0,universalBrightness,0,0, // White
|
|
||||||
0,universalBrightness,0,0, // White
|
|
||||||
0,0,universalBrightness,0, // Blue
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,0,universalBrightness,0,
|
|
||||||
0,universalBrightness,0,0,
|
|
||||||
//End Inner Round
|
|
||||||
//Start Outer Round
|
|
||||||
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,0,0,
|
|
||||||
0,0,0,0,
|
|
||||||
0,0,0,0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0,
|
|
||||||
//Start Inner Round
|
|
||||||
universalBrightness,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,
|
|
||||||
//End Inner Round
|
|
||||||
//Start Outer Round
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0,
|
|
||||||
universalBrightness,0,0,0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
|
||||||
void IRAM_ATTR isr() {
|
|
||||||
button1.pressed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ledBlink(int interval, int pinNumber) {
|
|
||||||
digitalWrite(
|
|
||||||
pinNumber,
|
|
||||||
!digitalRead(pinNumber)
|
|
||||||
);
|
|
||||||
delay(interval);
|
|
||||||
};
|
|
||||||
|
|
||||||
void dmxSetup() {
|
|
||||||
const dmx_port_t dmx_num = DMX_NUM_1;
|
|
||||||
Serial.printf("\nSetting up DMX Port %d", dmx_num);
|
|
||||||
// First, use the default DMX configuration...
|
|
||||||
dmx_config_t config = DMX_CONFIG_DEFAULT;
|
|
||||||
|
|
||||||
// Declare Personality RGBW
|
|
||||||
const int personality_count = 1;
|
|
||||||
Serial.print("\nDefining DMX Personality... ");
|
|
||||||
dmx_personality_t personalities[] = {
|
|
||||||
{4, "RGBW"}
|
|
||||||
};
|
|
||||||
Serial.print("Done");
|
|
||||||
Serial.print("\nInstalling DMX Driver... ");
|
|
||||||
// ...install the DMX driver...
|
|
||||||
dmx_driver_install(dmx_num, &config, personalities, personality_count);
|
|
||||||
Serial.print("Done");
|
|
||||||
|
|
||||||
// ...and then set the communication pins!
|
|
||||||
const int tx_pin = 18;
|
|
||||||
const int rx_pin = 5;
|
|
||||||
const int rts_pin = 21;
|
|
||||||
Serial.printf("\nSetting up pin %d as Transmit Pin, pin %d as Receive Pin and pin %d as RTS Pin... ", tx_pin, rx_pin, rts_pin);
|
|
||||||
dmx_set_pin(dmx_num, tx_pin, rx_pin, rts_pin);
|
|
||||||
Serial.print("Done\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int serialRead(){
|
|
||||||
int incomingByte;
|
|
||||||
if (Serial.available() > 0) {
|
|
||||||
// read the incoming byte:
|
|
||||||
incomingByte = Serial.read();
|
|
||||||
|
|
||||||
// say what you got:
|
|
||||||
Serial.print("I received: ");
|
|
||||||
Serial.println(incomingByte, DEC);
|
|
||||||
}
|
|
||||||
return incomingByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
pinMode(ledPin,OUTPUT);
|
|
||||||
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
|
|
||||||
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), isr, RISING);
|
|
||||||
delay(2000);
|
|
||||||
Serial.println("If you receive this message, ESP32 module has finished setting up Serial Interface for communication.");
|
|
||||||
dmxSetup();
|
|
||||||
const dmx_port_t dmx_num = DMX_NUM_1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
// Send the DMX packet.
|
|
||||||
dmx_send(DMX_NUM_1);
|
|
||||||
// Preparing next packet
|
|
||||||
if (button1.pressed){
|
|
||||||
if (mode < modeAmount - 1){mode++;} else {mode = 0;};
|
|
||||||
Serial.printf("\n Changing to mode %d", mode); // Increment the value of each slot, excluding the start code.
|
|
||||||
button1.pressed = false; // Reset button status to FALSE
|
|
||||||
};
|
|
||||||
// Wait until the packet is finished being sent before proceeding.
|
|
||||||
dmx_wait_sent(DMX_NUM_1, DMX_TIMEOUT_TICK);
|
|
||||||
// Now write the packet synchronously!
|
|
||||||
dmx_write(DMX_NUM_1, dataSeq[mode], 100);
|
|
||||||
}
|
|
||||||
401
src/lightingFirmware/esp32_test0/esp32_test0.ino
Normal file
401
src/lightingFirmware/esp32_test0/esp32_test0.ino
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
// Include Section
|
||||||
|
|
||||||
|
#include "esp_dmx.h"
|
||||||
|
#include "rdm/controller.h"
|
||||||
|
#include "rdm/responder.h"
|
||||||
|
#include "UUID.h"
|
||||||
|
#include "EEPROM.h"
|
||||||
|
#define INTERRUPT_PIN 0
|
||||||
|
|
||||||
|
#include <BLEDevice.h>
|
||||||
|
#include <BLEServer.h>
|
||||||
|
#include <BLEUtils.h>
|
||||||
|
#include <BLE2902.h>
|
||||||
|
|
||||||
|
bool debugMode = true;
|
||||||
|
int bleCharCount;
|
||||||
|
const int channelPerLamp = 4;
|
||||||
|
const int expectedLampCount = 25;
|
||||||
|
const int dmxPacketSize = channelPerLamp * expectedLampCount + 1; //
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
const uint8_t PIN;
|
||||||
|
uint32_t numberKeyPresses;
|
||||||
|
bool pressed;
|
||||||
|
};
|
||||||
|
uint8_t dmxData[DMX_PACKET_SIZE] = {0};
|
||||||
|
BLEServer* pServer = NULL;
|
||||||
|
|
||||||
|
bool deviceConnected = false;
|
||||||
|
bool oldDeviceConnected = false;
|
||||||
|
|
||||||
|
uint16_t SERVICE_UUID = 20241115;
|
||||||
|
const int panelAmount = 25;
|
||||||
|
BLECharacteristic* pCharacteristics[panelAmount];
|
||||||
|
char* CHARACTERISTIC_UUIDS[panelAmount];
|
||||||
|
|
||||||
|
class MyServerCallbacks: public BLEServerCallbacks {
|
||||||
|
void onConnect(BLEServer* pServer) {
|
||||||
|
deviceConnected = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
void onDisconnect(BLEServer* pServer) {
|
||||||
|
deviceConnected = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defining BOOT button on ESP32 as our built-in button.
|
||||||
|
Button button1 = {INTERRUPT_PIN, 0, false};
|
||||||
|
|
||||||
|
int mode = 0;
|
||||||
|
const int modeAmount = 16;
|
||||||
|
uint8_t brightnessMax = 20;
|
||||||
|
uint8_t universalBrightness = 10;
|
||||||
|
|
||||||
|
uint8_t dataSeq[modeAmount][DMX_PACKET_SIZE] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,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,0,0,0,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
0,universalBrightness,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,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,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,0,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
universalBrightness,0,0,0, // Orange
|
||||||
|
0,universalBrightness,0,0, // White
|
||||||
|
0,universalBrightness,0,0, // White
|
||||||
|
0,0,universalBrightness,0, // Blue
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,0,universalBrightness,0,
|
||||||
|
0,universalBrightness,0,0,
|
||||||
|
//End Inner Round
|
||||||
|
//Start Outer Round
|
||||||
|
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,0,0,
|
||||||
|
0,0,0,0,
|
||||||
|
0,0,0,0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
//Start Inner Round
|
||||||
|
0,0,universalBrightness,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,
|
||||||
|
//End Inner Round
|
||||||
|
//Start Outer Round
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0,
|
||||||
|
universalBrightness,0,0,0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
void IRAM_ATTR isr() {
|
||||||
|
button1.pressed = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ledBlink(int interval, int pinNumber) {
|
||||||
|
digitalWrite(
|
||||||
|
pinNumber,
|
||||||
|
!digitalRead(pinNumber)
|
||||||
|
);
|
||||||
|
delay(interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
void dmxSetup() {
|
||||||
|
const dmx_port_t dmx_num = DMX_NUM_1;
|
||||||
|
Serial.printf("\nSetting up DMX Port %d", dmx_num);
|
||||||
|
// First, use the default DMX configuration...
|
||||||
|
dmx_config_t config = DMX_CONFIG_DEFAULT;
|
||||||
|
|
||||||
|
// Declare Personality RGBW
|
||||||
|
const int personality_count = 1;
|
||||||
|
Serial.print("\nDefining DMX Personality... ");
|
||||||
|
dmx_personality_t personalities[] = {
|
||||||
|
{4, "RGBW"}
|
||||||
|
};
|
||||||
|
Serial.print("Done");
|
||||||
|
Serial.print("\nInstalling DMX Driver... ");
|
||||||
|
// ...install the DMX driver...
|
||||||
|
dmx_driver_install(dmx_num, &config, personalities, personality_count);
|
||||||
|
Serial.print("Done");
|
||||||
|
|
||||||
|
// ...and then set the communication pins!
|
||||||
|
const int tx_pin = 23;
|
||||||
|
const int rx_pin = 22;
|
||||||
|
const int rts_pin = 21;
|
||||||
|
Serial.printf("\nSetting up pin %d as Transmit Pin, pin %d as Receive Pin and pin %d as RTS Pin... ", tx_pin, rx_pin, rts_pin);
|
||||||
|
dmx_set_pin(dmx_num, tx_pin, rx_pin, rts_pin);
|
||||||
|
Serial.print("Done\n");
|
||||||
|
}
|
||||||
|
void serialRead(){
|
||||||
|
String incomingByte;
|
||||||
|
if (Serial.available() > 0) {
|
||||||
|
// read the incoming byte:
|
||||||
|
incomingByte = Serial.readStringUntil('\r\n');
|
||||||
|
Serial.print("\nI received: ");
|
||||||
|
Serial.print(incomingByte);
|
||||||
|
mode = incomingByte.toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(2000);
|
||||||
|
Serial.print("\nIf you receive this message, ESP32 module has finished setting up Serial Interface for communication.");
|
||||||
|
|
||||||
|
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), isr, RISING);
|
||||||
|
|
||||||
|
//Begin of the DMX Setup
|
||||||
|
const dmx_port_t dmx_num = DMX_NUM_1;
|
||||||
|
dmxSetup();
|
||||||
|
|
||||||
|
Serial.println("Welcome to Pupilometer LED Billboard!");
|
||||||
|
const int array_size = 25;
|
||||||
|
rdm_uid_t uids[array_size];
|
||||||
|
|
||||||
|
// This function blocks and may take some time to complete!
|
||||||
|
Serial.printf("Attempting to Discover the Existing DMX Network... ");
|
||||||
|
int num_uids = rdm_discover_devices_simple(DMX_NUM_1, uids, array_size);
|
||||||
|
Serial.printf("Done!\n");
|
||||||
|
Serial.printf("Discovery found %i UIDs as following:\n", num_uids);
|
||||||
|
for (int i = 0; i < num_uids; i++){
|
||||||
|
printf(UIDSTR "\n", UID2STR(uids[i]));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Create the BLE Device
|
||||||
|
BLEDevice::init("Pupilometer LED Billboard");
|
||||||
|
|
||||||
|
// Create the BLE Server
|
||||||
|
pServer = BLEDevice::createServer();
|
||||||
|
pServer->setCallbacks(new MyServerCallbacks());
|
||||||
|
|
||||||
|
// Create the BLE Service
|
||||||
|
BLEService *pService = pServer->createService(SERVICE_UUID,52);
|
||||||
|
const bool debugMode = false;
|
||||||
|
// Serial.printf(debugMode);
|
||||||
|
// Create a BLE Characteristic
|
||||||
|
Serial.printf("\nCalculating BLE Charateristic Count");
|
||||||
|
bleCharCount = (panelAmount * debugMode) + !debugMode;
|
||||||
|
Serial.printf("\nCalculating BLE MTU ...");
|
||||||
|
uint16_t bleMTU = ((panelAmount * 3) / bleCharCount) + 3;
|
||||||
|
Serial.printf("\nSetting BLE MTU to %i bytes... ", bleMTU);
|
||||||
|
BLEDevice::setMTU(bleMTU + 3);
|
||||||
|
Serial.printf("Done!\n");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < bleCharCount; i++){
|
||||||
|
//UUID uuid;
|
||||||
|
//uuid.seed(i+1);
|
||||||
|
//uuid.generate();
|
||||||
|
//Serial.printf("Creating BLE Characteristic with UUID %s ...", BLEUUID(i+1));
|
||||||
|
|
||||||
|
pCharacteristics[i] = pService->createCharacteristic(
|
||||||
|
i+1,
|
||||||
|
// BLEUUID(uuid.toCharArray()),
|
||||||
|
BLECharacteristic::PROPERTY_READ |
|
||||||
|
BLECharacteristic::PROPERTY_WRITE |
|
||||||
|
BLECharacteristic::PROPERTY_NOTIFY |
|
||||||
|
BLECharacteristic::PROPERTY_INDICATE
|
||||||
|
);
|
||||||
|
Serial.printf("Created BLE Characteristic with UUID %s ...", pCharacteristics[i]->getUUID().toString().c_str());
|
||||||
|
|
||||||
|
// pCharacteristics[i]->addDescriptor(new BLE2902());
|
||||||
|
// Serial.printf("Done\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
pService->start();
|
||||||
|
|
||||||
|
// Start advertising
|
||||||
|
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||||
|
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||||
|
pAdvertising->setScanResponse(false);
|
||||||
|
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||||
|
BLEDevice::startAdvertising();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Save Old Mode
|
||||||
|
int modeOld = mode;
|
||||||
|
int msgSize;
|
||||||
|
uint8_t* btMessage[bleCharCount];
|
||||||
|
// uint8_t dmxData[DMX_PACKET_SIZE] = {0};
|
||||||
|
// notify changed value
|
||||||
|
if (deviceConnected) {
|
||||||
|
|
||||||
|
}
|
||||||
|
// disconnecting
|
||||||
|
if (!deviceConnected && oldDeviceConnected) {
|
||||||
|
delay(500); // give the bluetooth stack the chance to get things ready
|
||||||
|
pServer->startAdvertising(); // restart advertising
|
||||||
|
Serial.println("Start advertising");
|
||||||
|
oldDeviceConnected = deviceConnected;
|
||||||
|
}
|
||||||
|
// connecting
|
||||||
|
if (deviceConnected && !oldDeviceConnected) {
|
||||||
|
// do stuff here on connecting
|
||||||
|
oldDeviceConnected = deviceConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.printf("\nConstructing Payload using ");
|
||||||
|
// Serial.printf("Bluetooth Data ...");
|
||||||
|
if (button1.pressed){
|
||||||
|
if (mode < modeAmount - 1){mode++;} else {mode = 0;};
|
||||||
|
// Increment the value of each slot, excluding the start code.
|
||||||
|
button1.pressed = false; // Reset button status to FALSE
|
||||||
|
};
|
||||||
|
serialRead();
|
||||||
|
if (modeOld != mode){
|
||||||
|
Serial.printf("\nChanging Lighting Preset to Preset %d", mode);
|
||||||
|
uint8_t lampData[DMX_PACKET_SIZE / 4 * 3];
|
||||||
|
Serial.printf("\nDetected preset %i size: %i", mode, sizeof(dataSeq[mode]));
|
||||||
|
for (int i = 0; i < sizeof(dataSeq[mode]); i++){
|
||||||
|
dmxData[i] = dataSeq[mode][i];
|
||||||
|
int sublampIndex = i % 4;
|
||||||
|
//Serial.printf("[%i]", sublampIndex, j);
|
||||||
|
if (sublampIndex > 0) {
|
||||||
|
int j = (i / 4) * 3 + sublampIndex - 1;
|
||||||
|
Serial.printf("[%i](%i)", j, sublampIndex);
|
||||||
|
lampData[j] = dataSeq[mode][i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pCharacteristics[0]->setValue(lampData, expectedLampCount * 3);
|
||||||
|
}
|
||||||
|
Serial.printf("\nConstructing DMX Payload with size ");
|
||||||
|
for (int i = 0; i < bleCharCount; i++){
|
||||||
|
btMessage[i] = pCharacteristics[i]->getData();
|
||||||
|
msgSize = pCharacteristics[i]->getLength();
|
||||||
|
Serial.printf("%i bytes ", msgSize);
|
||||||
|
for (int j = 0; j < msgSize; j++){
|
||||||
|
int packet = btMessage[i][j];
|
||||||
|
int lampSum = i*3 + j;
|
||||||
|
int dmxAddress = (lampSum / 3) * 4 + lampSum % 3 + 1;
|
||||||
|
dmxData[dmxAddress] = packet;
|
||||||
|
// Serial.printf("[[%i,%i] %i - %i] ",i , j, dmxAddress, packet);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Serial.printf("\n");
|
||||||
|
|
||||||
|
// Serial.printf(" Done");
|
||||||
|
// Wait until the packet is finished being sent before proceeding.
|
||||||
|
dmx_wait_sent(DMX_NUM_1, DMX_TIMEOUT_TICK);
|
||||||
|
// Now write the packet synchronously!
|
||||||
|
dmx_write(DMX_NUM_1, dmxData, DMX_PACKET_SIZE);
|
||||||
|
dmx_send(DMX_NUM_1);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user