-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
//#define NODE_DEBUG | ||
|
||
#include "module.h" | ||
#include "lauxlib.h" | ||
#include "lmem.h" | ||
#include "platform.h" | ||
#include "user_interface.h" | ||
#include <stdint.h> | ||
#include <string.h> | ||
#include "gpio.h" | ||
#include "hw_timer.h" | ||
|
||
/* Must be a power of 2 (max 128 without changing types) */ | ||
#define RAWBUF_SIZE 128 | ||
#define RAWBUF_MASK (RAWBUF_SIZE - 1) | ||
|
||
#define CB_RAW 0 | ||
#define CB_RC5 1 | ||
|
||
static task_handle_t tasknumber; | ||
static ETSTimer timer; | ||
static struct { | ||
int cb[2]; | ||
uint32_t last_time; | ||
uint16_t rawbuf[RAWBUF_SIZE]; | ||
uint16_t rc5_data; | ||
uint8_t rc5_state; | ||
uint8_t rawbuf_read; | ||
uint8_t rawbuf_write; | ||
uint8_t pin, pin_num; | ||
uint8_t setup; | ||
} data; | ||
|
||
static uint32_t ICACHE_RAM_ATTR lir_interrupt(uint32_t gpio_status) | ||
{ | ||
uint32_t bits = GPIO_REG_READ(GPIO_IN_ADDRESS); | ||
uint32_t now = system_get_time(); | ||
uint32_t pin = data.pin_num; | ||
|
||
/* Ack the interrupt */ | ||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin)); | ||
|
||
uint32_t duration = now - data.last_time; | ||
if (duration > 0xffff) | ||
duration = 0xffff; | ||
|
||
duration = (duration & ~1) | !(bits & BIT(pin)); | ||
|
||
data.last_time = now; | ||
|
||
uint8_t pos = data.rawbuf_write; | ||
uint8_t max = data.rawbuf_read; | ||
|
||
if (max <= pos) | ||
max += RAWBUF_SIZE; | ||
max--; | ||
if (pos < max) { | ||
data.rawbuf[pos] = duration; | ||
data.rawbuf_write = (pos + 1) & RAWBUF_MASK; | ||
} | ||
|
||
task_post_low(tasknumber, 0); | ||
|
||
return gpio_status & ~BIT(pin); | ||
} | ||
|
||
// Lua: setup([pin]) | ||
static int lir_setup( lua_State *L ) | ||
{ | ||
if (data.setup) { | ||
platform_gpio_unregister_intr_hook(lir_interrupt); | ||
gpio_pin_intr_state_set(GPIO_ID_PIN(data.pin_num), GPIO_PIN_INTR_DISABLE); | ||
platform_gpio_mode(data.pin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); | ||
data.setup = 0; | ||
} | ||
if (lua_gettop(L) == 0) | ||
return 0; | ||
|
||
data.pin = luaL_checkinteger( L, 1 ); | ||
luaL_argcheck(L, platform_gpio_exists(data.pin), 1, "Invalid pin"); | ||
data.pin_num = pin_num[data.pin]; | ||
|
||
uint32_t bits = (1 << data.pin_num); | ||
|
||
platform_gpio_mode(data.pin, PLATFORM_GPIO_INT, PLATFORM_GPIO_FLOAT); | ||
gpio_pin_intr_state_set(GPIO_ID_PIN(data.pin_num), GPIO_PIN_INTR_ANYEDGE); | ||
platform_gpio_register_intr_hook(bits, lir_interrupt); | ||
|
||
data.setup = 1; | ||
|
||
return 0; | ||
} | ||
|
||
// Lua: on(event[, cb]) | ||
static int lir_on( lua_State *L ) | ||
{ | ||
static const char * const opts[] = {"raw", "rc5"}; | ||
int type = luaL_checkoption(L, 1, NULL, opts); | ||
|
||
if (data.cb[type] != LUA_NOREF) { | ||
luaL_unref(L, LUA_REGISTRYINDEX, data.cb[type]); | ||
data.cb[type] = LUA_NOREF; | ||
} | ||
|
||
if (lua_gettop(L) < 2) | ||
return 0; | ||
|
||
luaL_argcheck(L, lua_type(L, 2) == LUA_TFUNCTION || | ||
lua_type(L, 2) == LUA_TLIGHTFUNCTION, 2, "Invalid callback"); | ||
|
||
lua_pushvalue(L, 2); | ||
data.cb[type] = luaL_ref(L, LUA_REGISTRYINDEX); | ||
|
||
return 0; | ||
} | ||
|
||
static void rc5_cb() | ||
{ | ||
lua_State *L = lua_getstate(); | ||
lua_rawgeti(L, LUA_REGISTRYINDEX, data.cb[CB_RC5]); | ||
lua_newtable(L); | ||
lua_pushnumber(L, data.rc5_data); | ||
lua_setfield(L, -2, "code"); | ||
lua_pushboolean(L, (data.rc5_data >> 11) & 1); | ||
lua_setfield(L, -2, "toggle"); | ||
lua_pushnumber(L, (data.rc5_data >> 6) & 0x1f); | ||
lua_setfield(L, -2, "device"); | ||
lua_pushnumber(L, data.rc5_data & 0x3f); | ||
lua_setfield(L, -2, "command"); | ||
lua_call(L, 1, 0); | ||
} | ||
|
||
#define RC5_STATE_END 0x80 | ||
#define RC5_STATE_START 0x81 | ||
#define RC5_STATE_ERROR 0x82 | ||
#define RC5_SECOND 0x10 | ||
|
||
static void rc5_break() | ||
{ | ||
data.rc5_state = RC5_STATE_START; | ||
data.rc5_data = 0; | ||
} | ||
|
||
static void rc5_data(int bit) | ||
{ | ||
NODE_DBG("rc5_state=0x%x bit=%d rc5_data=0x%x\n", | ||
data.rc5_state, bit, data.rc5_data); | ||
|
||
if (data.rc5_state == RC5_STATE_START) { | ||
if (bit) | ||
data.rc5_state = 12; | ||
else | ||
data.rc5_state = RC5_STATE_END; | ||
return; | ||
} else if (data.rc5_state & RC5_STATE_END) { | ||
return; | ||
} | ||
|
||
int offset = (data.rc5_state & 0xf); | ||
|
||
if (!(data.rc5_state & RC5_SECOND)) { | ||
if (!bit) { | ||
data.rc5_data |= 1 << offset; | ||
} | ||
if (!offset) { | ||
rc5_cb(); | ||
} | ||
data.rc5_state |= RC5_SECOND; | ||
} else { | ||
int old_bit = (data.rc5_data >> offset) & 1; | ||
if (old_bit ^ bit) | ||
data.rc5_state = RC5_STATE_ERROR; | ||
else if (offset > 0) | ||
data.rc5_state = offset - 1; | ||
else | ||
data.rc5_state = RC5_STATE_END; | ||
} | ||
} | ||
|
||
static void lir_task(os_param_t param, uint8_t prio) | ||
{ | ||
uint8_t pos = data.rawbuf_read; | ||
uint8_t max = data.rawbuf_write; | ||
if (max < pos) | ||
max += RAWBUF_SIZE; | ||
|
||
if (data.cb[CB_RC5] != LUA_NOREF) { | ||
uint8_t cb5_pos = pos; | ||
for(; cb5_pos < max; cb5_pos++) { | ||
uint16_t v = data.rawbuf[cb5_pos & RAWBUF_MASK]; | ||
NODE_DBG("cb5_pos=%u length:%u\n", | ||
cb5_pos, v); | ||
if (v > 2000) { | ||
rc5_break(); | ||
} else if (v > 1000) { | ||
rc5_data(!(v & 1)); | ||
rc5_data(!(v & 1)); | ||
} else { | ||
rc5_data(!(v & 1)); | ||
} | ||
} | ||
} | ||
if (data.cb[CB_RAW] != LUA_NOREF) { | ||
lua_State *L = lua_getstate(); | ||
lua_rawgeti(L, LUA_REGISTRYINDEX, data.cb[CB_RAW]); | ||
|
||
lua_newtable(L); | ||
for(int i = 1; pos < max; pos++, i++) { | ||
uint16_t v = data.rawbuf[pos & RAWBUF_MASK]; | ||
lua_pushnumber(L, v); | ||
lua_rawseti(L, -2, i); | ||
} | ||
|
||
lua_call(L, 1, 0); | ||
} | ||
data.rawbuf_read = max & RAWBUF_MASK; | ||
} | ||
|
||
#ifdef NODE_DEBUG | ||
static int lir_info( lua_State *L ) | ||
{ | ||
char buf[255]; | ||
sprintf(buf, "cb:(raw:%d,rc5:%d) last_time:%u rawbuf:(read:%u,write:%u) pin:%u setup:%u\n", | ||
data.cb[CB_RAW], data.cb[CB_RC5], data.last_time, | ||
data.rawbuf_read, data.rawbuf_write, data.pin, data.setup); | ||
lua_pushfstring(L, buf); | ||
return 1; | ||
} | ||
#endif | ||
|
||
static int ir_open( lua_State *L ) | ||
{ | ||
tasknumber = task_get_id( lir_task ); | ||
data.cb[CB_RAW] = LUA_NOREF; | ||
data.cb[CB_RC5] = LUA_NOREF; | ||
|
||
return 0; | ||
} | ||
|
||
LROT_BEGIN(ir) | ||
LROT_FUNCENTRY( setup, lir_setup ) | ||
LROT_FUNCENTRY( on, lir_on ) | ||
#ifdef NODE_DEBUG | ||
LROT_FUNCENTRY( info, lir_info ) | ||
#endif | ||
LROT_END( ir, NULL, 0 ) | ||
|
||
NODEMCU_MODULE(IR, "ir", ir, ir_open); |