Commit be047ff7 authored by Marcel Stör's avatar Marcel Stör
Browse files

Merge branch 'dev', 1.5.4.1 master drop

parents b580bfe7 a8d984ae
...@@ -485,15 +485,20 @@ static int enduser_setup_http_load_payload(void) ...@@ -485,15 +485,20 @@ static int enduser_setup_http_load_payload(void)
{ {
ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload"); ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload");
int err = (FS_OPEN_OK -1);
int err2 = (FS_OPEN_OK -1);
int file_len = 0;
int f = fs_open(http_html_filename, fs_mode2flag("r")); int f = fs_open(http_html_filename, fs_mode2flag("r"));
int err = fs_seek(f, 0, FS_SEEK_END); if (f >= FS_OPEN_OK) {
int file_len = (int) fs_tell(f); err = fs_seek(f, 0, FS_SEEK_END);
int err2 = fs_seek(f, 0, FS_SEEK_SET); file_len = (int) fs_tell(f);
err2 = fs_seek(f, 0, FS_SEEK_SET);
}
const char cl_hdr[] = "Content-length:%5d\r\n\r\n"; const char cl_hdr[] = "Content-length:%5d\r\n\r\n";
const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */ const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */
if (f == 0 || err == -1 || err2 == -1) if (f < FS_OPEN_OK || err < FS_OPEN_OK || err2 < FS_OPEN_OK)
{ {
ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML."); ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML.");
...@@ -527,6 +532,7 @@ static int enduser_setup_http_load_payload(void) ...@@ -527,6 +532,7 @@ static int enduser_setup_http_load_payload(void)
offset += LITLEN(http_header_200); offset += LITLEN(http_header_200);
offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len); offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len);
fs_read(f, &(state->http_payload_data[offset]), file_len); fs_read(f, &(state->http_payload_data[offset]), file_len);
fs_close(f);
return 0; return 0;
} }
...@@ -787,7 +793,7 @@ static void serve_status(struct tcp_pcb *conn) ...@@ -787,7 +793,7 @@ static void serve_status(struct tcp_pcb *conn)
"Failed to connect to \"%s\" - Wrong password.", "Failed to connect to \"%s\" - Wrong password.",
"Failed to connect to \"%s\" - Network not found.", "Failed to connect to \"%s\" - Network not found.",
"Failed to connect.", "Failed to connect.",
"Connected to \"%s\"." "Connected to \"%s\" (%s)."
}; };
const size_t num_states = sizeof(state)/sizeof(state[0]); const size_t num_states = sizeof(state)/sizeof(state[0]);
...@@ -806,12 +812,25 @@ static void serve_status(struct tcp_pcb *conn) ...@@ -806,12 +812,25 @@ static void serve_status(struct tcp_pcb *conn)
wifi_station_get_config(&config); wifi_station_get_config(&config);
config.ssid[31] = '\0'; config.ssid[31] = '\0';
struct ip_info ip_info;
wifi_get_ip_info(STATION_IF , &ip_info);
char ip_addr[16];
ip_addr[0] = '\0';
if (curr_state == STATION_GOT_IP)
{
c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr));
}
int state_len = c_strlen(s); int state_len = c_strlen(s);
int ip_len = c_strlen(ip_addr);
int ssid_len = c_strlen(config.ssid); int ssid_len = c_strlen(config.ssid);
int status_len = state_len + ssid_len + 1; int status_len = state_len + ssid_len + ip_len + 1;
char status_buf[status_len]; char status_buf[status_len];
memset(status_buf, 0, status_len); memset(status_buf, 0, status_len);
status_len = c_sprintf(status_buf, s, config.ssid); status_len = c_sprintf(status_buf, s, config.ssid, ip_addr);
int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field
char buf[buf_len]; char buf[buf_len];
......
...@@ -52,8 +52,9 @@ static int file_format( lua_State* L ) ...@@ -52,8 +52,9 @@ static int file_format( lua_State* L )
file_close(L); file_close(L);
if( !fs_format() ) if( !fs_format() )
{ {
NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
luaL_error(L, "Failed to format file system");
} }
else{ else{
NODE_ERR( "format done.\n" ); NODE_ERR( "format done.\n" );
...@@ -104,7 +105,7 @@ static int file_exists( lua_State* L ) ...@@ -104,7 +105,7 @@ static int file_exists( lua_State* L )
{ {
size_t len; size_t len;
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long"); luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(fname) == len, 1, "filename invalid");
spiffs_stat stat; spiffs_stat stat;
int rc = SPIFFS_stat(&fs, (char *)fname, &stat); int rc = SPIFFS_stat(&fs, (char *)fname, &stat);
...@@ -119,7 +120,7 @@ static int file_remove( lua_State* L ) ...@@ -119,7 +120,7 @@ static int file_remove( lua_State* L )
{ {
size_t len; size_t len;
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long"); luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(fname) == len, 1, "filename invalid");
file_close(L); file_close(L);
SPIFFS_remove(&fs, (char *)fname); SPIFFS_remove(&fs, (char *)fname);
return 0; return 0;
...@@ -156,10 +157,10 @@ static int file_rename( lua_State* L ) ...@@ -156,10 +157,10 @@ static int file_rename( lua_State* L )
} }
const char *oldname = luaL_checklstring( L, 1, &len ); const char *oldname = luaL_checklstring( L, 1, &len );
luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long"); luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(oldname) == len, 1, "filename invalid");
const char *newname = luaL_checklstring( L, 2, &len ); const char *newname = luaL_checklstring( L, 2, &len );
luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 2, "filename too long"); luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(newname) == len, 2, "filename invalid");
if(SPIFFS_OK==myspiffs_rename( oldname, newname )){ if(SPIFFS_OK==myspiffs_rename( oldname, newname )){
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
...@@ -173,11 +174,13 @@ static int file_rename( lua_State* L ) ...@@ -173,11 +174,13 @@ static int file_rename( lua_State* L )
static int file_fsinfo( lua_State* L ) static int file_fsinfo( lua_State* L )
{ {
u32_t total, used; u32_t total, used;
SPIFFS_info(&fs, &total, &used); if (SPIFFS_info(&fs, &total, &used)) {
return luaL_error(L, "file system failed");
}
NODE_DBG("total: %d, used:%d\n", total, used); NODE_DBG("total: %d, used:%d\n", total, used);
if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total) if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
{ {
return luaL_error(L, "file system error");; return luaL_error(L, "file system error");
} }
lua_pushinteger(L, total-used); lua_pushinteger(L, total-used);
lua_pushinteger(L, used); lua_pushinteger(L, used);
......
// Module for interfacing with GPIO // Module for interfacing with GPIO
//#define NODE_DEBUG
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
...@@ -8,6 +9,7 @@ ...@@ -8,6 +9,7 @@
#include "c_types.h" #include "c_types.h"
#include "c_string.h" #include "c_string.h"
#include "gpio.h" #include "gpio.h"
#include "hw_timer.h"
#define PULLUP PLATFORM_GPIO_PULLUP #define PULLUP PLATFORM_GPIO_PULLUP
#define FLOAT PLATFORM_GPIO_FLOAT #define FLOAT PLATFORM_GPIO_FLOAT
...@@ -160,8 +162,7 @@ static int lgpio_write( lua_State* L ) ...@@ -160,8 +162,7 @@ static int lgpio_write( lua_State* L )
#define DELAY_TABLE_MAX_LEN 256 #define DELAY_TABLE_MAX_LEN 256
#define delayMicroseconds os_delay_us #define delayMicroseconds os_delay_us
// Lua: serout( pin, firstLevel, delay_table, [repeatNum] ) // Lua: serout( pin, firstLevel, delay_table[, repeat_num[, callback]])
// -- serout( pin, firstLevel, delay_table, [repeatNum] )
// gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) // gpio.mode(1,gpio.OUTPUT,gpio.PULLUP)
// gpio.serout(1,1,{30,30,60,60,30,30}) -- serial one byte, b10110010 // gpio.serout(1,1,{30,30,60,60,30,30}) -- serial one byte, b10110010
// gpio.serout(1,1,{30,70},8) -- serial 30% pwm 10k, lasts 8 cycles // gpio.serout(1,1,{30,70},8) -- serial 30% pwm 10k, lasts 8 cycles
...@@ -170,42 +171,98 @@ static int lgpio_write( lua_State* L ) ...@@ -170,42 +171,98 @@ static int lgpio_write( lua_State* L )
// gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) // gpio.mode(1,gpio.OUTPUT,gpio.PULLUP)
// gpio.serout(1,0,{20,10,10,20,10,10,10,100}) -- sim uart one byte 0x5A at about 100kbps // gpio.serout(1,0,{20,10,10,20,10,10,10,100}) -- sim uart one byte 0x5A at about 100kbps
// gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles // gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles
typedef struct
{
unsigned pin;
unsigned level;
uint32 index;
uint32 repeats;
uint32 *delay_table;
uint32 tablelen;
task_handle_t done_taskid;
int lua_done_ref; // callback when transmission is done
} serout_t;
static serout_t serout;
static const os_param_t TIMER_OWNER = 0x6770696f; // "gpio"
static void seroutasync_done (task_param_t arg)
{
lua_State *L = lua_getstate();
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
if (serout.lua_done_ref != LUA_REFNIL) { // we're here so serout.lua_done_ref != LUA_NOREF
lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
if (lua_pcall(L, 0, 0, 0)) {
// Uncaught Error. Print instead of sudden reset
luaL_error(L, "error: %s", lua_tostring(L, -1));
}
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
}
}
static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) {
(void) p;
NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timing is delayed for short timings when debug output is enabled
if (serout.index < serout.tablelen) {
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level);
serout.level = serout.level==LOW ? HIGH : LOW;
platform_hw_timer_arm_us(TIMER_OWNER, serout.delay_table[serout.index]);
serout.index++;
if (serout.repeats && serout.index>=serout.tablelen) {serout.index=0; serout.repeats--;}
} else {
platform_hw_timer_close(TIMER_OWNER);
task_post_low (serout.done_taskid, (task_param_t)0);
}
}
static int lgpio_serout( lua_State* L ) static int lgpio_serout( lua_State* L )
{ {
unsigned clocks_per_us = system_get_cpu_freq(); serout.pin = luaL_checkinteger( L, 1 );
unsigned pin = luaL_checkinteger( L, 1 ); serout.level = luaL_checkinteger( L, 2 );
unsigned level = luaL_checkinteger( L, 2 ); serout.repeats = luaL_optint( L, 4, 1 )-1;
unsigned repeats = luaL_optint( L, 4, 1 ); if (!lua_isnoneornil(L, 5)) {
unsigned table_len, i, j; if (lua_isnumber(L, 5)) {
serout.lua_done_ref = LUA_REFNIL;
} else {
lua_pushvalue(L, 5);
serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
} else {
serout.lua_done_ref = LUA_NOREF;
}
luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin"); luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin");
luaL_argcheck(L, level==HIGH || level==LOW, 2, "Wrong arg type" ); luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" );
luaL_argcheck(L, lua_istable( L, 3 ) && luaL_argcheck(L, lua_istable( L, 3 ) &&
((table_len = lua_objlen( L, 3 )<DELAY_TABLE_MAX_LEN)), 3, "Invalid table" ); ((serout.tablelen = lua_objlen( L, 3 )) < DELAY_TABLE_MAX_LEN), 3, "Invalid delay_times" );
luaL_argcheck(L, repeats<256, 4, "repeats >= 256" );
uint32 *delay_table = luaM_newvector(L, table_len*repeats, uint32); serout.delay_table = luaM_newvector(L, serout.tablelen, uint32);
for( i = 1; i <= table_len; i++ ) { for(unsigned i = 0; i < serout.tablelen; i++ ) {
lua_rawgeti( L, 3, i + 1 ); lua_rawgeti( L, 3, i + 1 );
unsigned delay = (unsigned) luaL_checkinteger( L, -1 ); unsigned delay = (unsigned) luaL_checkinteger( L, -1 );
if (delay > 1000000) return luaL_error( L, "delay %u must be < 1,000,000 us", i ); serout.delay_table[i] = delay;
delay_table[i-1] = delay;
lua_pop( L, 1 ); lua_pop( L, 1 );
} }
for( i = 0; i <= repeats; i++ ) { if (serout.lua_done_ref != LUA_NOREF) { // async version for duration above 15 mSec
if (!i) // skip the first loop (presumably this is some form of icache priming??). if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
continue; // Failed to init the timer
luaL_error(L, "Unable to initialize timer");
for( j = 0;j < table_len; j++ ){
/* Direct Write is a ROM function which already disables interrupts for the atomic bit */
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level);
delayMicroseconds(delay_table[j]);
level = level==LOW ? HIGH : LOW;
} }
platform_hw_timer_set_func(TIMER_OWNER, seroutasync_cb, 0);
serout.index = 0;
seroutasync_cb(0);
} else { // sync version for sub-50 µs resolution & total duration < 15 mSec
do {
for( serout.index = 0;serout.index < serout.tablelen; serout.index++ ){
NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timings is delayed for short timings when debug output is enabled
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level);
serout.level = serout.level==LOW ? HIGH : LOW;
delayMicroseconds(serout.delay_table[serout.index]);
}
} while (serout.repeats--);
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
} }
luaM_freearray(L, delay_table, table_len, uint32);
return 0; return 0;
} }
#undef DELAY_TABLE_MAX_LEN #undef DELAY_TABLE_MAX_LEN
...@@ -238,6 +295,7 @@ int luaopen_gpio( lua_State *L ) { ...@@ -238,6 +295,7 @@ int luaopen_gpio( lua_State *L ) {
} }
platform_gpio_init(task_get_id(gpio_intr_callback_task)); platform_gpio_init(task_get_id(gpio_intr_callback_task));
#endif #endif
serout.done_taskid = task_get_id((task_callback_t) seroutasync_done);
return 0; return 0;
} }
......
/*
* Driver for Honeywell HMC5883L compass IC
*
* Code based on BMP085 driver.
*/
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_string.h"
static const uint32_t hmc5883_i2c_id = 0;
static const uint8_t hmc5883_i2c_addr = 0x1E;
static uint8_t ICACHE_FLASH_ATTR r8u(uint32_t id, uint8_t reg) {
uint8_t ret;
platform_i2c_send_start(id);
platform_i2c_send_address(id, hmc5883_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(id, reg);
platform_i2c_send_stop(id);
platform_i2c_send_start(id);
platform_i2c_send_address(id, hmc5883_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
ret = platform_i2c_recv_byte(id, 0);
platform_i2c_send_stop(id);
return ret;
}
static void ICACHE_FLASH_ATTR w8u(uint32_t id, uint8_t reg, uint8_t val) {
platform_i2c_send_start(hmc5883_i2c_id);
platform_i2c_send_address(hmc5883_i2c_id, hmc5883_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(hmc5883_i2c_id, reg);
platform_i2c_send_byte(hmc5883_i2c_id, val);
platform_i2c_send_stop(hmc5883_i2c_id);
}
static int ICACHE_FLASH_ATTR hmc5883_init(lua_State* L) {
uint32_t sda;
uint32_t scl;
uint8_t devid_a, devid_b, devid_c;
sda = luaL_checkinteger(L, 1);
scl = luaL_checkinteger(L, 2);
luaL_argcheck(L, sda > 0 && scl > 0, 1, "no i2c for D0");
platform_i2c_setup(hmc5883_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
devid_a = r8u(hmc5883_i2c_id, 10);
devid_b = r8u(hmc5883_i2c_id, 11);
devid_c = r8u(hmc5883_i2c_id, 12);
if ((devid_a != 0x48) || (devid_b != 0x34) || (devid_c != 0x33)) {
return luaL_error(L, "device not found");
}
// 8 sample average, 15 Hz update rate, normal measurement
w8u(hmc5883_i2c_id, 0x00, 0x70);
// Gain = 5
w8u(hmc5883_i2c_id, 0x01, 0xA0);
// Continuous measurement
w8u(hmc5883_i2c_id, 0x02, 0x00);
return 0;
}
static int ICACHE_FLASH_ATTR hmc5883_read(lua_State* L) {
uint8_t data[6];
int x,y,z;
int i;
platform_i2c_send_start(hmc5883_i2c_id);
platform_i2c_send_address(hmc5883_i2c_id, hmc5883_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(hmc5883_i2c_id, 0x03);
platform_i2c_send_stop(hmc5883_i2c_id);
platform_i2c_send_start(hmc5883_i2c_id);
platform_i2c_send_address(hmc5883_i2c_id, hmc5883_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
for (i=0; i<5; i++) {
data[i] = platform_i2c_recv_byte(hmc5883_i2c_id, 1);
}
data[5] = platform_i2c_recv_byte(hmc5883_i2c_id, 0);
platform_i2c_send_stop(hmc5883_i2c_id);
x = (int16_t) ((data[0] << 8) | data[1]);
y = (int16_t) ((data[2] << 8) | data[3]);
z = (int16_t) ((data[4] << 8) | data[5]);
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
return 3;
}
static const LUA_REG_TYPE hmc5883_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL( hmc5883_read )},
{ LSTRKEY( "init" ), LFUNCVAL( hmc5883_init )},
{ LNILKEY, LNILVAL}
};
NODEMCU_MODULE(HMC5883L, "hmc5883l", hmc5883_map, NULL);
/*
* Driver for STM L3G4200D three axis gyroscope
*
* Code based on BMP085 driver.
*/
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_string.h"
static const uint32_t i2c_id = 0;
static const uint8_t i2c_addr = 0x69;
static uint8_t ICACHE_FLASH_ATTR r8u(uint32_t id, uint8_t reg) {
uint8_t ret;
platform_i2c_send_start(id);
platform_i2c_send_address(id, i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(id, reg);
platform_i2c_send_stop(id);
platform_i2c_send_start(id);
platform_i2c_send_address(id, i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
ret = platform_i2c_recv_byte(id, 0);
platform_i2c_send_stop(id);
return ret;
}
static void ICACHE_FLASH_ATTR w8u(uint32_t id, uint8_t reg, uint8_t val) {
platform_i2c_send_start(i2c_id);
platform_i2c_send_address(i2c_id, i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(i2c_id, reg);
platform_i2c_send_byte(i2c_id, val);
platform_i2c_send_stop(i2c_id);
}
static int ICACHE_FLASH_ATTR l3g4200d_init(lua_State* L) {
uint32_t sda;
uint32_t scl;
uint8_t devid;
sda = luaL_checkinteger(L, 1);
scl = luaL_checkinteger(L, 2);
luaL_argcheck(L, sda > 0 && scl > 0, 1, "no i2c for D0");
platform_i2c_setup(i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW);
devid = r8u(i2c_id, 0xF);
if (devid != 0xD3) {
return luaL_error(L, "device not found");
}
w8u(i2c_id, 0x20, 0xF);
return 0;
}
static int ICACHE_FLASH_ATTR l3g4200d_read(lua_State* L) {
uint8_t data[6];
int x,y,z;
int i;
platform_i2c_send_start(i2c_id);
platform_i2c_send_address(i2c_id, i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(i2c_id, 0xA8);
platform_i2c_send_start(i2c_id);
platform_i2c_send_address(i2c_id, i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
for (i=0; i<5; i++) {
data[i] = platform_i2c_recv_byte(i2c_id, 1);
}
data[5] = platform_i2c_recv_byte(i2c_id, 0);
platform_i2c_send_stop(i2c_id);
x = (int16_t) ((data[1] << 8) | data[0]);
y = (int16_t) ((data[3] << 8) | data[2]);
z = (int16_t) ((data[5] << 8) | data[4]);
lua_pushinteger(L, x);
lua_pushinteger(L, y);
lua_pushinteger(L, z);
return 3;
}
static const LUA_REG_TYPE l3g4200d_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL( l3g4200d_read )},
{ LSTRKEY( "init" ), LFUNCVAL( l3g4200d_init )},
{ LNILKEY, LNILVAL}
};
NODEMCU_MODULE(L3G4200D, "l3g4200d", l3g4200d_map, NULL);
...@@ -398,7 +398,7 @@ static int node_compile( lua_State* L ) ...@@ -398,7 +398,7 @@ static int node_compile( lua_State* L )
int file_fd = FS_OPEN_OK - 1; int file_fd = FS_OPEN_OK - 1;
size_t len; size_t len;
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
if ( len > FS_NAME_MAX_LENGTH ) if ( len >= FS_NAME_MAX_LENGTH )
return luaL_error(L, "filename too long"); return luaL_error(L, "filename too long");
char output[FS_NAME_MAX_LENGTH]; char output[FS_NAME_MAX_LENGTH];
......
// Module for interfacing with PCM functionality
#include "module.h"
#include "lauxlib.h"
#include "task/task.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "pcm.h"
#include "pcm_drv.h"
#define GET_PUD() pud_t *pud = (pud_t *)luaL_checkudata(L, 1, "pcm.driver"); \
cfg_t *cfg = &(pud->cfg);
#define UNREF_CB(_cb) luaL_unref( L, LUA_REGISTRYINDEX, _cb ); \
_cb = LUA_NOREF;
#define COND_REF(_cond) _cond ? luaL_ref( L, LUA_REGISTRYINDEX ) : LUA_NOREF
static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
{
if (cb_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
lua_call( L, 1, returns );
}
}
static int pcm_drv_free( lua_State *L )
{
GET_PUD();
UNREF_CB( cfg->cb_data_ref );
UNREF_CB( cfg->cb_drained_ref );
UNREF_CB( cfg->cb_paused_ref );
UNREF_CB( cfg->cb_stopped_ref );
UNREF_CB( cfg->cb_vu_ref );
UNREF_CB( cfg->self_ref );
if (cfg->bufs[0].data) {
c_free( cfg->bufs[0].data );
cfg->bufs[0].data = NULL;
}
if (cfg->bufs[1].data) {
c_free( cfg->bufs[1].data );
cfg->bufs[1].data = NULL;
}
return 0;
}
// Lua: drv:close()
static int pcm_drv_close( lua_State *L )
{
GET_PUD();
pud->drv->close( cfg );
return pcm_drv_free( L );
}
// Lua: drv:stop(self)
static int pcm_drv_stop( lua_State *L )
{
GET_PUD();
// throttle ISR and reader
cfg->isr_throttled = -1;
pud->drv->stop( cfg );
// invalidate the buffers
cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
dispatch_callback( L, cfg->self_ref, cfg->cb_stopped_ref, 0 );
return 0;
}
// Lua: drv:pause(self)
static int pcm_drv_pause( lua_State *L )
{
GET_PUD();
// throttle ISR and reader
cfg->isr_throttled = -1;
pud->drv->stop( cfg );
dispatch_callback( L, cfg->self_ref, cfg->cb_paused_ref, 0 );
return 0;
}
static void pcm_start_play_task( task_param_t param, uint8 prio )
{
lua_State *L = lua_getstate();
pud_t *pud = (pud_t *)param;
cfg_t *cfg = &(pud->cfg);
// stop driver before starting it in case it hasn't been stopped from Lua
pud->drv->stop( cfg );
if (!pud->drv->play( cfg )) {
luaL_error( L, "pcm driver start" );
}
// unthrottle ISR and reader
pud->cfg.isr_throttled = 0;
}
// Lua: drv:play(self, rate)
static int pcm_drv_play( lua_State *L )
{
GET_PUD();
cfg->rate = luaL_optinteger( L, 2, PCM_RATE_8K );
luaL_argcheck( L, (cfg->rate >= PCM_RATE_1K) && (cfg->rate <= PCM_RATE_16K), 2, "invalid bit rate" );
if (cfg->self_ref == LUA_NOREF) {
lua_pushvalue( L, 1 ); // copy self userdata to the top of stack
cfg->self_ref = luaL_ref( L, LUA_REGISTRYINDEX );
}
// schedule actions for play in separate task since drv:play() might have been called
// in the callback fn of pcm_data_play_task() which in turn gets called when starting play...
task_post_low( cfg->start_play_task, (os_param_t)pud );
return 0;
}
// Lua: drv.on(self, event, cb_fn)
static int pcm_drv_on( lua_State *L )
{
size_t len;
const char *event;
uint8_t is_func = FALSE;
GET_PUD();
event = luaL_checklstring( L, 2, &len );
if ((lua_type( L, 3 ) == LUA_TFUNCTION) ||
(lua_type( L, 3 ) == LUA_TLIGHTFUNCTION)) {
lua_pushvalue( L, 3 ); // copy argument (func) to the top of stack
is_func = TRUE;
}
if ((len == 4) && (c_strcmp( event, "data" ) == 0)) {
luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_data_ref);
cfg->cb_data_ref = COND_REF( is_func );
} else if ((len == 7) && (c_strcmp( event, "drained" ) == 0)) {
luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_drained_ref);
cfg->cb_drained_ref = COND_REF( is_func );
} else if ((len == 6) && (c_strcmp( event, "paused" ) == 0)) {
luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_paused_ref);
cfg->cb_paused_ref = COND_REF( is_func );
} else if ((len == 7) && (c_strcmp( event, "stopped" ) == 0)) {
luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_stopped_ref);
cfg->cb_stopped_ref = COND_REF( is_func );
} else if ((len == 2) && (c_strcmp( event, "vu" ) == 0)) {
luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_vu_ref);
cfg->cb_vu_ref = COND_REF( is_func );
int freq = luaL_optinteger( L, 4, 10 );
luaL_argcheck( L, (freq > 0) && (freq <= 200), 4, "invalid range" );
cfg->vu_freq = (uint8_t)freq;
} else {
if (is_func) {
// need to pop pushed function arg
lua_pop( L, 1 );
}
return luaL_error( L, "method not supported" );
}
return 0;
}
// Lua: pcm.new( type, pin )
static int pcm_new( lua_State *L )
{
pud_t *pud = (pud_t *) lua_newuserdata( L, sizeof( pud_t ) );
cfg_t *cfg = &(pud->cfg);
int driver;
cfg->rbuf_idx = cfg->fbuf_idx = 0;
cfg->isr_throttled = -1; // start ISR and reader in throttled mode
driver = luaL_checkinteger( L, 1 );
luaL_argcheck( L, (driver >= 0) && (driver < PCM_DRIVER_END), 1, "invalid driver" );
cfg->self_ref = LUA_NOREF;
cfg->cb_data_ref = cfg->cb_drained_ref = LUA_NOREF;
cfg->cb_paused_ref = cfg->cb_stopped_ref = LUA_NOREF;
cfg->cb_vu_ref = LUA_NOREF;
cfg->bufs[0].buf_size = cfg->bufs[1].buf_size = 0;
cfg->bufs[0].data = cfg->bufs[1].data = NULL;
cfg->bufs[0].len = cfg->bufs[1].len = 0;
cfg->bufs[0].rpos = cfg->bufs[1].rpos = 0;
cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
cfg->data_vu_task = task_get_id( pcm_data_vu_task );
cfg->vu_freq = 10;
cfg->data_play_task = task_get_id( pcm_data_play_task );
cfg->start_play_task = task_get_id( pcm_start_play_task );
if (driver == PCM_DRIVER_SD) {
cfg->pin = luaL_checkinteger( L, 2 );
MOD_CHECK_ID(sigma_delta, cfg->pin);
pud->drv = &pcm_drv_sd;
pud->drv->init( cfg );
/* set its metatable */
lua_pushvalue( L, -1 ); // copy self userdata to the top of stack
luaL_getmetatable( L, "pcm.driver" );
lua_setmetatable( L, -2 );
return 1;
} else {
pud->drv = NULL;
return 0;
}
}
static const LUA_REG_TYPE pcm_driver_map[] = {
{ LSTRKEY( "play" ), LFUNCVAL( pcm_drv_play ) },
{ LSTRKEY( "pause" ), LFUNCVAL( pcm_drv_pause ) },
{ LSTRKEY( "stop" ), LFUNCVAL( pcm_drv_stop ) },
{ LSTRKEY( "close" ), LFUNCVAL( pcm_drv_close ) },
{ LSTRKEY( "on" ), LFUNCVAL( pcm_drv_on ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( pcm_drv_free ) },
{ LSTRKEY( "__index" ), LROVAL( pcm_driver_map ) },
{ LNILKEY, LNILVAL }
};
// Module function map
static const LUA_REG_TYPE pcm_map[] = {
{ LSTRKEY( "new" ), LFUNCVAL( pcm_new ) },
{ LSTRKEY( "SD" ), LNUMVAL( PCM_DRIVER_SD ) },
{ LSTRKEY( "RATE_1K" ), LNUMVAL( PCM_RATE_1K ) },
{ LSTRKEY( "RATE_2K" ), LNUMVAL( PCM_RATE_2K ) },
{ LSTRKEY( "RATE_4K" ), LNUMVAL( PCM_RATE_4K ) },
{ LSTRKEY( "RATE_5K" ), LNUMVAL( PCM_RATE_5K ) },
{ LSTRKEY( "RATE_8K" ), LNUMVAL( PCM_RATE_8K ) },
{ LSTRKEY( "RATE_10K" ), LNUMVAL( PCM_RATE_10K ) },
{ LSTRKEY( "RATE_12K" ), LNUMVAL( PCM_RATE_12K ) },
{ LSTRKEY( "RATE_16K" ), LNUMVAL( PCM_RATE_16K ) },
{ LNILKEY, LNILVAL }
};
int luaopen_pcm( lua_State *L ) {
luaL_rometatable( L, "pcm.driver", (void *)pcm_driver_map ); // create metatable
return 0;
}
NODEMCU_MODULE(PCM, "pcm", pcm_map, luaopen_pcm);
...@@ -7,6 +7,33 @@ ...@@ -7,6 +7,33 @@
#include "rtc/rtctime.h" #include "rtc/rtctime.h"
/* seconds per day */
#define SPD 24*60*60
/* days per month -- nonleap! */
static const short __spm[13] =
{ 0,
(31),
(31+28),
(31+28+31),
(31+28+31+30),
(31+28+31+30+31),
(31+28+31+30+31+30),
(31+28+31+30+31+30+31),
(31+28+31+30+31+30+31+31),
(31+28+31+30+31+30+31+31+30),
(31+28+31+30+31+30+31+31+30+31),
(31+28+31+30+31+30+31+31+30+31+30),
(31+28+31+30+31+30+31+31+30+31+30+31),
};
static int __isleap (int year) {
/* every fourth year is a leap year except for century years that are
* not divisible by 400. */
/* return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); */
return (!(year % 4) && ((year % 100) || !(year % 400)));
}
// ******* C API functions ************* // ******* C API functions *************
void rtctime_early_startup (void) void rtctime_early_startup (void)
...@@ -49,6 +76,36 @@ void rtctime_deep_sleep_until_aligned_us (uint32_t align_us, uint32_t min_us) ...@@ -49,6 +76,36 @@ void rtctime_deep_sleep_until_aligned_us (uint32_t align_us, uint32_t min_us)
rtc_time_deep_sleep_until_aligned (align_us, min_us); rtc_time_deep_sleep_until_aligned (align_us, min_us);
} }
void rtctime_gmtime (const int32 stamp, struct rtc_tm *r)
{
int32_t i;
int32_t work = stamp % (SPD);
r->tm_sec = work % 60; work /= 60;
r->tm_min = work % 60; r->tm_hour = work / 60;
work = stamp / (SPD);
r->tm_wday = (4 + work) % 7;
for (i = 1970; ; ++i) {
int32_t k = __isleap (i) ? 366 : 365;
if (work >= k) {
work -= k;
} else {
break;
}
}
r->tm_year = i - 1900;
r->tm_yday = work;
r->tm_mday = 1;
if (__isleap (i) && (work > 58)) {
if (work == 59) r->tm_mday = 2; /* 29.2. */
work -= 1;
}
for (i = 11; i && (__spm[i] > work); --i) ;
r->tm_mon = i;
r->tm_mday += work - __spm[i];
}
// ******* Lua API functions ************* // ******* Lua API functions *************
...@@ -115,12 +172,43 @@ static int rtctime_dsleep_aligned (lua_State *L) ...@@ -115,12 +172,43 @@ static int rtctime_dsleep_aligned (lua_State *L)
} }
static void add_table_item (lua_State *L, const char *key, int val)
{
lua_pushstring (L, key);
lua_pushinteger (L, val);
lua_rawset (L, -3);
}
// rtctime.epoch2cal (stamp)
static int rtctime_epoch2cal (lua_State *L)
{
struct rtc_tm date;
int32_t stamp = luaL_checkint (L, 1);
luaL_argcheck (L, stamp >= 0, 1, "wrong arg range");
rtctime_gmtime (stamp, &date);
/* construct Lua table */
lua_createtable (L, 0, 8);
add_table_item (L, "yday", date.tm_yday + 1);
add_table_item (L, "wday", date.tm_wday + 1);
add_table_item (L, "year", date.tm_year + 1900);
add_table_item (L, "mon", date.tm_mon + 1);
add_table_item (L, "day", date.tm_mday);
add_table_item (L, "hour", date.tm_hour);
add_table_item (L, "min", date.tm_min);
add_table_item (L, "sec", date.tm_sec);
return 1;
}
// Module function map // Module function map
static const LUA_REG_TYPE rtctime_map[] = { static const LUA_REG_TYPE rtctime_map[] = {
{ LSTRKEY("set"), LFUNCVAL(rtctime_set) }, { LSTRKEY("set"), LFUNCVAL(rtctime_set) },
{ LSTRKEY("get"), LFUNCVAL(rtctime_get) }, { LSTRKEY("get"), LFUNCVAL(rtctime_get) },
{ LSTRKEY("dsleep"), LFUNCVAL(rtctime_dsleep) }, { LSTRKEY("dsleep"), LFUNCVAL(rtctime_dsleep) },
{ LSTRKEY("dsleep_aligned"), LFUNCVAL(rtctime_dsleep_aligned) }, { LSTRKEY("dsleep_aligned"), LFUNCVAL(rtctime_dsleep_aligned) },
{ LSTRKEY("epoch2cal"), LFUNCVAL(rtctime_epoch2cal) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
......
...@@ -39,8 +39,8 @@ static int spi_setup( lua_State *L ) ...@@ -39,8 +39,8 @@ static int spi_setup( lua_State *L )
return luaL_error( L, "out of range" ); return luaL_error( L, "out of range" );
} }
if (clock_div < 4) { if (clock_div == 0) {
// defaulting to 8 // defaulting to 8 for backward compatibility
clock_div = 8; clock_div = 8;
} }
......
/*
* Module for interfacing with Switec instrument steppers (and
* similar devices). These are the steppers that are used in automotive
* instrument panels and the like. Run off 5 volts at low current.
*
* Code inspired by:
*
* SwitecX25 Arduino Library
* Guy Carpenter, Clearwater Software - 2012
*
* Licensed under the BSD2 license, see license.txt for details.
*
* NodeMcu integration by Philip Gladstone, N1DQ
*/
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_types.h"
#include "task/task.h"
#include "driver/switec.h"
// This is the reference to the callbacks for when the pointer
// stops moving.
static int stopped_callback[SWITEC_CHANNEL_COUNT] = { LUA_NOREF, LUA_NOREF, LUA_NOREF };
static task_handle_t tasknumber;
static void callback_free(lua_State* L, unsigned int id)
{
luaL_unref(L, LUA_REGISTRYINDEX, stopped_callback[id]);
stopped_callback[id] = LUA_NOREF;
}
static void callback_set(lua_State* L, unsigned int id, int argNumber)
{
if (lua_type(L, argNumber) == LUA_TFUNCTION || lua_type(L, argNumber) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, argNumber); // copy argument (func) to the top of stack
callback_free(L, id);
stopped_callback[id] = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
static void callback_execute(lua_State* L, unsigned int id)
{
if (stopped_callback[id] != LUA_NOREF) {
int callback = stopped_callback[id];
lua_rawgeti(L, LUA_REGISTRYINDEX, callback);
callback_free(L, id);
lua_call(L, 0, 0);
}
}
int platform_switec_exists( unsigned int id )
{
return (id < SWITEC_CHANNEL_COUNT);
}
// Lua: setup(id, P1, P2, P3, P4, maxSpeed)
static int lswitec_setup( lua_State* L )
{
unsigned int id;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( switec, id );
int pin[4];
if (switec_close(id)) {
return luaL_error( L, "Unable to setup stepper." );
}
int i;
for (i = 0; i < 4; i++) {
uint32_t gpio = luaL_checkinteger(L, 2 + i);
luaL_argcheck(L, platform_gpio_exists(gpio), 2 + i, "Invalid pin");
pin[i] = pin_num[gpio];
platform_gpio_mode(gpio, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
}
int deg_per_sec = 0;
if (lua_gettop(L) >= 6) {
deg_per_sec = luaL_checkinteger(L, 6);
}
if (switec_setup(id, pin, deg_per_sec, tasknumber)) {
return luaL_error(L, "Unable to setup stepper.");
}
return 0;
}
// Lua: close( id )
static int lswitec_close( lua_State* L )
{
unsigned int id;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( switec, id );
callback_free(L, id);
if (switec_close( id )) {
return luaL_error( L, "Unable to close stepper." );
}
return 0;
}
// Lua: reset( id )
static int lswitec_reset( lua_State* L )
{
unsigned int id;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( switec, id );
if (switec_reset( id )) {
return luaL_error( L, "Unable to reset stepper." );
}
return 0;
}
// Lua: moveto( id, pos [, cb] )
static int lswitec_moveto( lua_State* L )
{
unsigned int id;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( switec, id );
int pos;
pos = luaL_checkinteger( L, 2 );
if (lua_gettop(L) >= 3) {
callback_set(L, id, 3);
} else {
callback_free(L, id);
}
if (switec_moveto( id, pos )) {
return luaL_error( L, "Unable to move stepper." );
}
return 0;
}
// Lua: getpos( id ) -> position, moving
static int lswitec_getpos( lua_State* L )
{
unsigned int id;
id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( switec, id );
int32_t pos;
int32_t dir;
int32_t target;
if (switec_getpos( id, &pos, &dir, &target )) {
return luaL_error( L, "Unable to get position." );
}
lua_pushnumber(L, pos);
lua_pushnumber(L, dir);
return 2;
}
static int lswitec_dequeue(lua_State* L)
{
int id;
for (id = 0; id < SWITEC_CHANNEL_COUNT; id++) {
if (stopped_callback[id] != LUA_NOREF) {
int32_t pos;
int32_t dir;
int32_t target;
if (!switec_getpos( id, &pos, &dir, &target )) {
if (dir == 0 && pos == target) {
callback_execute(L, id);
}
}
}
}
return 0;
}
static void lswitec_task(os_param_t param, uint8_t prio)
{
(void) param;
(void) prio;
lswitec_dequeue(lua_getstate());
}
static int switec_open(lua_State *L)
{
(void) L;
tasknumber = task_get_id(lswitec_task);
return 0;
}
// Module function map
static const LUA_REG_TYPE switec_map[] = {
{ LSTRKEY( "setup" ), LFUNCVAL( lswitec_setup ) },
{ LSTRKEY( "close" ), LFUNCVAL( lswitec_close ) },
{ LSTRKEY( "reset" ), LFUNCVAL( lswitec_reset ) },
{ LSTRKEY( "moveto" ), LFUNCVAL( lswitec_moveto) },
{ LSTRKEY( "getpos" ), LFUNCVAL( lswitec_getpos) },
#ifdef SQITEC_DEBUG
{ LSTRKEY( "dequeue" ), LFUNCVAL( lswitec_dequeue) },
#endif
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(SWITEC, "switec", switec_map, switec_open);
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_string.h"
#include "user_interface.h"
static inline uint32_t _getCycleCount(void) {
uint32_t cycles;
__asm__ __volatile__("rsr %0,ccount":"=a" (cycles));
return cycles;
}
// This algorithm reads the cpu clock cycles to calculate the correct
// pulse widths. It works in both 80 and 160 MHz mode.
static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, uint32_t length) {
uint8_t *p, *end;
p = pixels;
end = p + length;
const uint32_t t0l = (1000 * system_get_cpu_freq()) / 3333; // 0.390us (spec=0.35 +- 0.15)
const uint32_t t1l = (1000 * system_get_cpu_freq()) / 1250; // 0.800us (spec=0.70 +- 0.15)
const uint32_t ttot = (1000 * system_get_cpu_freq()) / 800; // 1.25us
while (p != end) {
register int i;
register uint8_t pixel = *p++;
ets_intr_lock();
for (i = 7; i >= 0; i--) {
register uint32_t pin_mask = 1 << pin;
// Select low time
register uint32_t tl = ((pixel >> i) & 1) ? t1l : t0l;
register uint32_t t1, t2;
register uint32_t t = _getCycleCount();
t1 = t + tl;
t2 = t + ttot;
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pin_mask); // Set pin low
while (_getCycleCount() < t1);
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pin_mask); // Set pin high
while (_getCycleCount() < t2);
}
ets_intr_unlock();
}
}
// Lua: tm1829.write(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)
// tm1829.write(3,a)
// =a.byte()
// (0,255,128)
static int ICACHE_FLASH_ATTR tm1829_write(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);
// Ignore incomplete Byte triples at the end of buffer
length -= length % 3;
// Copy payload and make sure first byte is < 0xFF (triggers
// constant current command, instead of PWM duty command)
size_t i;
for (i = 0; i < length; i += 3) {
buffer[i] = rgb[i];
buffer[i + 1] = rgb[i + 1];
buffer[i + 2] = rgb[i + 2];
// Check for first byte
if (buffer[i] == 0xff)
buffer[i] = 0xfe;
}
// Initialize the output pin and wait a bit
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(pin, 1);
// Send the buffer
tm1829_write_to_pin(pin_num[pin], (uint8_t*) buffer, length);
os_delay_us(500); // reset time
c_free(buffer);
return 0;
}
static const LUA_REG_TYPE tm1829_map[] =
{
{ LSTRKEY( "write" ), LFUNCVAL( tm1829_write) },
{ LNILKEY, LNILVAL }
};
int luaopen_tm1829(lua_State *L) {
// TODO: Make sure that the GPIO system is initialized
return 0;
}
NODEMCU_MODULE(TM1829, "tm1829", tm1829_map, luaopen_tm1829);
...@@ -703,6 +703,23 @@ static int lucg_undoScale( lua_State *L ) ...@@ -703,6 +703,23 @@ static int lucg_undoScale( lua_State *L )
} }
spi_data_type cache;
uint8_t cached;
#define CACHED_TRANSFER(dat, num) cache = cached = 0; \
while( arg > 0 ) { \
if (cached == 4) { \
platform_spi_transaction( 1, 0, 0, 32, cache, 0, 0, 0 ); \
cache = cached = 0; \
} \
cache = (cache << num*8) | dat; \
cached += num; \
arg--; \
} \
if (cached > 0) { \
platform_spi_transaction( 1, 0, 0, cached * 8, cache, 0, 0, 0 ); \
}
static int16_t ucg_com_esp8266_hw_spi(ucg_t *ucg, int16_t msg, uint16_t arg, uint8_t *data) static int16_t ucg_com_esp8266_hw_spi(ucg_t *ucg, int16_t msg, uint16_t arg, uint8_t *data)
{ {
...@@ -754,34 +771,22 @@ static int16_t ucg_com_esp8266_hw_spi(ucg_t *ucg, int16_t msg, uint16_t arg, uin ...@@ -754,34 +771,22 @@ static int16_t ucg_com_esp8266_hw_spi(ucg_t *ucg, int16_t msg, uint16_t arg, uin
break; break;
case UCG_COM_MSG_REPEAT_1_BYTE: case UCG_COM_MSG_REPEAT_1_BYTE:
while( arg > 0 ) { CACHED_TRANSFER(data[0], 1);
platform_spi_send( 1, 8, data[0] );
arg--;
}
break; break;
case UCG_COM_MSG_REPEAT_2_BYTES: case UCG_COM_MSG_REPEAT_2_BYTES:
while( arg > 0 ) { CACHED_TRANSFER((data[0] << 8) | data[1], 2);
platform_spi_send( 1, 8, data[0] );
platform_spi_send( 1, 8, data[1] );
arg--;
}
break; break;
case UCG_COM_MSG_REPEAT_3_BYTES: case UCG_COM_MSG_REPEAT_3_BYTES:
while( arg > 0 ) { while( arg > 0 ) {
platform_spi_send( 1, 8, data[0] ); platform_spi_transaction( 1, 0, 0, 24, (data[0] << 16) | (data[1] << 8) | data[2], 0, 0, 0 );
platform_spi_send( 1, 8, data[1] );
platform_spi_send( 1, 8, data[2] );
arg--; arg--;
} }
break; break;
case UCG_COM_MSG_SEND_STR: case UCG_COM_MSG_SEND_STR:
while( arg > 0 ) { CACHED_TRANSFER(*data++, 1);
platform_spi_send( 1, 8, *data++ );
arg--;
}
break; break;
case UCG_COM_MSG_SEND_CD_DATA_SEQUENCE: case UCG_COM_MSG_SEND_CD_DATA_SEQUENCE:
......
...@@ -1311,6 +1311,8 @@ static const LUA_REG_TYPE wifi_map[] = { ...@@ -1311,6 +1311,8 @@ static const LUA_REG_TYPE wifi_map[] = {
void wifi_change_default_host_name(void) void wifi_change_default_host_name(void)
{ {
uint8 opmode_temp=wifi_get_opmode();
wifi_set_opmode_current(STATION_MODE);
#ifndef WIFI_STA_HOSTNAME #ifndef WIFI_STA_HOSTNAME
char temp[32]; char temp[32];
uint8_t mac[6]; uint8_t mac[6];
...@@ -1318,7 +1320,7 @@ void wifi_change_default_host_name(void) ...@@ -1318,7 +1320,7 @@ void wifi_change_default_host_name(void)
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]); c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
wifi_sta_sethostname((const char*)temp, strlen(temp)); wifi_sta_sethostname((const char*)temp, strlen(temp));
#elif defined(WIFI_STA_HOSTNAME) && !defined(WIFI_STA_HOSTNAME_APPEND_MAC) #elif defined(WIFI_STA_HOSTNAME) && !defined(WIFI_STA_HOSTNAME_APPEND_MAC)
if(!wifi_sta_sethostname(WIFI_STA_HOSTNAME, strlen(WIFI_STA_HOSTNAME))) if(!wifi_sta_sethostname(WIFI_STA_HOSTNAME, strlen(WIFI_STA_HOSTNAME)))
{ {
char temp[32]; char temp[32];
...@@ -1335,11 +1337,14 @@ void wifi_change_default_host_name(void) ...@@ -1335,11 +1337,14 @@ void wifi_change_default_host_name(void)
c_sprintf(temp, "%s%X%X%X", WIFI_STA_HOSTNAME, (mac)[3], (mac)[4], (mac)[5]); c_sprintf(temp, "%s%X%X%X", WIFI_STA_HOSTNAME, (mac)[3], (mac)[4], (mac)[5]);
if(!wifi_sta_sethostname(temp, strlen(temp))) if(!wifi_sta_sethostname(temp, strlen(temp)))
{ {
c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]); c_sprintf(temp, "NODE-%X%X%X", (mac)[3], (mac)[4], (mac)[5]);
wifi_sta_sethostname((const char*)temp, strlen(temp)); wifi_sta_sethostname((const char*)temp, strlen(temp));
} }
#endif #endif
if(opmode_temp!=wifi_get_opmode())
{
wifi_set_opmode_current(opmode_temp);
}
} }
int luaopen_wifi( lua_State *L ) int luaopen_wifi( lua_State *L )
......
...@@ -6,8 +6,17 @@ ...@@ -6,8 +6,17 @@
#include "c_string.h" #include "c_string.h"
#include "user_interface.h" #include "user_interface.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "osapi.h"
#define CANARY_VALUE 0x32383132 #define CANARY_VALUE 0x32383132
#define MODE_SINGLE 0
#define MODE_DUAL 1
#define FADE_IN 1
#define FADE_OUT 0
#define SHIFT_LOGICAL 0
#define SHIFT_CIRCULAR 1
typedef struct { typedef struct {
int canary; int canary;
...@@ -16,15 +25,27 @@ typedef struct { ...@@ -16,15 +25,27 @@ typedef struct {
uint8_t values[0]; uint8_t values[0];
} ws2812_buffer; } ws2812_buffer;
// Init UART1 to be able to stream WS2812 data // Init UART1 to be able to stream WS2812 data to GPIO2 pin
// We use GPIO2 as output pin // If DUAL mode is selected, init UART0 to stream to TXD0 as well
static void ws2812_init() { // You HAVE to redirect LUA's output somewhere else
static void ws2812_init(lua_State* L) {
const int mode = luaL_optinteger(L, 1, MODE_SINGLE);
luaL_argcheck(L, mode == MODE_SINGLE || mode == MODE_DUAL, 1, "ws2812.SINGLE or ws2812.DUAL expected");
// Configure UART1 // Configure UART1
// Set baudrate of UART1 to 3200000 // Set baudrate of UART1 to 3200000
WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000); WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000);
// Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX // Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX
WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S)); WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S));
if (mode == MODE_DUAL) {
// Configure UART0
// Set baudrate of UART0 to 3200000
WRITE_PERI_REG(UART_CLKDIV(0), UART_CLK_FREQ / 3200000);
// Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX
WRITE_PERI_REG(UART_CONF0(0), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S));
}
// Pull GPIO2 down // Pull GPIO2 down
platform_gpio_mode(4, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); platform_gpio_mode(4, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(4, 0); platform_gpio_write(4, 0);
...@@ -39,12 +60,11 @@ static void ws2812_init() { ...@@ -39,12 +60,11 @@ static void ws2812_init() {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
} }
// Stream data using UART1 routed to GPIO2 // Stream data using UART1 routed to GPIO2
// ws2812.init() should be called first // ws2812.init() should be called first
// //
// NODE_DEBUG should not be activated because it also uses UART1 // NODE_DEBUG should not be activated because it also uses UART1
static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) { static void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted // Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00 // 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
...@@ -55,23 +75,33 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) { ...@@ -55,23 +75,33 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) {
// But declared in ".data" section to avoid read penalty from FLASH // But declared in ".data" section to avoid read penalty from FLASH
static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 }; static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
uint8_t *end = pixels + length; const uint8_t *end = pixels + length;
const uint8_t *end2 = pixels2 + length2;
do { do {
uint8_t value = *pixels++; // If something to send for first buffer and enough room
// in FIFO buffer (we wants to write 4 bytes, so less than
// Wait enough space in the FIFO buffer // 124 in the buffer)
// (Less than 124 bytes in the buffer) if (pixels < end && (((READ_PERI_REG(UART_STATUS(1)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) {
while (((READ_PERI_REG(UART_STATUS(1)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) > 124); uint8_t value = *pixels++;
// Fill the buffer // Fill the buffer
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 6) & 3]); WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 6) & 3]);
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 4) & 3]); WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 4) & 3]);
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 2) & 3]); WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 2) & 3]);
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]); WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
}
} while(pixels < end); // Same for the second buffer
if (pixels2 < end2 && (((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) {
uint8_t value = *pixels2++;
// Fill the buffer
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 6) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 4) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 2) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 0) & 3]);
}
} while(pixels < end || pixels2 < end2); // Until there is still something to send
} }
// Lua: ws2812.write("string") // Lua: ws2812.write("string")
...@@ -82,12 +112,63 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) { ...@@ -82,12 +112,63 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) {
// ws2812.write(string.char(0, 255, 0)) sets the first LED red. // ws2812.write(string.char(0, 255, 0)) sets the first LED red.
// ws2812.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue. // ws2812.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
// ws2812.write(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white. // ws2812.write(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
static int ws2812_writegrb(lua_State* L) { //
size_t length; // In DUAL mode 'ws2812.init(ws2812.DUAL)', you may pass a second string as parameter
const char *values = luaL_checklstring(L, 1, &length); // It will be sent through TXD0 in parallel
static int ws2812_write(lua_State* L) {
size_t length1, length2;
const char *buffer1, *buffer2;
// First mandatory parameter
int type = lua_type(L, 1);
if (type == LUA_TNIL)
{
buffer1 = 0;
length1 = 0;
}
else if(type == LUA_TSTRING)
{
buffer1 = lua_tolstring(L, 1, &length1);
}
else if (type == LUA_TUSERDATA)
{
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
// Send the buffer buffer1 = buffer->values;
ws2812_write((uint8_t*) values, length); length1 = buffer->colorsPerLed*buffer->size;
}
else
{
luaL_argerror(L, 1, "ws2812.buffer or string expected");
}
// Second optionnal parameter
type = lua_type(L, 2);
if (type == LUA_TNONE || type == LUA_TNIL)
{
buffer2 = 0;
length2 = 0;
}
else if (type == LUA_TSTRING)
{
buffer2 = lua_tolstring(L, 2, &length2);
}
else if (type == LUA_TUSERDATA)
{
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
buffer2 = buffer->values;
length2 = buffer->colorsPerLed*buffer->size;
}
else
{
luaL_argerror(L, 2, "ws2812.buffer or string expected");
}
// Send the buffers
ws2812_write_data(buffer1, length1, buffer2, length2);
return 0; return 0;
} }
...@@ -151,20 +232,96 @@ static int ws2812_buffer_fill(lua_State* L) { ...@@ -151,20 +232,96 @@ static int ws2812_buffer_fill(lua_State* L) {
static int ws2812_buffer_fade(lua_State* L) { static int ws2812_buffer_fade(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int fade = luaL_checkinteger(L, 2); const int fade = luaL_checkinteger(L, 2);
unsigned direction = luaL_optinteger( L, 3, FADE_OUT );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number");
uint8_t * p = &buffer->values[0]; uint8_t * p = &buffer->values[0];
int val = 0;
int i; int i;
for(i = 0; i < buffer->size * buffer->colorsPerLed; i++) for(i = 0; i < buffer->size * buffer->colorsPerLed; i++)
{ {
*p++ /= fade; if (direction == FADE_OUT)
{
*p++ /= fade;
}
else
{
// as fade in can result in value overflow, an int is used to perform the check afterwards
val = *p * fade;
if (val > 255) val = 255;
*p++ = val;
}
} }
return 0; return 0;
} }
static int ws2812_buffer_shift(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int shiftValue = luaL_checkinteger(L, 2);
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size");
int shift = shiftValue >= 0 ? shiftValue : -shiftValue;
// check if we want to shift at all
if (shift == 0)
{
return 0;
}
uint8_t * tmp_pixels = luaM_malloc(L, buffer->colorsPerLed * sizeof(uint8_t) * shift);
int i,j;
size_t shift_len, remaining_len;
// calculate length of shift section and remaining section
shift_len = shift*buffer->colorsPerLed;
remaining_len = (buffer->size-shift)*buffer->colorsPerLed;
if (shiftValue > 0)
{
// Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len);
// Move pixels to end
os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len);
// Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL)
{
c_memset(&buffer->values[0], 0, shift_len);
}
else
{
c_memcpy(&buffer->values[0], tmp_pixels, shift_len);
}
}
else
{
// Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[0], shift_len);
// Move pixels to end
os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len);
// Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL)
{
c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len);
}
else
{
c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len);
}
}
// Free memory
luaM_free(L, tmp_pixels);
return 0;
}
static int ws2812_buffer_get(lua_State* L) { static int ws2812_buffer_get(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int led = luaL_checkinteger(L, 2) - 1; const int led = luaL_checkinteger(L, 2) - 1;
...@@ -239,34 +396,29 @@ static int ws2812_buffer_size(lua_State* L) { ...@@ -239,34 +396,29 @@ static int ws2812_buffer_size(lua_State* L) {
return 1; return 1;
} }
static int ws2812_buffer_write(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
// Send the buffer
ws2812_write(buffer->values, buffer->colorsPerLed*buffer->size);
return 0;
}
static const LUA_REG_TYPE ws2812_buffer_map[] = static const LUA_REG_TYPE ws2812_buffer_map[] =
{ {
{ LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )},
{ LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )},
{ LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )},
{ LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "write" ), LFUNCVAL( ws2812_buffer_write )}, { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )},
{ LSTRKEY( "__index" ), LROVAL ( ws2812_buffer_map )}, { LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )},
{ LNILKEY, LNILVAL} { LNILKEY, LNILVAL}
}; };
static const LUA_REG_TYPE ws2812_map[] = static const LUA_REG_TYPE ws2812_map[] =
{ {
{ LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )}, { LSTRKEY( "init" ), LFUNCVAL( ws2812_init )},
{ LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )}, { LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )},
{ LSTRKEY( "init" ), LFUNCVAL( ws2812_init )}, { LSTRKEY( "write" ), LFUNCVAL( ws2812_write )},
{ LSTRKEY( "FADE_IN" ), LNUMVAL( FADE_IN ) },
{ LSTRKEY( "FADE_OUT" ), LNUMVAL( FADE_OUT ) },
{ LSTRKEY( "MODE_SINGLE" ), LNUMVAL( MODE_SINGLE ) },
{ LSTRKEY( "MODE_DUAL" ), LNUMVAL( MODE_DUAL ) },
{ LSTRKEY( "SHIFT_LOGICAL" ), LNUMVAL( SHIFT_LOGICAL ) },
{ LSTRKEY( "SHIFT_CIRCULAR" ), LNUMVAL( SHIFT_CIRCULAR ) },
{ LNILKEY, LNILVAL} { LNILKEY, LNILVAL}
}; };
......
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = pcm.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../lua
INCLUDES += -I ../libc
INCLUDES += -I ../platform
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/*
This file contains the sigma-delta driver implementation.
*/
#include "platform.h"
#include "hw_timer.h"
#include "task/task.h"
#include "c_stdlib.h"
#include "pcm.h"
static const os_param_t drv_sd_hw_timer_owner = 0x70636D; // "pcm"
static void ICACHE_RAM_ATTR drv_sd_timer_isr( os_param_t arg )
{
cfg_t *cfg = (cfg_t *)arg;
pcm_buf_t *buf = &(cfg->bufs[cfg->rbuf_idx]);
if (cfg->isr_throttled) {
return;
}
if (!buf->empty) {
uint16_t tmp;
// buffer is not empty, continue reading
tmp = abs((int16_t)(buf->data[buf->rpos]) - 128);
if (tmp > cfg->vu_peak_tmp) {
cfg->vu_peak_tmp = tmp;
}
cfg->vu_samples_tmp++;
if (cfg->vu_samples_tmp >= cfg->vu_req_samples) {
cfg->vu_peak = cfg->vu_peak_tmp;
task_post_low( cfg->data_vu_task, (os_param_t)cfg );
cfg->vu_samples_tmp = 0;
cfg->vu_peak_tmp = 0;
}
platform_sigma_delta_set_target( buf->data[buf->rpos++] );
if (buf->rpos >= buf->len) {
// buffer data consumed, request to re-fill it
buf->empty = TRUE;
cfg->fbuf_idx = cfg->rbuf_idx;
task_post_high( cfg->data_play_task, (os_param_t)cfg );
// switch to next buffer
cfg->rbuf_idx ^= 1;
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
}
} else {
// flag ISR throttled
cfg->isr_throttled = 1;
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
cfg->fbuf_idx = cfg->rbuf_idx;
task_post_high( cfg->data_play_task, (os_param_t)cfg );
}
}
static uint8_t drv_sd_stop( cfg_t *cfg )
{
platform_hw_timer_close( drv_sd_hw_timer_owner );
return TRUE;
}
static uint8_t drv_sd_close( cfg_t *cfg )
{
drv_sd_stop( cfg );
platform_sigma_delta_close( cfg->pin );
dbg_platform_gpio_mode( PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP );
return TRUE;
}
static uint8_t drv_sd_play( cfg_t *cfg )
{
// VU control: derive callback frequency
cfg->vu_req_samples = (uint16_t)((1000000L / (uint32_t)cfg->vu_freq) / (uint32_t)pcm_rate_def[cfg->rate]);
cfg->vu_samples_tmp = 0;
cfg->vu_peak_tmp = 0;
// (re)start hardware timer ISR to feed the sigma-delta
if (platform_hw_timer_init( drv_sd_hw_timer_owner, FRC1_SOURCE, TRUE )) {
platform_hw_timer_set_func( drv_sd_hw_timer_owner, drv_sd_timer_isr, (os_param_t)cfg );
platform_hw_timer_arm_us( drv_sd_hw_timer_owner, pcm_rate_def[cfg->rate] );
return TRUE;
} else {
return FALSE;
}
}
static uint8_t drv_sd_init( cfg_t *cfg )
{
dbg_platform_gpio_write( PLATFORM_GPIO_HIGH );
dbg_platform_gpio_mode( PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP );
platform_sigma_delta_setup( cfg->pin );
platform_sigma_delta_set_prescale( 9 );
return TRUE;
}
static uint8_t drv_sd_fail( cfg_t *cfg )
{
return FALSE;
}
const drv_t pcm_drv_sd = {
.init = drv_sd_init,
.close = drv_sd_close,
.play = drv_sd_play,
.record = drv_sd_fail,
.stop = drv_sd_stop
};
#ifndef _PCM_H
#define _PCM_H
#include "platform.h"
//#define DEBUG_PIN 2
#ifdef DEBUG_PIN
# define dbg_platform_gpio_write(level) platform_gpio_write( DEBUG_PIN, level )
# define dbg_platform_gpio_mode(mode, pull) platform_gpio_mode( DEBUG_PIN, mode, pull)
#else
# define dbg_platform_gpio_write(level)
# define dbg_platform_gpio_mode(mode, pull)
#endif
#define BASE_RATE 1000000
enum pcm_driver_index {
PCM_DRIVER_SD = 0,
PCM_DRIVER_END = 1
};
enum pcm_rate_index {
PCM_RATE_1K = 0,
PCM_RATE_2K = 1,
PCM_RATE_4K = 2,
PCM_RATE_5K = 3,
PCM_RATE_8K = 4,
PCM_RATE_10K = 5,
PCM_RATE_12K = 6,
PCM_RATE_16K = 7,
};
static const uint16_t pcm_rate_def[] = {BASE_RATE / 1000, BASE_RATE / 2000, BASE_RATE / 4000,
BASE_RATE / 5000, BASE_RATE / 8000, BASE_RATE / 10000,
BASE_RATE / 12000, BASE_RATE / 16000};
typedef struct {
// available bytes in buffer
size_t len;
// next read position
size_t rpos;
// empty flag
uint8_t empty;
// allocated size
size_t buf_size;
uint8_t *data;
} pcm_buf_t;
typedef struct {
// selected sample rate
int rate;
// ISR throttle control
// -1 = ISR and data task throttled
// 1 = ISR throttled
// 0 = all running
sint8_t isr_throttled;
// buffer selectors
uint8_t rbuf_idx; // read by ISR
uint8_t fbuf_idx; // fill by data task
// task handles
task_handle_t data_vu_task, data_play_task, start_play_task;
// callback fn refs
int self_ref;
int cb_data_ref, cb_drained_ref, cb_paused_ref, cb_stopped_ref, cb_vu_ref;
// data buffers
pcm_buf_t bufs[2];
// vu measuring
uint8_t vu_freq;
uint16_t vu_req_samples, vu_samples_tmp;
uint16_t vu_peak_tmp, vu_peak;
// sigma-delta: output pin
int pin;
} cfg_t;
typedef uint8_t (*drv_fn_t)(cfg_t *);
typedef struct {
drv_fn_t init;
drv_fn_t close;
drv_fn_t play;
drv_fn_t record;
drv_fn_t stop;
} drv_t;
typedef struct {
cfg_t cfg;
const drv_t *drv;
} pud_t;
void pcm_data_vu_task( task_param_t param, uint8 prio );
void pcm_data_play_task( task_param_t param, uint8 prio );
#endif /* _PCM_H */
/*
This file contains core routines for the PCM sub-system.
pcm_data_play_task()
Triggered by the driver ISR when further data for play mode is required.
It handles the play buffer allocation and forwards control to 'data' and 'drained'
callbacks in Lua land.
pcm_data_rec_task() - n/a yet
Triggered by the driver ISR when data for record mode is available.
*/
#include "lauxlib.h"
#include "task/task.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "pcm.h"
static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
{
if (cb_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
lua_call( L, 1, returns );
}
}
void pcm_data_vu_task( task_param_t param, uint8 prio )
{
cfg_t *cfg = (cfg_t *)param;
lua_State *L = lua_getstate();
if (cfg->cb_vu_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, cfg->cb_vu_ref );
lua_rawgeti( L, LUA_REGISTRYINDEX, cfg->self_ref );
lua_pushnumber( L, (LUA_NUMBER)(cfg->vu_peak) );
lua_call( L, 2, 0 );
}
}
void pcm_data_play_task( task_param_t param, uint8 prio )
{
cfg_t *cfg = (cfg_t *)param;
pcm_buf_t *buf = &(cfg->bufs[cfg->fbuf_idx]);
size_t string_len;
const char *data = NULL;
uint8_t need_pop = FALSE;
lua_State *L = lua_getstate();
// retrieve new data from callback
if ((cfg->isr_throttled >= 0) &&
(cfg->cb_data_ref != LUA_NOREF)) {
dispatch_callback( L, cfg->self_ref, cfg->cb_data_ref, 1 );
need_pop = TRUE;
if (lua_type( L, -1 ) == LUA_TSTRING) {
data = lua_tolstring( L, -1, &string_len );
if (string_len > buf->buf_size) {
uint8_t *new_data = (uint8_t *) c_malloc( string_len );
if (new_data) {
if (buf->data) c_free( buf->data );
buf->buf_size = string_len;
buf->data = new_data;
}
}
}
}
if (data) {
size_t to_copy = string_len > buf->buf_size ? buf->buf_size : string_len;
c_memcpy( buf->data, data, to_copy );
buf->rpos = 0;
buf->len = to_copy;
buf->empty = FALSE;
dbg_platform_gpio_write( PLATFORM_GPIO_HIGH );
lua_pop( L, 1 );
if (cfg->isr_throttled > 0) {
uint8_t other_buf = cfg->fbuf_idx ^ 1;
if (cfg->bufs[other_buf].empty) {
// rerun data callback to get next buffer chunk
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
cfg->fbuf_idx = other_buf;
pcm_data_play_task( param, 0 );
}
// unthrottle ISR
cfg->isr_throttled = 0;
}
} else {
dbg_platform_gpio_write( PLATFORM_GPIO_HIGH );
if (need_pop) lua_pop( L, 1 );
if (cfg->isr_throttled > 0) {
// ISR found no further data
// this was the last invocation of the reader task, fire drained cb
cfg->isr_throttled = -1;
dispatch_callback( L, cfg->self_ref, cfg->cb_drained_ref, 0 );
}
}
}
#ifndef _PCM_DRV_H
#define _PCM_DRV_H
extern const drv_t pcm_drv_sd;
#endif /* _PCM_DRV_H */
#ifndef __CPU_ESP8266_H__ #ifndef __CPU_ESP8266_H__
#define __CPU_ESP8266_H__ #define __CPU_ESP8266_H__
#ifndef NO_CPU_ESP8266_INCLUDE
#include "os_type.h" #include "os_type.h"
#include "spi_flash.h" #include "spi_flash.h"
#include "pin_map.h" #include "pin_map.h"
#include "user_config.h" #include "user_config.h"
#include "flash_api.h" #include "flash_api.h"
#endif
// Number of resources (0 if not available/not implemented) // Number of resources (0 if not available/not implemented)
#define NUM_GPIO GPIO_PIN_NUM #define NUM_GPIO GPIO_PIN_NUM
#define NUM_SPI 2 #define NUM_SPI 2
...@@ -38,7 +40,8 @@ ...@@ -38,7 +40,8 @@
#else #else
#define FLASH_SEC_NUM 0x80 #define FLASH_SEC_NUM 0x80
#endif #endif
#define SYS_PARAM_SEC_NUM 4 // SDK 1.5.4.1 added 1 sector for rf_cal
#define SYS_PARAM_SEC_NUM 5
#define SYS_PARAM_SEC_START (FLASH_SEC_NUM - SYS_PARAM_SEC_NUM) #define SYS_PARAM_SEC_START (FLASH_SEC_NUM - SYS_PARAM_SEC_NUM)
#define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE #define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment