From 1ca8e9c006d289a69449027d2cf06ea7576a1885 Mon Sep 17 00:00:00 2001 From: makefu Date: Sat, 4 Jul 2015 15:03:52 +0200 Subject: [PATCH] ws2812: implement brightness/dimming for ws2812,keep buffer while providing rgb brightness may between 0 and 1 and will be applied with the next ws2812.write ws2812.write now takes R,G,B instead of grb (was undocumumented before) by allocating a transfer buffer and freeing it afterwards. ws2812.writergb now becomes obsolete with it but currently remains in the module. --- README.md | 16 ++-- app/modules/ws2812.c | 170 ++++++++++++++++++++++++++++--------------- 2 files changed, 121 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 3c7d04f7..64fb7aca 100644 --- a/README.md +++ b/README.md @@ -440,12 +440,18 @@ In contrast to the source code based inclusion of XBMs into u8glib, it's require ####Control a WS2812 based light strip ```lua + -- set the color of one LED on GPIO2 to red - ws2812.writergb(4, string.char(255, 0, 0)) - -- set the color of 10 LEDs on GPIO0 to blue - ws2812.writergb(3, string.char(0, 0, 255):rep(10)) - -- first LED green, second LED white - ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) + ws2812.write(4, string.char(255, 0, 0)) + -- set the brightness to 50% + ws2812.set_brightness(0.5) + -- set the color of 10 LEDs on GPIO0 to blue with half brightness + ws2812.write(3, string.char(0, 0, 255):rep(10)) + --returns the current brightness + print(ws2812.brightness()) + 0.5 + -- first LED green, second LED white at half brightness + ws2812.write(4, string.char(0, 255, 0, 255, 255, 255)) ``` ####coap client and server diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index 21079cc3..aea1fad7 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -5,6 +5,15 @@ #include "lrotable.h" #include "c_stdlib.h" #include "c_string.h" +#include "c_math.h" + +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_stdarg.h" +#include "c_string.h" + +#include "strbuf.h" + /** * All this code is mostly from http://www.esp8266.com/viewtopic.php?f=21&t=1143&sid=a620a377672cfe9f666d672398415fcb * from user Markus Gritsch. @@ -28,89 +37,128 @@ static void ICACHE_FLASH_ATTR __attribute__((optimize("O2"))) send_ws_1(uint8_t i = 6; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio); } -// Lua: ws2812.writergb(pin, "string") -// Byte triples in the string are interpreted as R G B values and sent to the hardware as G R B. -// WARNING: this function scrambles the input buffer : -// a = string.char(255,0,128) -// ws212.writergb(3,a) -// =a.byte() -// (0,255,128) +// brightness may be between 0 and 1 +#define MAX_BRIGHTNESS 1 -// ws2812.writergb(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red. -// ws2812.writergb(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. -// ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white. -static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) -{ - const uint8_t pin = luaL_checkinteger(L, 1); - size_t length; - const char *rgb = luaL_checklstring(L, 2, &length); - // dont modify lua-internal lstring - make a copy instead - char *buffer = (char *)c_malloc(length); - c_memcpy(buffer, rgb, length); +// Brightness is a floating-point number which will be multiplied before +// setting an LED +static lua_Number brightness = MAX_BRIGHTNESS; - // Initialize the output pin: - platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); - platform_gpio_write(pin, 0); - - // Ignore incomplete Byte triples at the end of buffer: - length -= length % 3; - - // Rearrange R G B values to G R B order needed by WS2812 LEDs: - size_t i; - for (i = 0; i < length; i += 3) { - const char r = buffer[i]; - const char g = buffer[i + 1]; - buffer[i] = g; - buffer[i + 1] = r; - } - - // Do not remove these: - os_delay_us(1); - os_delay_us(1); - - // Send the buffer: - os_intr_lock(); - for (i = 0; i < length; i++) { - uint8_t mask = 0x80; - while (mask) { - (buffer[i] & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); - mask >>= 1; - } - } - os_intr_unlock(); - - c_free(buffer); +// Lua: ws2812.set_brightness(brightness) +// +// brightness must be between 0 and MAX_BRIGHTNESS +static int ICACHE_FLASH_ATTR set_brightness(lua_State* L){ + brightness = luaL_checknumber(L,1); return 0; } +// Lua: brightness =ws2812.brightness() +// returns the current brightness +static int ICACHE_FLASH_ATTR get_brightness(lua_State* L){ + lua_pushnumber(L,brightness); + return 1; +} + +// Lua: R,G,B = ws2812.hsv2rgb(H,S,V) +// H: Hue, S: Saturation, V: Value +// R,G,B are on [0,255] +// +// H is given on [0, 1]. S and V are given on [0, 1]. +// This function does not corrupt your buffer +// +// modified from Alvy Ray Smith's site: +// http://www.alvyray.com/Papers/hsv2rgb.htm +static int ICACHE_FLASH_ATTR ws2812_hsv2rgb(lua_State* L) { + uint8_t i; + lua_Number m, n, f; + const lua_Number h = luaL_checknumber(L,1) * 6; + const lua_Number s = luaL_checknumber(L,2); + lua_Number v = luaL_checknumber(L,3); + + // not very elegant way of dealing with out of range: return black + if ((s<0.0) || (s>1.0) || (v<0.0) || (v>1.0)) { + NODE_ERR("S or V out of range\n"); + lua_pushnumber(L, 0); lua_pushnumber(L, 0); lua_pushnumber(L, 0); + return 3; + } + v = v * 255; + if ((h < 0.0) || (h > 6.0)) { + lua_pushnumber(L, v); lua_pushnumber(L, v); lua_pushnumber(L, v); + return 3; + } + i = floor(h); + f = h - i; + if ( !(i&1) ) { + f = 1 - f; // if i is even + } + m = v * (1 - s) ; + n = v * (1 - s * f) ; + switch (i) { + case 6: + case 0: // RETURN_RGB(v, n, m) + lua_pushnumber(L, v ); lua_pushnumber(L, n ); lua_pushnumber(L, m );break; + case 1: // RETURN_RGB(n, v, m) + lua_pushnumber(L, n ); lua_pushnumber(L, v ); lua_pushnumber(L, m );break; + case 2: // RETURN_RGB(m, v, n) + lua_pushnumber(L, m ); lua_pushnumber(L, v ); lua_pushnumber(L, n );break; + case 3: // RETURN_RGB(m, n, v) + lua_pushnumber(L, m ); lua_pushnumber(L, n ); lua_pushnumber(L, v );break; + case 4: // RETURN_RGB(n, m, v) + lua_pushnumber(L, n ); lua_pushnumber(L, m ); lua_pushnumber(L, v );break; + case 5: // RETURN_RGB(v, m, n) + lua_pushnumber(L, v ); lua_pushnumber(L, m ); lua_pushnumber(L, n );break; + } + return 3; +} + // Lua: ws2812.write(pin, "string") -// Byte triples in the string are interpreted as G R B values. -// This function does not corrupt your buffer. +// Byte triples in the string are interpreted as R G B values. +// This function does not corrupt your buffer // // ws2812.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red. // ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. // ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white. -static int ICACHE_FLASH_ATTR ws2812_writegrb(lua_State* L) { +static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) { const uint8_t pin = luaL_checkinteger(L, 1); size_t length; const char *buffer = luaL_checklstring(L, 2, &length); + // ignore incomplete byte triples for re-arranging + length -= length % 3; + char *transfer = (char*)c_malloc(length); platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); platform_gpio_write(pin, 0); - os_delay_us(10); + + // add brightness + size_t i; + for (i = 0; i < length; i ++) { + transfer[i] = buffer[i]*brightness; + } + + // transfer from grb to rgb + for (i = 0; i < length; i += 3) { + const char r = transfer[i]; + const char g = transfer[i + 1]; + transfer[i] = g; + transfer[i + 1] = r; + } + + os_delay_us(1); + os_delay_us(1); os_intr_lock(); - const char * const end = buffer + length; - while (buffer != end) { + const char * const end = transfer + length; + while (transfer != end) { uint8_t mask = 0x80; while (mask) { - (*buffer & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); + ( *transfer & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); mask >>= 1; } - ++buffer; + ++transfer; } os_intr_unlock(); + c_free(transfer-length); return 0; } @@ -119,8 +167,10 @@ static int ICACHE_FLASH_ATTR ws2812_writegrb(lua_State* L) { #include "lrodefs.h" const LUA_REG_TYPE ws2812_map[] = { - { LSTRKEY( "writergb" ), LFUNCVAL( ws2812_writergb )}, - { LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )}, + { LSTRKEY( "set_brightness" ), LFUNCVAL( set_brightness )}, + { LSTRKEY( "brightness" ), LFUNCVAL( get_brightness )}, + { LSTRKEY( "write" ), LFUNCVAL( ws2812_writergb )}, + { LSTRKEY( "hsv2rgb" ), LFUNCVAL( ws2812_hsv2rgb )}, { LNILKEY, LNILVAL} };