diff --git a/RpiLedBars/.vscode/c_cpp_properties.json b/RpiLedBars/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..b7c0a7d --- /dev/null +++ b/RpiLedBars/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++14", + "intelliSenseMode": "linux-gcc-arm", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/RpiLedBars/.vscode/settings.json b/RpiLedBars/.vscode/settings.json new file mode 100644 index 0000000..798b48e --- /dev/null +++ b/RpiLedBars/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.sourceDirectory": "${workspaceFolder}/backend" +} \ No newline at end of file diff --git a/RpiLedBars/backend/CMakeLists.txt b/RpiLedBars/backend/CMakeLists.txt index 1f184dc..4b7d0e8 100644 --- a/RpiLedBars/backend/CMakeLists.txt +++ b/RpiLedBars/backend/CMakeLists.txt @@ -7,4 +7,6 @@ set(CMAKE_C_STANDARD 99) add_subdirectory(libs) +configure_file(cava_config ../ COPYONLY) + add_subdirectory(src) \ No newline at end of file diff --git a/RpiLedBars/backend/install.sh b/RpiLedBars/backend/install.sh index c3dbf49..135c5ad 100755 --- a/RpiLedBars/backend/install.sh +++ b/RpiLedBars/backend/install.sh @@ -1,5 +1,6 @@ # make -cp ./bin/pixled bin/service_pixled +install -v -D ../build/src/RpiLedBars cava_config -t /opt/pixled/ +# cp ./bin/pixled bin/service_pixled sudo -s bash -c "cp pixled.service /etc/systemd/system; systemctl daemon-reload" # sudo -s bash -c "systemctl enable pixled" sudo -s bash -c "systemctl restart pixled" \ No newline at end of file diff --git a/RpiLedBars/backend/libs/CMakeLists.txt b/RpiLedBars/backend/libs/CMakeLists.txt index 32e4307..c68072b 100644 --- a/RpiLedBars/backend/libs/CMakeLists.txt +++ b/RpiLedBars/backend/libs/CMakeLists.txt @@ -1,5 +1,7 @@ include(FetchContent) +set(FETCHCONTENT_FULLY_DISCONNECTED ON) + FetchContent_Declare( logc GIT_REPOSITORY "https://github.com/Tropicananass/log.c.git" diff --git a/RpiLedBars/backend/pixled.service b/RpiLedBars/backend/pixled.service index eb9a841..17d234a 100644 --- a/RpiLedBars/backend/pixled.service +++ b/RpiLedBars/backend/pixled.service @@ -1,9 +1,10 @@ [Unit] -Description=pixled +Description=RpiLedBars After=network.target [Service] -ExecStart=pixled -n 60 -d 2 +WorkingDirectory=/opt/pixled +ExecStart=/opt/pixled/RpiLedBars -n 60 -d 2 Restart=always User=root Group=root diff --git a/RpiLedBars/backend/src/rpi_param.c b/RpiLedBars/backend/src/rpi_param.c index e859de9..df95f9e 100644 --- a/RpiLedBars/backend/src/rpi_param.c +++ b/RpiLedBars/backend/src/rpi_param.c @@ -106,7 +106,7 @@ void set_sensitivity(int channel, int8_t sensitivity8) { } else { param.ledbar[channel].sensitivity = sensitivity; } - log_debug("Sensitivity : %f", sensitivity); + log_debug("Sensitivity[%d] : %f", channel, sensitivity); } void set_hue_base(int channel, int8_t hueBase8) { diff --git a/RpiLedBars/backend/src/tasks/cava/cava.c b/RpiLedBars/backend/src/tasks/cava/cava.c index 552b302..01983c5 100644 --- a/RpiLedBars/backend/src/tasks/cava/cava.c +++ b/RpiLedBars/backend/src/tasks/cava/cava.c @@ -187,7 +187,7 @@ static int start_cava_process() { if (cavaPid == 0) { /* Child process*/ // int fdLogOut; - char *args[] = {"cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL}; + char *args[] = {"cava", "-p", "./cava_config", NULL}; /* Close reading end of the pipe */ close(fdCavaPipe[0]); diff --git a/RpiLedBars/backend/src/tasks/websocket/websocket.c b/RpiLedBars/backend/src/tasks/websocket/websocket.c index b75718d..3926751 100644 --- a/RpiLedBars/backend/src/tasks/websocket/websocket.c +++ b/RpiLedBars/backend/src/tasks/websocket/websocket.c @@ -34,6 +34,8 @@ int client_fd = -1; +int channel = LED_NCHANS; + /*************************************************************************************************** * Internal Function Prototypes **************************************************************************************************/ @@ -77,6 +79,14 @@ void pattern_command_handler(char *payload); void color_command_handler(char *payload); +void modulate_command_handler(char *payload); + +void channel_command_handler(char *payload); + +void sensitivity_command_handler(char *payload); + +void gravity_command_handler(char *payload); + /*************************************************************************************************** * External Function Definitions **************************************************************************************************/ @@ -142,6 +152,18 @@ void onmessage(int fd, const unsigned char *msg, uint64_t size, int type) { case 'c': color_command_handler(payload); break; + case 's': + modulate_command_handler(payload); + break; + case 'h': + channel_command_handler(payload); + break; + case 'e': + sensitivity_command_handler(payload); + break; + case 'g': + gravity_command_handler(payload); + break; default: log_warn("Unkown command in \"%s\"", msgStr); @@ -187,8 +209,36 @@ void color_command_handler(char *payload) { log_debug(" - %s", tokenArray[n]); } if (atoi(tokenArray[0]) == 0) { - set_hue_base(LED_NCHANS, atoi(tokenArray[1]) * INT8_MAX / HUE_MAX); - set_saturation(LED_NCHANS, atoi(tokenArray[2]) * INT8_MAX / 100); - set_luminosity(LED_NCHANS, atoi(tokenArray[3]) * INT8_MAX / 100); + set_hue_base(channel, atoi(tokenArray[1]) * INT8_MAX / HUE_MAX); + set_saturation(channel, atoi(tokenArray[2]) * INT8_MAX / 100); + set_luminosity(channel, atoi(tokenArray[3]) * INT8_MAX / 100); + } else { + set_saturation(channel, atoi(tokenArray[2]) * INT8_MAX / 100); + set_luminosity(channel, atoi(tokenArray[3]) * INT8_MAX / 100); } +} + +void modulate_command_handler(char *payload) { + bool modulate; + modulate = strcmp(payload, "true") == 0; + log_debug("modulate :%s", modulate ? "true" : "false"); + set_hue_auto_shift(modulate); +} + +void channel_command_handler(char *payload) { + int newChannel = atoi(payload); + if (0 <= newChannel && newChannel <= LED_NCHANS) { + log_debug("Channel: %d", newChannel); + channel = newChannel; + } +} + +void sensitivity_command_handler(char *payload) { + int newSensitivity = atoi(payload); + set_sensitivity(channel, newSensitivity); +} + +void gravity_command_handler(char *payload) { + int newGravity = atoi(payload); + set_gravity(newGravity); } \ No newline at end of file diff --git a/RpiLedBars/cava_config b/RpiLedBars/cava_config new file mode 100644 index 0000000..f6075e6 --- /dev/null +++ b/RpiLedBars/cava_config @@ -0,0 +1,24 @@ +[general] +framerate = 60 +autosens = 0 +sensitivity = 200 +bars = 128 + +[input] +method = alsa +source = plughw:1 + +[output] +method = raw +channels = mono +mono_option = left +data_format = ascii +ascii_max_range=65535 +bit_format = 16bit + +[smoothing] +integral = 77 +monstercat = 0 +waves = 0 +gravity = 0 + diff --git a/RpiLedBars/frontend/ledbars.nginx b/RpiLedBars/frontend/ledbars.nginx index c956095..c15c93a 100644 --- a/RpiLedBars/frontend/ledbars.nginx +++ b/RpiLedBars/frontend/ledbars.nginx @@ -38,6 +38,6 @@ server { alias /home/pi/LedBars/RpiLedBars/frontend/web/; # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. - try_files $uri $uri/ index.html =404; + try_files $uri $uri/ =404; } } diff --git a/RpiLedBars/frontend/web/index.html b/RpiLedBars/frontend/web/index.html index 6dbaa4d..19961f0 100644 --- a/RpiLedBars/frontend/web/index.html +++ b/RpiLedBars/frontend/web/index.html @@ -30,6 +30,11 @@ +
+ + + +
@@ -72,32 +77,32 @@
- - - - - - - - - + + + + + + + + +
- 50 + 64
- +
- 50 + 64
- +
diff --git a/RpiLedBars/frontend/web/index.js b/RpiLedBars/frontend/web/index.js index 07e0ef5..5ba7c75 100644 --- a/RpiLedBars/frontend/web/index.js +++ b/RpiLedBars/frontend/web/index.js @@ -22,6 +22,7 @@ function addLabelAndListener(name, callback) { } } +addLabelAndListener("ws", wsSelectCallback); addLabelAndListener("mode", modeSelectCallback); addLabelAndListener("pattern", patternSelectCallback); addLabelAndListener("channel", channelSelectCallback); @@ -74,4 +75,4 @@ var modulationPicker = new iro.ColorPicker('#modulationPicker', { reconnect(); colorPicker.on("color:change", colorChangeCallback); -modulationPicker.on("color:change", colorChangeCallback); \ No newline at end of file +modulationPicker.on("color:change", modulationColorChangeCallback); \ No newline at end of file diff --git a/RpiLedBars/frontend/web/index_network_handler.js b/RpiLedBars/frontend/web/index_network_handler.js index 3f4cf58..04c42ff 100644 --- a/RpiLedBars/frontend/web/index_network_handler.js +++ b/RpiLedBars/frontend/web/index_network_handler.js @@ -1,3 +1,6 @@ +var ws_index = 0 +const ws_address_list = ["ws://192.168.4.1:8080", "ws://192.168.1.130:8080", "ws://tropicananass.ovh:8080"] + function wait_reconnection() { --timeout; document.getElementById("time-left").innerHTML = timeout + "s"; @@ -8,7 +11,15 @@ function wait_reconnection() { } function reconnect() { - socket = new WebSocket("ws://tropicananass.ovh:8080"); + // for (i in ws_address_list) { + // console.log("try to connect " + ws_address_list[i]) + // try { + socket = new WebSocket(ws_address_list[ws_index]); + // } catch (error) { + // console.error("failed to connect to " + ws_address_list[i] + "\n" + error); + // } + // } + // socket = new WebSocket("ws://192.168.1.130"); timeout = 60; socket.onopen = function(e) { @@ -47,5 +58,10 @@ function reconnect() { socket.onerror = function(error) { console.log(`[error] ws: ${error.message}`); + ++ws_index; + if (ws_index >= ws_address_list.length) { + ws_index = 0; + } + document.getElementById("ws-input").elements["ws-selection"][ws_index].checked = true; }; } \ No newline at end of file diff --git a/RpiLedBars/frontend/web/index_ui_handler.js b/RpiLedBars/frontend/web/index_ui_handler.js index 206cd99..a6a96ce 100644 --- a/RpiLedBars/frontend/web/index_ui_handler.js +++ b/RpiLedBars/frontend/web/index_ui_handler.js @@ -1,3 +1,11 @@ +function wsSelectCallback(event) { + // todo: disable form on this != auto + if (event.target.nodeName == 'INPUT') { + console.log("ws:" + event.target.value); + ws_index = event.target.value; + } +} + function modeSelectCallback(event) { // todo: disable form on this != auto if (event.target.nodeName == 'INPUT') { @@ -20,30 +28,29 @@ function patternSelectCallback(event) { function channelSelectCallback(event) { if (event.target.nodeName == 'INPUT') { - channel = event.target.id.split("-")[0]; - if (channel == "all") { - // inputArray = document.getElementById("channel-input").elements[name + "channel-selection"] - // for (let inputIndex = 0; inputIndex < inputArray.length; inputIndex++) { - // inputArray[inputIndex].checked = false; - // } - selectedChannel = 8; - } else if (channel == "none") { - inputArray = document.getElementById("channel-input").elements[name + "channel-selection"] - for (let inputIndex = 0; inputIndex < inputArray.length; inputIndex++) { - inputArray[inputIndex].checked = false; - } - selectedChannel = -1; - } else { - inputArray = document.getElementById("channelGlobal-input").elements[name + "channelGlobal-selection"] - for (let inputIndex = 0; inputIndex < inputArray.length; inputIndex++) { - inputArray[inputIndex].checked = false; - } - selectedChannel = channel; + console.log("h:" + event.target.value); + if (socket.readyState == WebSocket.OPEN) { + socket.send("h:" + event.target.value); } - console.log("channel:" + selectedChannel); } } +function sensitivityCallback(element) { + console.log("e:" + element.value); + if (socket.readyState == WebSocket.OPEN) { + socket.send("e:" + element.value); + } + rangeCallback(element); +} + +function gravityCallback(element) { + console.log("g:" + element.value); + if (socket.readyState == WebSocket.OPEN) { + socket.send("g:" + element.value); + } + rangeCallback(element); +} + var eventd; function colorModulateCallback(element) { @@ -54,6 +61,9 @@ function colorModulateCallback(element) { document.getElementById("modulationPicker").classList.add("d-none"); } console.log("s: " + element.checked); + if (socket.readyState == WebSocket.OPEN) { + socket.send("s:" + element.checked); + } } function colorChangeCallback(color) { @@ -63,6 +73,13 @@ function colorChangeCallback(color) { } } +function modulationColorChangeCallback(color) { + console.log("c: " + (color.index + 2) + ", h: " + color.hsl.h + ", s: " + color.hsl.s + ", l: " + color.hsl.l + "}"); + if (socket.readyState == WebSocket.OPEN) { + socket.send("c:" + (color.index + 2) + "," + color.hsl.h + "," + color.hsl.s + "," + color.hsl.l); + } +} + function rangeCallback(element) { eventd = this element.parentNode.parentElement.getElementsByTagName('span')[0].innerText = element.value; diff --git a/RpiLedBars/frontend/webapp/src/App.vue b/RpiLedBars/frontend/webapp/src/App.vue index 21bc227..901e3b3 100644 --- a/RpiLedBars/frontend/webapp/src/App.vue +++ b/RpiLedBars/frontend/webapp/src/App.vue @@ -1,123 +1,295 @@ - + diff --git a/RpiLedBars/install.sh b/RpiLedBars/install.sh new file mode 100644 index 0000000..c2f9536 --- /dev/null +++ b/RpiLedBars/install.sh @@ -0,0 +1,18 @@ +#! /usr/bin/env bash + +target=pi_hotspot +dir_target=~/RpiLedBars + +# I - Copy project to pi target +## 1 - select target from ssh config + +## 2 - rsync project +rsync -avz --delete --backup-dir=../RpiLedBars_backup/"$(date +%F_%H-%M-%S)" ./ -e ssh ${target}:${dir_target} + +# II - Connect to pi target +## 1 - Check prequistite (cava, i2s micropphone module, CMake) + +## 2 - Build project +ssh ${target} "cd ${dir_target}/frontend && " + +## 3 - Install project diff --git a/RpiLedBars/main.py b/RpiLedBars/main.py index 964883e..0faf9f2 100644 --- a/RpiLedBars/main.py +++ b/RpiLedBars/main.py @@ -1,5 +1,10 @@ #! /usr/bin/env python3 +### +# This is a test script for testing rgb led strip reacting to i2S microphone input. +# This do not use hardware acceleration. +### + import alsaaudio import audioop import board