diff --git a/app/modules/node.c b/app/modules/node.c index 7a3af5fd..53d59ae0 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -25,11 +25,14 @@ #include "flash_fs.h" #include "user_version.h" +#define CPU80MHZ 80 +#define CPU160MHZ 160 + // Lua: restart() static int node_restart( lua_State* L ) { system_restart(); - return 0; + return 0; } // Lua: dsleep( us, option ) @@ -56,7 +59,7 @@ static int node_deepsleep( lua_State* L ) else system_deep_sleep( us ); } - return 0; + return 0; } // Lua: dsleep_set_options @@ -80,14 +83,14 @@ static int node_info( lua_State* L ) lua_pushinteger(L, NODE_VERSION_REVISION); lua_pushinteger(L, system_get_chip_id()); // chip id lua_pushinteger(L, spi_flash_get_id()); // flash id - #if defined(FLASH_SAFE_API) +#if defined(FLASH_SAFE_API) lua_pushinteger(L, flash_safe_get_size_byte() / 1024); // flash size in KB - #else +#else lua_pushinteger(L, flash_rom_get_size_byte() / 1024); // flash size in KB - #endif // defined(FLASH_SAFE_API) +#endif // defined(FLASH_SAFE_API) lua_pushinteger(L, flash_rom_get_mode()); lua_pushinteger(L, flash_rom_get_speed()); - return 8; + return 8; } // Lua: chipid() @@ -95,7 +98,7 @@ static int node_chipid( lua_State* L ) { uint32_t id = system_get_chip_id(); lua_pushinteger(L, id); - return 1; + return 1; } // Lua: readvdd33() static int node_readvdd33( lua_State* L ) @@ -110,28 +113,23 @@ static int node_flashid( lua_State* L ) { uint32_t id = spi_flash_get_id(); lua_pushinteger( L, id ); - return 1; + return 1; } // Lua: flashsize() static int node_flashsize( lua_State* L ) { - //uint32_t sz = 0; - //if(lua_type(L, 1) == LUA_TNUMBER) - //{ - // sz = luaL_checkinteger(L, 1); - // if(sz > 0) - // { - // flash_set_size_byte(sz); - // } - //} + if (lua_type(L, 1) == LUA_TNUMBER) + { + flash_rom_set_size_byte(luaL_checkinteger(L, 1)); + } #if defined(FLASH_SAFE_API) uint32_t sz = flash_safe_get_size_byte(); #else uint32_t sz = flash_rom_get_size_byte(); #endif // defined(FLASH_SAFE_API) lua_pushinteger( L, sz ); - return 1; + return 1; } // Lua: heap() @@ -139,7 +137,7 @@ static int node_heap( lua_State* L ) { uint32_t sz = system_get_free_heap_size(); lua_pushinteger(L, sz); - return 1; + return 1; } static lua_State *gL = NULL; @@ -154,7 +152,7 @@ static int node_led( lua_State* L ) if ( lua_isnumber(L, 1) ) { low = lua_tointeger(L, 1); - if ( low < 0 ){ + if ( low < 0 ) { return luaL_error( L, "wrong arg type" ); } } else { @@ -163,7 +161,7 @@ static int node_led( lua_State* L ) if ( lua_isnumber(L, 2) ) { high = lua_tointeger(L, 2); - if ( high < 0 ){ + if ( high < 0 ) { return luaL_error( L, "wrong arg type" ); } } else { @@ -171,14 +169,14 @@ static int node_led( lua_State* L ) } led_high_count = (uint32_t)high / READLINE_INTERVAL; led_low_count = (uint32_t)low / READLINE_INTERVAL; - return 0; + return 0; } static int long_key_ref = LUA_NOREF; static int short_key_ref = LUA_NOREF; -void default_long_press(void *arg){ - if(led_high_count == 12 && led_low_count == 12){ +void default_long_press(void *arg) { + if (led_high_count == 12 && led_low_count == 12) { led_low_count = led_high_count = 6; } else { led_low_count = led_high_count = 12; @@ -188,32 +186,32 @@ void default_long_press(void *arg){ // NODE_DBG("default_long_press is called. hc: %d, lc: %d\n", led_high_count, led_low_count); } -void default_short_press(void *arg){ +void default_short_press(void *arg) { system_restart(); } -void key_long_press(void *arg){ +void key_long_press(void *arg) { NODE_DBG("key_long_press is called.\n"); - if(long_key_ref == LUA_NOREF){ + if (long_key_ref == LUA_NOREF) { default_long_press(arg); return; } - if(!gL) + if (!gL) return; lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref); lua_call(gL, 0, 0); } -void key_short_press(void *arg){ +void key_short_press(void *arg) { NODE_DBG("key_short_press is called.\n"); - if(short_key_ref == LUA_NOREF){ + if (short_key_ref == LUA_NOREF) { default_short_press(arg); return; } - if(!gL) + if (!gL) return; lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref); - lua_call(gL, 0, 0); + lua_call(gL, 0, 0); } // Lua: key(type, function) @@ -221,32 +219,32 @@ static int node_key( lua_State* L ) { int *ref = NULL; size_t sl; - + const char *str = luaL_checklstring( L, 1, &sl ); if (str == NULL) return luaL_error( L, "wrong arg type" ); - if(sl == 5 && c_strcmp(str, "short") == 0){ + if (sl == 5 && c_strcmp(str, "short") == 0) { ref = &short_key_ref; - }else if(sl == 4 && c_strcmp(str, "long") == 0){ + } else if (sl == 4 && c_strcmp(str, "long") == 0) { ref = &long_key_ref; - }else{ + } else { ref = &short_key_ref; } gL = L; // luaL_checkanyfunction(L, 2); - if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) { lua_pushvalue(L, 2); // copy argument (func) to the top of stack - if(*ref != LUA_NOREF) + if (*ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, *ref); *ref = luaL_ref(L, LUA_REGISTRYINDEX); } else { // unref the key press function - if(*ref != LUA_NOREF) + if (*ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, *ref); - *ref = LUA_NOREF; + *ref = LUA_NOREF; } - return 0; + return 0; } #endif @@ -256,15 +254,15 @@ extern void dojob(lua_Load *load); // Lua: input("string") static int node_input( lua_State* L ) { - size_t l=0; + size_t l = 0; const char *s = luaL_checklstring(L, 1, &l); if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1) { lua_Load *load = &gLoad; - if(load->line_position == 0){ + if (load->line_position == 0) { c_memcpy(load->line, s, l); - load->line[l+1] = '\0'; - load->line_position = c_strlen(load->line)+1; + load->line[l + 1] = '\0'; + load->line_position = c_strlen(load->line) + 1; load->done = 1; NODE_DBG("Get command:\n"); NODE_DBG(load->line); // buggy here @@ -279,18 +277,18 @@ static int node_input( lua_State* L ) static int output_redir_ref = LUA_NOREF; static int serial_debug = 1; -void output_redirect(const char *str){ +void output_redirect(const char *str) { // if(c_strlen(str)>=TX_BUFF_SIZE){ // NODE_ERR("output too long.\n"); // return; // } - if(output_redir_ref == LUA_NOREF || !gL){ + if (output_redir_ref == LUA_NOREF || !gL) { uart0_sendStr(str); return; } - if(serial_debug!=0){ + if (serial_debug != 0) { uart0_sendStr(str); } @@ -304,15 +302,15 @@ static int node_output( lua_State* L ) { gL = L; // luaL_checkanyfunction(L, 1); - if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){ + if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) { lua_pushvalue(L, 1); // copy argument (func) to the top of stack - if(output_redir_ref != LUA_NOREF) + if (output_redir_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX); } else { // unref the key press function - if(output_redir_ref != LUA_NOREF) + if (output_redir_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); - output_redir_ref = LUA_NOREF; + output_redir_ref = LUA_NOREF; serial_debug = 1; return 0; } @@ -320,26 +318,26 @@ static int node_output( lua_State* L ) if ( lua_isnumber(L, 2) ) { serial_debug = lua_tointeger(L, 2); - if(serial_debug!=0) + if (serial_debug != 0) serial_debug = 1; } else { serial_debug = 1; // default to 1 } - return 0; + return 0; } static int writer(lua_State* L, const void* p, size_t size, void* u) { UNUSED(L); int file_fd = *( (int *)u ); - if((FS_OPEN_OK - 1)==file_fd) + if ((FS_OPEN_OK - 1) == file_fd) return 1; - NODE_DBG("get fd:%d,size:%d\n",file_fd,size); - - if(size!=0 && (size!=fs_write(file_fd, (const char *)p, size)) ) + NODE_DBG("get fd:%d,size:%d\n", file_fd, size); + + if (size != 0 && (size != fs_write(file_fd, (const char *)p, size)) ) return 1; - NODE_DBG("write fd:%d,size:%d\n",file_fd,size); + NODE_DBG("write fd:%d,size:%d\n", file_fd, size); return 0; } @@ -351,55 +349,73 @@ static int node_compile( lua_State* L ) int file_fd = FS_OPEN_OK - 1; size_t 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"); char output[FS_NAME_MAX_LENGTH]; c_strcpy(output, fname); // check here that filename end with ".lua". - if(len<4 || (c_strcmp( output+len-4,".lua")!=0) ) + if (len < 4 || (c_strcmp( output + len - 4, ".lua") != 0) ) return luaL_error(L, "not a .lua file"); - output[c_strlen(output)-2] = 'c'; - output[c_strlen(output)-1] = '\0'; + output[c_strlen(output) - 2] = 'c'; + output[c_strlen(output) - 1] = '\0'; NODE_DBG(output); NODE_DBG("\n"); - if (luaL_loadfsfile(L,fname)!=0){ - return luaL_error(L, lua_tostring(L,-1)); + if (luaL_loadfsfile(L, fname) != 0) { + return luaL_error(L, lua_tostring(L, -1)); } - f = toproto(L,-1); + f = toproto(L, -1); int stripping = 1; /* strip debug information? */ file_fd = fs_open(output, fs_mode2flag("w+")); - if(file_fd < FS_OPEN_OK) + if (file_fd < FS_OPEN_OK) { return luaL_error(L, "cannot open/write to file"); } lua_lock(L); - int result=luaU_dump(L,f,writer,&file_fd,stripping); + int result = luaU_dump(L, f, writer, &file_fd, stripping); lua_unlock(L); fs_flush(file_fd); fs_close(file_fd); file_fd = FS_OPEN_OK - 1; - if (result==LUA_ERR_CC_INTOVERFLOW){ + if (result == LUA_ERR_CC_INTOVERFLOW) { return luaL_error(L, "value too big or small for target integer type"); } - if (result==LUA_ERR_CC_NOTINTEGER){ + if (result == LUA_ERR_CC_NOTINTEGER) { return luaL_error(L, "target lua_Number is integral but fractional value found"); } return 0; } +// Lua: setcpufreq(mhz) +// mhz is either CPU80MHZ od CPU160MHZ +static int node_setcpufreq(lua_State* L) +{ + // http://www.esp8266.com/viewtopic.php?f=21&t=1369 + uint32_t new_freq = luaL_checkinteger(L, 1); + if (new_freq == CPU160MHZ){ + REG_SET_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(CPU160MHZ); + } else { + REG_CLR_BIT(0x3ff00014, BIT(0)); + os_update_cpu_frequency(CPU80MHZ); + } + new_freq = ets_get_cpu_frequency(); + lua_pushinteger(L, new_freq); + return 1; +} + // Module function map #define MIN_OPT_LEVEL 2 #include "lrodefs.h" -const LUA_REG_TYPE node_map[] = +const LUA_REG_TYPE node_map[] = { { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, @@ -416,7 +432,10 @@ const LUA_REG_TYPE node_map[] = { LSTRKEY( "output" ), LFUNCVAL( node_output ) }, { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, { LSTRKEY( "compile" ), LFUNCVAL( node_compile) }, -// Combined to dsleep(us, option) + { LSTRKEY( "CPU80MHZ" ), LNUMVAL( CPU80MHZ ) }, + { LSTRKEY( "CPU160MHZ" ), LNUMVAL( CPU160MHZ ) }, + { LSTRKEY( "setcpufreq" ), LFUNCVAL( node_setcpufreq) }, +// Combined to dsleep(us, option) // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, #if LUA_OPTIMIZE_MEMORY > 0 @@ -433,5 +452,5 @@ LUALIB_API int luaopen_node( lua_State *L ) // Add constants return 1; -#endif // #if LUA_OPTIMIZE_MEMORY > 0 +#endif // #if LUA_OPTIMIZE_MEMORY > 0 } diff --git a/app/platform/flash_api.c b/app/platform/flash_api.c index 6af6e31c..a648d91b 100644 --- a/app/platform/flash_api.c +++ b/app/platform/flash_api.c @@ -149,27 +149,21 @@ bool flash_rom_set_size_type(uint8_t size) // Dangerous, here are dinosaur infested!!!!! // Reboot required!!! // If you don't know what you're doing, your nodemcu may turn into stone ... -#if defined(FLASH_SAFE_API) + NODE_DBG("\nBEGIN SET FLASH HEADER\n"); uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; - flash_safe_read(0, (uint32 *)data, sizeof(data)); - SPIFlashInfo *p_spi_flash_info = (SPIFlashInfo *)(data); - p_spi_flash_info->size = size; - flash_safe_erase_sector(0); - flash_safe_write(0, (uint32 *)data, sizeof(data)); - // TODO: CHECKSUM Firmware - //p_spi_flash_info = flash_rom_getinfo(); - //p_spi_flash_info->size = size; -#else - uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; - spi_flash_read(0, (uint32 *)data, sizeof(data)); - SPIFlashInfo *p_spi_flash_info = (SPIFlashInfo *)(data); - p_spi_flash_info->size = size; - spi_flash_erase_sector(0); - spi_flash_write(0, (uint32 *)data, sizeof(data)); - // TODO: CHECKSUM Firmware - //p_spi_flash_info = flash_rom_getinfo(); - //p_spi_flash_info->size = size; -#endif // defined(FLASH_SAFE_API) + if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) + { + ((SPIFlashInfo *)(&data[0]))->size = size; + if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE)) + { + NODE_DBG("\nERASE SUCCESS\n"); + } + if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) + { + NODE_DBG("\nWRITE SUCCESS, %u\n", size); + } + } + NODE_DBG("\nEND SET FLASH HEADER\n"); return true; } @@ -207,6 +201,16 @@ bool flash_rom_set_size_byte(uint32_t size) flash_size = SIZE_32MBIT; flash_rom_set_size_type(flash_size); break; + case 8 * 1024 * 1024: + // 64Mbit, 8MByte + flash_size = SIZE_64MBIT; + flash_rom_set_size_type(flash_size); + break; + case 16 * 1024 * 1024: + // 128Mbit, 16MByte + flash_size = SIZE_128MBIT; + flash_rom_set_size_type(flash_size); + break; default: // Unknown flash size. result = false; @@ -272,6 +276,46 @@ uint32_t flash_rom_get_speed(void) return speed; } +bool flash_rom_set_speed(uint32_t speed) +{ + // Dangerous, here are dinosaur infested!!!!! + // Reboot required!!! + // If you don't know what you're doing, your nodemcu may turn into stone ... + NODE_DBG("\nBEGIN SET FLASH HEADER\n"); + uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; + uint8_t speed_type = SPEED_40MHZ; + if (speed < 26700000) + { + speed_type = SPEED_20MHZ; + } + else if (speed < 40000000) + { + speed_type = SPEED_26MHZ; + } + else if (speed < 80000000) + { + speed_type = SPEED_40MHZ; + } + else if (speed >= 80000000) + { + speed_type = SPEED_80MHZ; + } + if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) + { + ((SPIFlashInfo *)(&data[0]))->speed = speed_type; + if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE)) + { + NODE_DBG("\nERASE SUCCESS\n"); + } + if (SPI_FLASH_RESULT_OK == spi_flash_write(0, (uint32 *)data, SPI_FLASH_SEC_SIZE)) + { + NODE_DBG("\nWRITE SUCCESS, %u\n", speed_type); + } + } + NODE_DBG("\nEND SET FLASH HEADER\n"); + return true; +} + bool flash_init_data_written(void) { // FLASH SEC - 4 @@ -356,3 +400,28 @@ uint8_t byte_of_aligned_array(const uint8_t *aligned_array, uint32_t index) uint8_t *p = (uint8_t *) (&v); return p[ (index % 4) ]; } + +// uint8_t flash_rom_get_checksum(void) +// { +// // SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR = flash_rom_getinfo(); +// // uint32_t address = sizeof(spi_flash_info) + spi_flash_info.segment_size; +// // uint32_t address_aligned_4bytes = (address + 3) & 0xFFFFFFFC; +// // uint8_t buffer[64] = {0}; +// // spi_flash_read(address, (uint32 *) buffer, 64); +// // uint8_t i = 0; +// // c_printf("\nBEGIN DUMP\n"); +// // for (i = 0; i < 64; i++) +// // { +// // c_printf("%02x," , buffer[i]); +// // } +// // i = (address + 0x10) & 0x10 - 1; +// // c_printf("\nSIZE:%d CHECK SUM:%02x\n", spi_flash_info.segment_size, buffer[i]); +// // c_printf("\nEND DUMP\n"); +// // return buffer[0]; +// return 0; +// } + +// uint8_t flash_rom_calc_checksum(void) +// { +// return 0; +// } \ No newline at end of file diff --git a/app/platform/flash_api.h b/app/platform/flash_api.h index f912be41..ff276a83 100644 --- a/app/platform/flash_api.h +++ b/app/platform/flash_api.h @@ -16,10 +16,10 @@ #define FLASH_SIZE_256KBYTE (FLASH_SIZE_2MBIT / 8) #define FLASH_SIZE_512KBYTE (FLASH_SIZE_4MBIT / 8) -#define FLASH_SIZE_1MBYTE (FLASH_SIZE_8MBIT / 8) +#define FLASH_SIZE_1MBYTE (FLASH_SIZE_8MBIT / 8) #define FLASH_SIZE_2MBYTE (FLASH_SIZE_16MBIT / 8) #define FLASH_SIZE_4MBYTE (FLASH_SIZE_32MBIT / 8) -#define FLASH_SIZE_8MBYTE (FLASH_SIZE_64MBIT / 8) +#define FLASH_SIZE_8MBYTE (FLASH_SIZE_64MBIT / 8) #define FLASH_SIZE_16MBYTE (FLASH_SIZE_128MBIT/ 8) #define FLASH_SAFEMODE_ENTER() \ @@ -57,8 +57,8 @@ do { \ typedef struct { - uint8_t magic_e9; - uint8_t segments; + uint8_t header_magic; + uint8_t segment_count; enum { MODE_QIO = 0, @@ -83,6 +83,9 @@ typedef struct SIZE_64MBIT = 5, SIZE_128MBIT = 6, } size : 4; + uint32_t entry_point; + uint32_t memory_offset; + uint32_t segment_size; } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo; uint32_t flash_detect_size_byte(void); @@ -104,5 +107,7 @@ bool flash_init_data_default(void); bool flash_init_data_blank(void); bool flash_self_destruct(void); uint8_t byte_of_aligned_array(const uint8_t* aligned_array, uint32_t index); +// uint8_t flash_rom_get_checksum(void); +// uint8_t flash_rom_calc_checksum(void); #endif // __FLASH_API_H__ diff --git a/app/user/user_main.c b/app/user/user_main.c index 7d470e2c..dee11754 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -60,7 +60,19 @@ void nodemcu_init(void) NODE_DBG("Can not init platform for modules.\n"); return; } - + +#if defined(FLASH_SAFE_API) + if( flash_safe_get_size_byte() != flash_rom_get_size_byte()) { + NODE_ERR("Self adjust flash size.\n"); + // Fit hardware real flash size. + flash_rom_set_size_byte(flash_safe_get_size_byte()); + // Flash init data at FLASHSIZE - 0x04000 Byte. + flash_init_data_default(); + // Flash blank data at FLASHSIZE - 0x02000 Byte. + flash_init_data_blank(); + } +#endif // defined(FLASH_SAFE_API) + if( !flash_init_data_written() ){ NODE_ERR("Restore init data.\n"); // Flash init data at FLASHSIZE - 0x04000 Byte. diff --git a/lua_examples/email/read_email_imap.lua b/lua_examples/email/read_email_imap.lua new file mode 100644 index 00000000..f64e5c31 --- /dev/null +++ b/lua_examples/email/read_email_imap.lua @@ -0,0 +1,120 @@ +--- +-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc +-- @author Miguel (AllAboutEE.com) +-- @description This example will read the first email in your inbox using IMAP and +-- display it through serial. The email server must provided unecrypted access. The code +-- was tested with an AOL and Time Warner cable email accounts (GMail and other services who do +-- not support no SSL access will not work). + +require("imap") + +local IMAP_USERNAME = "email@domain.com" +local IMAP_PASSWORD = "password" + +-- find out your unencrypted imap server and port +-- from your email provided i.e. google "[my email service] imap settings" for example +local IMAP_SERVER = "imap.service.com" +local IMAP_PORT = "143" + +local IMAP_TAG = "t1" -- You do not need to change this +local IMAP_DEBUG = true -- change to true if you would like to see the entire conversation between + -- the ESP8266 and IMAP server + +local SSID = "ssid" +local SSID_PASSWORD = "password" + + +local count = 0 -- we will send several IMAP commands/requests, this variable helps keep track of which one to send + +-- configure the ESP8266 as a station +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + +-- create an unencrypted connection +local imap_socket = net.createConnection(net.TCP,0) + + +--- +-- @name setup +-- @description A call back function used to begin reading email +-- upon sucessfull connection to the IMAP server +function setup(sck) + -- Set the email user name and password, IMAP tag, and if debugging output is needed + imap.config(IMAP_USERNAME, + IMAP_PASSWORD, + IMAP_TAG, + IMAP_DEBUG) + + imap.login(sck) +end + +imap_socket:on("connection",setup) -- call setup() upon connection +imap_socket:connect(IMAP_PORT,IMAP_SERVER) -- connect to the IMAP server + +local subject = "" +local from = "" +local message = "" + +--- +-- @name do_next +-- @description A call back function for a timer alarm used to check if the previous +-- IMAP command reply has been processed. If the IMAP reply has been processed +-- this function will call the next IMAP command function necessary to read the email +function do_next() + + -- Check if the IMAP reply was processed + if(imap.response_processed() == true) then + + -- The IMAP reply was processed + + if (count == 0) then + -- After logging in we need to select the email folder from which we wish to read + -- in this case the INBOX folder + imap.examine(imap_socket,"INBOX") + count = count + 1 + elseif (count == 1) then + -- After examining/selecting the INBOX folder we can begin to retrieve emails. + imap.fetch_header(imap_socket,imap.get_most_recent_num(),"SUBJECT") -- Retrieve the SUBJECT of the first/newest email + count = count + 1 + elseif (count == 2) then + subject = imap.get_header() -- store the SUBJECT response in subject + imap.fetch_header(imap_socket,imap.get_most_recent_num(),"FROM") -- Retrieve the FROM of the first/newest email + count = count + 1 + elseif (count == 3) then + from = imap.get_header() -- store the FROM response in from + imap.fetch_body_plain_text(imap_socket,imap.get_most_recent_num()) -- Retrieve the BODY of the first/newest email + count = count + 1 + elseif (count == 4) then + body = imap.get_body() -- store the BODY response in body + imap.logout(imap_socket) -- Logout of the email account + count = count + 1 + else + -- display the email contents + + -- create patterns to strip away IMAP protocl text from actual message + pattern1 = "(\*.+\}\r\n)" -- to remove "* n command (BODY[n] {n}" + pattern2 = "(%)\r\n.+)" -- to remove ") t1 OK command completed" + + from = string.gsub(from,pattern1,"") + from = string.gsub(from,pattern2,"") + print(from) + + subject = string.gsub(subject,pattern1,"") + subject = string.gsub(subject,pattern2,"") + print(subject) + + body = string.gsub(body,pattern1,"") + body = string.gsub(body,pattern2,"") + print("Message: " .. body) + + tmr.stop(0) -- Stop the timer alarm + imap_socket:close() -- close the IMAP socket + collectgarbage() -- clean up + end + end + +end + +-- A timer alarm is sued to check if an IMAP reply has been processed +tmr.alarm(0,1000,1, do_next) diff --git a/lua_examples/email/send_email_smtp.lua b/lua_examples/email/send_email_smtp.lua new file mode 100644 index 00000000..646b4ff6 --- /dev/null +++ b/lua_examples/email/send_email_smtp.lua @@ -0,0 +1,129 @@ +--- +-- Working Example: https://www.youtube.com/watch?v=CcRbFIJ8aeU +-- @description a basic SMTP email example. You must use an account which can provide unencrypted authenticated access. +-- This example was tested with an AOL and Time Warner email accounts. GMail does not offer unecrypted authenticated access. +-- To obtain your email's SMTP server and port simply Google it e.g. [my email domain] SMTP settings +-- For example for timewarner you'll get to this page http://www.timewarnercable.com/en/support/faqs/faqs-internet/e-mailacco/incoming-outgoing-server-addresses.html +-- To Learn more about SMTP email visit: +-- SMTP Commands Reference - http://www.samlogic.net/articles/smtp-commands-reference.htm +-- See "SMTP transport example" in this page http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol +-- @author Miguel + +require("base64") + +-- The email and password from the account you want to send emails from +local MY_EMAIL = "esp8266@domain.com" +local EMAIL_PASSWORD = "123456" + +-- The SMTP server and port of your email provider. +-- If you don't know it google [my email provider] SMTP settings +local SMTP_SERVER = "smtp.server.com" +local SMTP_PORT = "587" + +-- The account you want to send email to +local mail_to = "to_email@domain.com" + +-- Your access point's SSID and password +local SSID = "ssid" +local SSID_PASSWORD = "password" + +-- configure ESP as a station +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + +-- These are global variables. Don't change their values +-- they will be changed in the functions below +local email_subject = "" +local email_body = "" +local count = 0 + + +local smtp_socket = nil -- will be used as socket to email server + +-- The display() function will be used to print the SMTP server's response +function display(sck,response) + print(response) +end + +-- The do_next() function is used to send the SMTP commands to the SMTP server in the required sequence. +-- I was going to use socket callbacks but the code would not run callbacks after the first 3. +function do_next() + if(count == 0)then + count = count+1 + local IP_ADDRESS = wifi.sta.getip() + smtp_socket:send("HELO "..IP_ADDRESS.."\r\n") + elseif(count==1) then + count = count+1 + smtp_socket:send("AUTH LOGIN\r\n") + elseif(count == 2) then + count = count + 1 + smtp_socket:send(base64.enc(MY_EMAIL).."\r\n") + elseif(count == 3) then + count = count + 1 + smtp_socket:send(base64.enc(EMAIL_PASSWORD).."\r\n") + elseif(count==4) then + count = count+1 + smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n") + elseif(count==5) then + count = count+1 + smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n") + elseif(count==6) then + count = count+1 + smtp_socket:send("DATA\r\n") + elseif(count==7) then + count = count+1 + local message = string.gsub( + "From: \"".. MY_EMAIL .."\"<"..MY_EMAIL..">\r\n" .. + "To: \"".. mail_to .. "\"<".. mail_to..">\r\n".. + "Subject: ".. email_subject .. "\r\n\r\n" .. + email_body,"\r\n.\r\n","") + + smtp_socket:send(message.."\r\n.\r\n") + elseif(count==8) then + count = count+1 + tmr.stop(0) + smtp_socket:send("QUIT\r\n") + else + smtp_socket:close() + end +end + +-- The connectted() function is executed when the SMTP socket is connected to the SMTP server. +-- This function will create a timer to call the do_next function which will send the SMTP commands +-- in sequence, one by one, every 5000 seconds. +-- You can change the time to be smaller if that works for you, I used 5000ms just because. +function connected(sck) + tmr.alarm(0,5000,1,do_next) +end + +-- @name send_email +-- @description Will initiated a socket connection to the SMTP server and trigger the connected() function +-- @param subject The email's subject +-- @param body The email's body +function send_email(subject,body) + count = 0 + email_subject = subject + email_body = body + smtp_socket = net.createConnection(net.TCP,0) + smtp_socket:on("connection",connected) + smtp_socket:on("receive",display) + smtp_socket:connect(SMTP_PORT,SMTP_SERVER) +end + +-- Send an email +send_email( + "ESP8266", +[[Hi, +How are your IoT projects coming along? +Best Wishes, +ESP8266]]) + + + + + + + + + diff --git a/lua_modules/base64/base64.lua b/lua_modules/base64/base64.lua new file mode 100644 index 00000000..e3523a31 --- /dev/null +++ b/lua_modules/base64/base64.lua @@ -0,0 +1,41 @@ +-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss +-- licensed under the terms of the LGPL2 + +local moduleName = ... +local M = {} +_G[moduleName] = M + +-- character table string +local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function M.enc(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return b:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function M.dec(data) + data = string.gsub(data, '[^'..b..'=]', '') + return (data:gsub('.', function(x) + if (x == '=') then return '' end + local r,f='',(b:find(x)-1) + for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end + return r; + end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) + if (#x ~= 8) then return '' end + local c=0 + for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(7-i) or 0) end + return string.char(c) + end)) +end + +return M diff --git a/lua_modules/email/imap.lua b/lua_modules/email/imap.lua new file mode 100644 index 00000000..e18e0d12 --- /dev/null +++ b/lua_modules/email/imap.lua @@ -0,0 +1,205 @@ +--- +-- Working Example: https://www.youtube.com/watch?v=PDxTR_KJLhc +-- IMPORTANT: run node.compile("imap.lua") after uploading this script +-- to create a compiled module. Then run file.remove("imap.lua") +-- @name imap +-- @description An IMAP 4rev1 module that can be used to read email. +-- Tested on NodeMCU 0.9.5 build 20150213. +-- @date March 12, 2015 +-- @author Miguel +-- GitHub: https://github.com/AllAboutEE +-- YouTube: https://www.youtube.com/user/AllAboutEE +-- Website: http://AllAboutEE.com +-- +-- Visit the following URLs to learn more about IMAP: +-- "How to test an IMAP server by using telnet" http://www.anta.net/misc/telnet-troubleshooting/imap.shtml +-- "RFC 2060 - Internet Message Access Protocol - Version 4rev1" http://www.faqs.org/rfcs/rfc2060.html +------------------------------------------------------------------------------------------------------------- +local moduleName = ... +local M = {} +_G[moduleName] = M + +local USERNAME = "" +local PASSWORD = "" + +local SERVER = "" +local PORT = "" +local TAG = "" + +local DEBUG = false + +local body = "" -- used to store an email's body / main text +local header = "" -- used to store an email's last requested header field e.g. SUBJECT, FROM, DATA etc. +local most_recent_num = 1 -- used to store the latest/newest email number/id + + +local response_processed = false -- used to know if the last IMAP response has been processed + +--- +-- @name response_processed +-- @returns The response process status of the last IMAP command sent +function M.response_processed() + return response_processed +end + +--- +-- @name display +-- @description A generic IMAP response processing function. +-- Can disply the IMAP response if DEBUG is set to true. +-- Sets the reponse processed variable to true when the string "complete" +-- is found in the IMAP reply/response +local function display(socket, response) + + -- If debuggins is enabled print the IMAP response + if(DEBUG) then + print(response) + end + + -- Some IMAP responses are long enough that they will cause the display + -- function to be called several times. One thing is certain, IMAP will replay with + -- " OK complete" when it's done sending data back. + if(string.match(response,'complete') ~= nil) then + response_processed = true + end + +end + +--- +-- @name config +-- @description Initiates the IMAP settings +function M.config(username,password,tag,debug) + USERNAME = username + PASSWORD = password + TAG = tag + DEBUG = debug +end + +--- +-- @name login +-- @descrpiton Logs into a new email session +function M.login(socket) + response_processed = false -- we are sending a new command + -- which means that the response for it has not been processed + socket:send(TAG .. " LOGIN " .. USERNAME .. " " .. PASSWORD .. "\r\n") + socket:on("receive",display) +end + +--- +-- @name get_most_recent_num +-- @returns The most recent email number. Should only be called after examine() +function M.get_most_recent_num() + return most_recent_num +end + +--- +-- @name set_most_recent_num +-- @description Gets the most recent email number from the EXAMINE command. +-- i.e. if EXAMINE returns "* 4 EXISTS" this means that there are 4 emails, +-- so the latest/newest will be identified by the number 4 +local function set_most_recent_num(socket,response) + + if(DEBUG) then + print(response) + end + + local _, _, num = string.find(response,"([0-9]+) EXISTS(\.)") -- the _ and _ keep the index of the string found + -- but we don't care about that. + + if(num~=nil) then + most_recent_num = num + end + + if(string.match(response,'complete') ~= nil) then + response_processed = true + end +end + +--- +-- @name examine +-- @description IMAP examines the given mailbox/folder. Sends the IMAP EXAMINE command +function M.examine(socket,mailbox) + + response_processed = false + socket:send(TAG .. " EXAMINE " .. mailbox .. "\r\n") + socket:on("receive",set_most_recent_num) +end + +--- +-- @name get_header +-- @returns The last fetched header field +function M.get_header() + return header +end + +--- +-- @name set_header +-- @description Records the IMAP header field response in a variable +-- so that it may be read later +local function set_header(socket,response) + if(DEBUG) then + print(response) + end + + header = header .. response + if(string.match(response,'complete') ~= nil) then + response_processed = true + end +end + +--- +-- @name fetch_header +-- @description Fetches an emails header field e.g. SUBJECT, FROM, DATE +-- @param socket The IMAP socket to use +-- @param msg_number The email number to read e.g. 1 will read fetch the latest/newest email +-- @param field A header field such as SUBJECT, FROM, or DATE +function M.fetch_header(socket,msg_number,field) + header = "" -- we are getting a new header so clear this variable + response_processed = false + socket:send(TAG .. " FETCH " .. msg_number .. " BODY[HEADER.FIELDS (" .. field .. ")]\r\n") + socket:on("receive",set_header) +end + + +--- +-- @name get_body +-- @return The last email read's body +function M.get_body() + return body +end + +--- +-- @name set_body +-- @description Records the IMAP body response in a variable +-- so that it may be read later +local function set_body(socket,response) + + if(DEBUG) then + print(response) + end + + body = body .. response + if(string.match(response,'complete') ~= nil) then + response_processed = true + end +end + +--- +-- @name fetch_body_plain_text +-- @description Sends the IMAP command to fetch a plain text version of the email's body +-- @param socket The IMAP socket to use +-- @param msg_number The email number to obtain e.g. 1 will obtain the latest email +function M.fetch_body_plain_text(socket,msg_number) + response_processed = false + body = "" -- clear the body variable since we'll be fetching a new email + socket:send(TAG .. " FETCH " .. msg_number .. " BODY[1]\r\n") + socket:on("receive",set_body) +end + +--- +-- @name logout +-- @description Sends the IMAP command to logout of the email session +function M.logout(socket) + response_processed = false + socket:send(TAG .. " LOGOUT\r\n") + socket:on("receive",display) +end