Initial commit
This commit is contained in:
commit
7ef1a57a17
11 changed files with 378 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build/
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[submodule "rpi_ws281x"]
|
||||
path = rpi_ws281x
|
||||
url = https://github.com/jgarff/rpi_ws281x.git
|
||||
[submodule "cpp-httplib"]
|
||||
path = cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib.git
|
12
CMakeLists.txt
Normal file
12
CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
project( SharingLeds )
|
||||
|
||||
add_subdirectory(lua-5.4.3)
|
||||
|
||||
SET(BUILD_TEST OFF CACHE BOOL "")
|
||||
SET(BUILD_SHARED OFF CACHE BOOL "")
|
||||
add_subdirectory(rpi_ws281x)
|
||||
|
||||
add_subdirectory(cpp-httplib)
|
||||
add_subdirectory(main)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Zeus WPI
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
1
cpp-httplib
Submodule
1
cpp-httplib
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 9648f950f5a8a41d18833cf4a85f5821b1bcac54
|
23
main/CMakeLists.txt
Normal file
23
main/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
project( Main )
|
||||
|
||||
add_compile_options(-W -Wall -Wno-missing-field-initializers)
|
||||
|
||||
set (SOURCES "main.cpp")
|
||||
|
||||
source_group("src" FILES ${SOURCES})
|
||||
|
||||
add_executable(server
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories ( ws2811 PUBLIC "${PROJECT_SOURCE_DIR}/../rpi_ws281x")
|
||||
target_link_libraries(server PUBLIC LuaLib )
|
||||
target_link_libraries(server PUBLIC ws2811 )
|
||||
|
||||
target_link_libraries(server PUBLIC httplib::httplib)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(server PRIVATE Threads::Threads)
|
12
main/interpreter_config.h
Normal file
12
main/interpreter_config.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <string>
|
||||
#include "log.h"
|
||||
|
||||
using namespace std;
|
||||
struct InterpreterConfig {
|
||||
unsigned int begin;
|
||||
unsigned int length;
|
||||
bool enabled;
|
||||
std::string scriptkey; // To change the script in this part of the interpreter and get logger output
|
||||
std::string persistkey; // To persist the current running script to disk
|
||||
Log logger;
|
||||
};
|
56
main/log.h
Normal file
56
main/log.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef _LOG_
|
||||
#define _LOG_
|
||||
|
||||
class Log {
|
||||
public:
|
||||
Log() {}
|
||||
Log(unsigned int s) {size = s;}
|
||||
~Log() {
|
||||
}
|
||||
template<typename T>
|
||||
Log& operator<<(const T &msg) {
|
||||
std::stringstream current_string;
|
||||
current_string << msg;
|
||||
os << msg;
|
||||
|
||||
if (current_string.str().back() == '\n') {
|
||||
std::cout << "LOG: " << os.str();
|
||||
if (current >= size || full) {
|
||||
if (ctr >= size) {
|
||||
ctr = 0;
|
||||
}
|
||||
buffer[ctr] = os.str();
|
||||
full = true;
|
||||
} else {
|
||||
buffer.push_back(os.str());
|
||||
}
|
||||
ctr++;
|
||||
current++;
|
||||
os.str("");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
std::vector<std::string> getLogs() {
|
||||
std::vector<std::string> result;
|
||||
int iterator_limit = std::min(size, current);
|
||||
for (int i = iterator_limit - 1; i >= 0; i--) {
|
||||
result.emplace_back(buffer[(current - 1 - i) % size]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int current = 0;
|
||||
unsigned int ctr = 0;
|
||||
bool full = false;
|
||||
std::vector<std::string> buffer;
|
||||
std::stringstream os;
|
||||
unsigned int size = 255;
|
||||
bool last_char_is_newline = true;
|
||||
};
|
||||
|
||||
#endif
|
242
main/main.cpp
Normal file
242
main/main.cpp
Normal file
|
@ -0,0 +1,242 @@
|
|||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <csignal>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
|
||||
#include "lua.hpp"
|
||||
#include "ws2811.h"
|
||||
#include "interpreter_config.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "httplib.h"
|
||||
|
||||
|
||||
#define TARGET_FREQ WS2811_TARGET_FREQ
|
||||
#define GPIO_PIN 18
|
||||
#define DMA 10
|
||||
#define STRIP_TYPE WS2811_STRIP_GBR // WS2812/SK6812RGB integrated chip+leds
|
||||
#define LED_COUNT 690
|
||||
|
||||
#define FPS 15
|
||||
|
||||
int frametime = 1000 / FPS;
|
||||
|
||||
std::unordered_map<lua_State*, InterpreterConfig*> statemap;
|
||||
std::atomic<std::uint64_t> framecounter = 0;
|
||||
|
||||
ws2811_t ledstring =
|
||||
{
|
||||
.freq = TARGET_FREQ,
|
||||
.dmanum = DMA,
|
||||
.channel =
|
||||
{
|
||||
[0] =
|
||||
{
|
||||
.gpionum = GPIO_PIN,
|
||||
.invert = 0,
|
||||
.count = LED_COUNT,
|
||||
.strip_type = WS2811_STRIP_GBR,
|
||||
.brightness = 255,
|
||||
},
|
||||
[1] =
|
||||
{
|
||||
.gpionum = 0,
|
||||
.invert = 0,
|
||||
.count = 0,
|
||||
.brightness = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
static int c_override_print (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int i;
|
||||
for (i = 1; i <= n; i++) { /* for each argument */
|
||||
size_t l;
|
||||
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
|
||||
if (i > 1) /* not the first element? */
|
||||
statemap[L]->logger << '\t'; /* add a tab before it */
|
||||
statemap[L]->logger << s; /* print it */
|
||||
lua_pop(L, 1); /* pop result */
|
||||
}
|
||||
statemap[L]->logger << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int c_led (lua_State *L) {
|
||||
int virtual_location = luaL_checkinteger(L, 1);
|
||||
int red = luaL_checkinteger(L, 2);
|
||||
int green = luaL_checkinteger(L, 3);
|
||||
int blue = luaL_checkinteger(L, 4);
|
||||
InterpreterConfig* config = statemap[L];
|
||||
|
||||
// Lua is one-based, let's keep it consistent and also make our API one-based
|
||||
if (virtual_location <= 0 || virtual_location > (int) config->length) {
|
||||
std::ostringstream errstream;
|
||||
errstream << "setting led " << virtual_location << " of strip with lenght " << config->length << "";
|
||||
luaL_argerror(L, 1, errstream.str().c_str());
|
||||
return 0;
|
||||
} else if (red < 0 || red > 0xff) {
|
||||
std::ostringstream errstream;
|
||||
errstream << "setting red channel to " << red << " but should be between 0 and 255";
|
||||
luaL_argerror(L, 2, errstream.str().c_str());
|
||||
return 0;
|
||||
} else if (green < 0 || green > 0xff) {
|
||||
std::ostringstream errstream;
|
||||
errstream << "setting green channel to " << green << " but should be between 0 and 255";
|
||||
luaL_argerror(L, 3, errstream.str().c_str());
|
||||
return 0;
|
||||
} else if (blue < 0 || blue > 0xff) {
|
||||
std::ostringstream errstream;
|
||||
errstream << "setting blue channel to " << blue << " but should be between 0 and 255";
|
||||
luaL_argerror(L, 4, errstream.str().c_str());
|
||||
return 0;
|
||||
}
|
||||
unsigned int real_location = config->begin + virtual_location - 1;
|
||||
// TODO remove this debugging line
|
||||
printf("Setting led %d to %d %d %d\n", real_location, red, green, blue);
|
||||
ledstring.channel[0].leds[real_location] = (red << 16) | (green << 8)| blue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int c_ledamount(lua_State *L) {
|
||||
lua_pushinteger(L, statemap[L]->length);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int c_delay(lua_State *L) {
|
||||
int millis = luaL_checkinteger (L, 1);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int c_waitframes(lua_State *L) {
|
||||
int amount = luaL_checkinteger(L, 1);
|
||||
uint64_t destination = amount + framecounter;
|
||||
if (amount >= 2 * FPS) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * ((amount / FPS) - 1)));
|
||||
}
|
||||
while (framecounter <= destination) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(frametime / 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to https://www.stefanmisik.com/post/sandboxing-lua-from-c.html
|
||||
static void LuaLoadAndUndefine(lua_State* L, lua_CFunction openFunction, const char* moduleName, const char* functions[])
|
||||
{
|
||||
/* Load the module, the module table gets placed on the top of the stack */
|
||||
luaL_requiref(L, moduleName, openFunction, 1);
|
||||
|
||||
/* Undefine the unwanted functions */
|
||||
for (int i = 0; functions[i] != NULL; i++)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, functions[i]);
|
||||
}
|
||||
|
||||
/* Pop the module table */
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lua_State* setup_lua_sandbox(const char* luacode) {
|
||||
lua_State* L = luaL_newstate();
|
||||
L = luaL_newstate();
|
||||
if (!L) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* remove_base[] = {"assert",
|
||||
"collectgarbage", "dofile", "getmetatable", "loadfile", "load",
|
||||
"loadstring", "rawequal", "rawlen", "rawget", "rawset",
|
||||
"setmetatable", "print", NULL};
|
||||
LuaLoadAndUndefine(L, luaopen_base, "_G", remove_base);
|
||||
static const char* remove_str[] = {"dump", NULL};
|
||||
LuaLoadAndUndefine(L, luaopen_string, LUA_STRLIBNAME, remove_str);
|
||||
|
||||
static const char* all_allowed[] = {NULL};
|
||||
LuaLoadAndUndefine(L, luaopen_table, LUA_TABLIBNAME, all_allowed);
|
||||
LuaLoadAndUndefine(L, luaopen_math, LUA_MATHLIBNAME, all_allowed);
|
||||
|
||||
lua_pushcfunction(L, c_led);
|
||||
lua_setglobal(L, "led");
|
||||
lua_pushcfunction(L, c_ledamount);
|
||||
lua_setglobal(L, "ledamount");
|
||||
lua_pushcfunction(L, c_delay);
|
||||
lua_setglobal(L, "delay");
|
||||
lua_pushcfunction(L, c_waitframes);
|
||||
lua_setglobal(L, "waitframes");
|
||||
lua_pushcfunction(L, c_override_print);
|
||||
lua_setglobal(L, "print");
|
||||
|
||||
luaL_loadbuffer(L, luacode, strlen(luacode), "script");
|
||||
return L;
|
||||
}
|
||||
|
||||
int execute_lua_sandbox(lua_State* L) {
|
||||
int ret = lua_pcall(L, 0, 0, 0);
|
||||
if (ret != 0) {
|
||||
statemap[L]->logger << "CRASH: " << lua_tostring(L, -1) << '\n';
|
||||
lua_close(L);
|
||||
return 1;
|
||||
}
|
||||
lua_close(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::thread spawn_lua_tread(const char* luacode, InterpreterConfig* config) {
|
||||
lua_State* L = setup_lua_sandbox(luacode);
|
||||
statemap[L] = config;
|
||||
std::thread t(execute_lua_sandbox, L);
|
||||
return t;
|
||||
}
|
||||
|
||||
void signal_callback_handler(int signum) {
|
||||
(void) signum;
|
||||
ws2811_fini(&ledstring);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
InterpreterConfig* config = new InterpreterConfig {
|
||||
.begin = 0,
|
||||
.length = 10,
|
||||
.enabled = true
|
||||
};
|
||||
std::thread t = spawn_lua_tread("print(\"hi\")\nled(0, 0, 0, 0)\ndelay(0)\nled(0, 0, 0, 0)\nprint(\"hi\")", config);
|
||||
|
||||
|
||||
t.join();
|
||||
httplib::Server svr;
|
||||
|
||||
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("0.0.0.0", 8080);
|
||||
|
||||
ws2811_return_t ret;
|
||||
if ((ret = ws2811_init(&ledstring)) != WS2811_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "ws2811_init failed: %s\n", ws2811_get_return_t_str(ret));
|
||||
return ret;
|
||||
}
|
||||
signal(SIGINT, signal_callback_handler);
|
||||
signal(SIGHUP, signal_callback_handler);
|
||||
signal(SIGTERM, signal_callback_handler);
|
||||
|
||||
ledstring.channel[0].leds[0] = 0x0000ff00;
|
||||
|
||||
while (true) {
|
||||
ws2811_render(&ledstring);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(frametime));
|
||||
}
|
||||
}
|
1
rpi_ws281x
Submodule
1
rpi_ws281x
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 7624b7828829497bfbbd6b13e7afd1f05593f7c6
|
3
test.lua
Normal file
3
test.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
print("hello world")
|
||||
led(1, 255, 255, 3)
|
||||
print("There are " .. tostring(ledamount()) .. " leds")
|
Loading…
Reference in a new issue