Merge pull request #4 from nodemcu/dev

Dev
This commit is contained in:
markusgritsch 2015-03-17 09:15:45 +01:00
commit abfdb58d40
49 changed files with 4558 additions and 331 deletions

View File

@ -8,13 +8,21 @@ install:
script: script:
- make all - make all
- cd bin/ - cd bin/
- file_name="nodemcu-firmware_v${TRAVIS_TAG}.${TRAVIS_BUILD_NUMBER}.bin" - file_name_float="nodemcu_float_${TRAVIS_TAG}.bin"
- srec_cat -output ${file_name} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000 - srec_cat -output ${file_name_float} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000
- cd ../
- make clean
- make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL"
- cd bin/
- file_name_integer="nodemcu_integer_${TRAVIS_TAG}.bin"
- srec_cat -output ${file_name_integer} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000
deploy: deploy:
provider: releases provider: releases
api_key: api_key:
secure: Swecz5lWvsuSbchSbVQ1rmCPN9nQIN5p/HlZNIEdEgAgnoLcJxRV4P8poVTB37jiA8Pck+8x2nWXpg74Rqik0i3KlPNvDfg5o4rIazWLNs4bc1Tbcpt44XAzFKKLYnDnWQUGcqjk7BcAXuNAF2X/fPBCVhFbHVg3Z7cDb32RsNw= secure: Swecz5lWvsuSbchSbVQ1rmCPN9nQIN5p/HlZNIEdEgAgnoLcJxRV4P8poVTB37jiA8Pck+8x2nWXpg74Rqik0i3KlPNvDfg5o4rIazWLNs4bc1Tbcpt44XAzFKKLYnDnWQUGcqjk7BcAXuNAF2X/fPBCVhFbHVg3Z7cDb32RsNw=
file: "$TRAVIS_BUILD_DIR/bin/${file_name}" file:
- "$TRAVIS_BUILD_DIR/bin/${file_name_float}"
- "$TRAVIS_BUILD_DIR/bin/${file_name_integer}"
skip_cleanup: true skip_cleanup: true
on: on:
tags: true tags: true

View File

@ -1,7 +1,8 @@
# **NodeMCU** # # **NodeMCU** #
version 0.9.5 version 0.9.5
[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) [![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) [![Download](https://img.shields.io/badge/download-~400k-orange.svg)](https://github.com/nodemcu/nodemcu-firmware/releases/latest)
###A lua based firmware for wifi-soc esp8266 ###A lua based firmware for wifi-soc esp8266
Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)<br /> Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)<br />
@ -33,6 +34,23 @@ Tencent QQ group: 309957875<br />
- cross compiler (done) - cross compiler (done)
# Change log # Change log
2015-03-15<br />
bugs fixed: #239, #273.<br />
reduce coap module memory usage, add coap module to default built.
2015-03-11<br />
fix bugs of spiffs.<br />
build both float and integer version [latest releases](https://github.com/nodemcu/nodemcu-firmware/releases/latest).<br />
fix tmr.time().<br />
fix memory leak when DNS fail.
2015-03-10<br />
update to the recent spiffs.<br />
add file.fsinfo() api, usage: remain, used, total = file.fsinfo().<br />
add Travis CI. please download the latest firmware from [releases](https://github.com/nodemcu/nodemcu-firmware/releases).<br />
add math lib, partial api work.<br />
u8g module, ws2812 module default enabled in dev-branch build.
2015-02-13<br /> 2015-02-13<br />
add node.compile() api to compile lua text file into lua bytecode file.<br /> add node.compile() api to compile lua text file into lua bytecode file.<br />
this will reduce memory usage noticeably when require modules into NodeMCU.<br /> this will reduce memory usage noticeably when require modules into NodeMCU.<br />
@ -400,3 +418,25 @@ They'll be available as `u8g.<font_name>` in Lua.
-- first LED green, second LED white -- first LED green, second LED white
ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255))
``` ```
####coap client and server
```lua
-- use copper addon for firefox
cs=coap.Server()
cs:listen(5683)
myvar=1
cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1
-- function should tack one string, return one string.
function myfun(payload)
print("myfun called")
respond = "hello"
return respond
end
cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun
cc = coap.Client()
cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core")
cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello")
```

View File

@ -76,7 +76,7 @@ int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8
else else
if (hdr->tkl <= 8) if (hdr->tkl <= 8)
{ {
if (4 + hdr->tkl > buflen) if (4U + hdr->tkl > buflen)
return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
tokbuf->p = buf+4; // past header tokbuf->p = buf+4; // past header
tokbuf->len = hdr->tkl; tokbuf->len = hdr->tkl;
@ -93,7 +93,7 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8
{ {
// inject token // inject token
uint8_t *p; uint8_t *p;
if (buflen < 4 + hdr->tkl) if (buflen < (4U + hdr->tkl))
return COAP_ERR_BUFFER_TOO_SMALL; return COAP_ERR_BUFFER_TOO_SMALL;
p = buf + 4; p = buf + 4;
if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len)) if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
@ -102,7 +102,7 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8
if (hdr->tkl > 0) if (hdr->tkl > 0)
c_memcpy(p, tokbuf->p, hdr->tkl); c_memcpy(p, tokbuf->p, hdr->tkl);
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1 // http://tools.ietf.org/html/rfc7252#section-3.1
// inject options // inject options
return hdr->tkl; return hdr->tkl;
} }
@ -111,8 +111,8 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8
int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen) int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
{ {
const uint8_t *p = *buf; const uint8_t *p = *buf;
uint16_t len, delta;
uint8_t headlen = 1; uint8_t headlen = 1;
uint16_t len, delta;
if (buflen < headlen) // too small if (buflen < headlen) // too small
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER; return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
@ -179,14 +179,14 @@ int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8
return 0; return 0;
} }
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1 // http://tools.ietf.org/html/rfc7252#section-3.1
int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen) int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{ {
size_t optionIndex = 0; size_t optionIndex = 0;
uint16_t delta = 0;
const uint8_t *p = buf + 4 + hdr->tkl; const uint8_t *p = buf + 4 + hdr->tkl;
const uint8_t *end = buf + buflen; const uint8_t *end = buf + buflen;
int rc; int rc;
uint16_t delta = 0;
if (p > end) if (p > end)
return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
@ -215,7 +215,7 @@ int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coa
return 0; return 0;
} }
int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen) int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen)
{ {
int n = 0; int n = 0;
uint8_t *p = buf; uint8_t *p = buf;
@ -298,7 +298,7 @@ int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
} }
// options are always stored consecutively, so can return a block with same option num // options are always stored consecutively, so can return a block with same option num
const coap_option_t * coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count) const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
{ {
// FIXME, options is always sorted, can find faster than this // FIXME, options is always sorted, can find faster than this
size_t i; size_t i;
@ -352,7 +352,7 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
uint16_t optDelta = 0; uint16_t optDelta = 0;
int rc = 0; int rc = 0;
if (p-buf > *buflen) if (((size_t)(p-buf)) > *buflen)
return COAP_ERR_BUFFER_TOO_SMALL; return COAP_ERR_BUFFER_TOO_SMALL;
optDelta = pkt->opts[i].num - running_delta; optDelta = pkt->opts[i].num - running_delta;
@ -381,17 +381,17 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
return 0; return 0;
} }
void coap_option_nibble(uint16_t value, uint8_t *nibble) void coap_option_nibble(uint32_t value, uint8_t *nibble)
{ {
if (value<13) if (value<13)
{ {
*nibble = (0xFF & value); *nibble = (0xFF & value);
} }
else else
if (((uint32_t)value)<=0xFF+13) if (value<=0xFF+13)
{ {
*nibble = 13; *nibble = 13;
} else if (((uint32_t)value) -269 <=0xFFFF) } else if (value<=0xFFFF+269)
{ {
*nibble = 14; *nibble = 14;
} }
@ -405,7 +405,7 @@ int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint
pkt->hdr.code = rspcode; pkt->hdr.code = rspcode;
pkt->hdr.id[0] = msgid_hi; pkt->hdr.id[0] = msgid_hi;
pkt->hdr.id[1] = msgid_lo; pkt->hdr.id[1] = msgid_lo;
pkt->numopts = 0; pkt->numopts = 1;
// need token in response // need token in response
if (tok) { if (tok) {
@ -442,7 +442,7 @@ unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
return n; return n;
} }
static uint8_t _token_data[4]={'n','o','d','e'}; static uint8_t _token_data[4] = {'n','o','d','e'};
coap_buffer_t the_token = { _token_data, 4 }; coap_buffer_t the_token = { _token_data, 4 };
static unsigned short message_id; static unsigned short message_id;

View File

@ -18,13 +18,15 @@ extern "C" {
#define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF) #define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF)
// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3 //http://tools.ietf.org/html/rfc7252#section-3
typedef struct typedef struct
{ {
uint8_t ver; uint8_t ver; /* CoAP version number */
uint8_t t; uint8_t t; /* CoAP Message Type */
uint8_t tkl; uint8_t tkl; /* Token length: indicates length of the Token field */
uint8_t code; uint8_t code; /* CoAP status code. Can be request (0.xx), success reponse (2.xx),
* client error response (4.xx), or rever error response (5.xx)
* For possible values, see http://tools.ietf.org/html/rfc7252#section-12.1 */
uint8_t id[2]; uint8_t id[2];
} coap_header_t; } coap_header_t;
@ -42,23 +44,24 @@ typedef struct
typedef struct typedef struct
{ {
uint8_t num; uint8_t num; /* Option number. See http://tools.ietf.org/html/rfc7252#section-5.10 */
coap_buffer_t buf; coap_buffer_t buf; /* Option value */
} coap_option_t; } coap_option_t;
typedef struct typedef struct
{ {
coap_header_t hdr; coap_header_t hdr; /* Header of the packet */
coap_buffer_t tok; coap_buffer_t tok; /* Token value, size as specified by hdr.tkl */
uint8_t numopts; uint8_t numopts; /* Number of options */
coap_option_t opts[MAXOPT]; coap_option_t opts[MAXOPT]; /* Options of the packet. For possible entries see
coap_buffer_t payload; * http://tools.ietf.org/html/rfc7252#section-5.10 */
coap_rw_buffer_t scratch; // scratch->p = malloc(...) , and free it when done. coap_buffer_t payload; /* Payload carried by the packet */
coap_rw_buffer_t content; // content->p = malloc(...) , and free it when done.
} coap_packet_t; } coap_packet_t;
///////////////////////////////////////// /////////////////////////////////////////
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.2 //http://tools.ietf.org/html/rfc7252#section-12.2
typedef enum typedef enum
{ {
COAP_OPTION_IF_MATCH = 1, COAP_OPTION_IF_MATCH = 1,
@ -78,7 +81,7 @@ typedef enum
COAP_OPTION_PROXY_SCHEME = 39 COAP_OPTION_PROXY_SCHEME = 39
} coap_option_num_t; } coap_option_num_t;
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1 //http://tools.ietf.org/html/rfc7252#section-12.1.1
typedef enum typedef enum
{ {
COAP_METHOD_GET = 1, COAP_METHOD_GET = 1,
@ -87,7 +90,7 @@ typedef enum
COAP_METHOD_DELETE = 4 COAP_METHOD_DELETE = 4
} coap_method_t; } coap_method_t;
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1 //http://tools.ietf.org/html/rfc7252#section-12.1.1
typedef enum typedef enum
{ {
COAP_TYPE_CON = 0, COAP_TYPE_CON = 0,
@ -96,8 +99,8 @@ typedef enum
COAP_TYPE_RESET = 3 COAP_TYPE_RESET = 3
} coap_msgtype_t; } coap_msgtype_t;
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.2 //http://tools.ietf.org/html/rfc7252#section-5.2
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.2 //http://tools.ietf.org/html/rfc7252#section-12.1.2
#define MAKE_RSPCODE(clas, det) ((clas << 5) | (det)) #define MAKE_RSPCODE(clas, det) ((clas << 5) | (det))
typedef enum typedef enum
{ {
@ -107,7 +110,7 @@ typedef enum
COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4) COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4)
} coap_responsecode_t; } coap_responsecode_t;
//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.3 //http://tools.ietf.org/html/rfc7252#section-12.3
typedef enum typedef enum
{ {
COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block
@ -155,15 +158,21 @@ struct coap_luser_entry{
coap_luser_entry *next; coap_luser_entry *next;
}; };
struct coap_endpoint_t struct coap_endpoint_t{
{ coap_method_t method; /* (i.e. POST, PUT or GET) */
coap_method_t method; coap_endpoint_func handler; /* callback function which handles this
coap_endpoint_func handler; * type of endpoint (and calls
const coap_endpoint_path_t *path; * coap_make_response() at some point) */
const char *core_attr; const coap_endpoint_path_t *path; /* path towards a resource (i.e. foo/bar/) */
coap_luser_entry *user_entry; const char *core_attr; /* the 'ct' attribute, as defined in RFC7252, section 7.2.1.:
* "The Content-Format code "ct" attribute
* provides a hint about the
* Content-Formats this resource returns."
* (Section 12.3. lists possible ct values.) */
coap_luser_entry *user_entry;
}; };
/////////////////////// ///////////////////////
void coap_dumpPacket(coap_packet_t *pkt); void coap_dumpPacket(coap_packet_t *pkt);
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen); int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen);
@ -173,11 +182,11 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt);
void coap_dump(const uint8_t *buf, size_t buflen, bool bare); void coap_dump(const uint8_t *buf, size_t buflen, bool bare);
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type); int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type);
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt); int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt);
void coap_option_nibble(uint16_t value, uint8_t *nibble); void coap_option_nibble(uint32_t value, uint8_t *nibble);
void coap_setup(void); void coap_setup(void);
void endpoint_setup(void); void endpoint_setup(void);
int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen); int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -11,6 +11,8 @@ void coap_client_response_handler(char *data, unsigned short len, unsigned short
{ {
NODE_DBG("coap_client_response_handler is called.\n"); NODE_DBG("coap_client_response_handler is called.\n");
coap_packet_t pkt; coap_packet_t pkt;
pkt.content.p = NULL;
pkt.content.len = 0;
int rc; int rc;
if (0 != (rc = coap_parse(&pkt, data, len))){ if (0 != (rc = coap_parse(&pkt, data, len))){

View File

@ -1,11 +1,11 @@
#ifndef _COAP_SERVER_H #ifndef _COAP_CLIENT_H
#define _COAP_SERVER_H 1 #define _COAP_CLIENT_H 1
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
size_t coap_server_respond(char *data, unsigned short len, unsigned short size); void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,53 +1,60 @@
#include "user_config.h" #include "user_config.h"
#include "c_types.h" #include "c_types.h"
#include "c_stdlib.h"
#include "coap.h" #include "coap.h"
size_t coap_server_respond(char *data, unsigned short len, unsigned short size) size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen)
{ {
NODE_DBG("coap_server_respond is called.\n"); NODE_DBG("coap_server_respond is called.\n");
if(len>size){ size_t rlen = rsplen;
NODE_DBG("len:%d, size:%d\n",len,size);
return 0;
}
size_t rsplen = size;
coap_packet_t pkt; coap_packet_t pkt;
pkt.content.p = NULL;
pkt.content.len = 0;
uint8_t scratch_raw[4]; uint8_t scratch_raw[4];
coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)}; coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)};
int rc; int rc;
#ifdef COAP_DEBUG #ifdef COAP_DEBUG
NODE_DBG("Received: "); NODE_DBG("Received: ");
coap_dump(data, len, true); coap_dump(req, reqlen, true);
NODE_DBG("\n"); NODE_DBG("\n");
#endif #endif
if (0 != (rc = coap_parse(&pkt, data, len))){ if (0 != (rc = coap_parse(&pkt, req, reqlen))){
NODE_DBG("Bad packet rc=%d\n", rc); NODE_DBG("Bad packet rc=%d\n", rc);
return 0; return 0;
} }
else else
{ {
coap_packet_t rsppkt; coap_packet_t rsppkt;
rsppkt.content.p = NULL;
rsppkt.content.len = 0;
#ifdef COAP_DEBUG #ifdef COAP_DEBUG
coap_dumpPacket(&pkt); coap_dumpPacket(&pkt);
#endif #endif
coap_handle_req(&scratch_buf, &pkt, &rsppkt); coap_handle_req(&scratch_buf, &pkt, &rsppkt);
if (0 != (rc = coap_build(data, &rsplen, &rsppkt))){ if (0 != (rc = coap_build(rsp, &rlen, &rsppkt))){
NODE_DBG("coap_build failed rc=%d\n", rc); NODE_DBG("coap_build failed rc=%d\n", rc);
return 0; // return 0;
rlen = 0;
} }
else else
{ {
#ifdef COAP_DEBUG #ifdef COAP_DEBUG
NODE_DBG("Responding: "); NODE_DBG("Responding: ");
coap_dump(data, rsplen, true); coap_dump(rsp, rlen, true);
NODE_DBG("\n"); NODE_DBG("\n");
#endif #endif
#ifdef COAP_DEBUG #ifdef COAP_DEBUG
coap_dumpPacket(&rsppkt); coap_dumpPacket(&rsppkt);
#endif #endif
} }
return rsplen; if(rsppkt.content.p){
c_free(rsppkt.content.p);
rsppkt.content.p = NULL;
rsppkt.content.len = 0;
}
return rlen;
} }
} }

View File

@ -5,7 +5,7 @@
extern "C" { extern "C" {
#endif #endif
size_t coap_server_respond(char *data, unsigned short len, unsigned short size); size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,5 +1,6 @@
#include "c_stdio.h" #include "c_stdio.h"
#include "c_string.h" #include "c_string.h"
#include "c_stdlib.h"
#include "coap.h" #include "coap.h"
#include "lua.h" #include "lua.h"
@ -8,20 +9,24 @@
#include "os_type.h" #include "os_type.h"
static char rsp[MAX_PAYLOAD_SIZE] = ""; void build_well_known_rsp(char *rsp, uint16_t rsplen);
const uint16_t rsplen = MAX_PAYLOAD_SIZE;
void build_well_known_rsp(void);
void endpoint_setup(void) void endpoint_setup(void)
{ {
coap_setup(); coap_setup();
build_well_known_rsp();
} }
static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}}; static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}};
static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo) static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
{ {
return coap_make_response(scratch, outpkt, (const uint8_t *)rsp, c_strlen(rsp), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT); outpkt->content.p = (uint8_t *)c_zalloc(MAX_PAYLOAD_SIZE); // this should be free-ed when outpkt is built in coap_server_respond()
if(outpkt->content.p == NULL){
NODE_DBG("not enough memory\n");
return COAP_ERR_BUFFER_TOO_SMALL;
}
outpkt->content.len = MAX_PAYLOAD_SIZE;
build_well_known_rsp(outpkt->content.p, outpkt->content.len);
return coap_make_response(scratch, outpkt, (const uint8_t *)outpkt->content.p, c_strlen(outpkt->content.p), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT);
} }
static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}}; static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}};
@ -29,6 +34,7 @@ static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scra
{ {
const coap_option_t *opt; const coap_option_t *opt;
uint8_t count; uint8_t count;
int n;
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count))) if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
{ {
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable] if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
@ -54,14 +60,15 @@ static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scra
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
if(c_strlen(h->name)) if(c_strlen(h->name))
{ {
n = lua_gettop(h->L);
lua_getglobal(h->L, h->name); lua_getglobal(h->L, h->name);
if (!lua_isnumber(h->L, -1)) { if (!lua_isnumber(h->L, -1)) {
NODE_DBG ("should be a number.\n"); NODE_DBG ("should be a number.\n");
lua_pop(h->L, 1); lua_settop(h->L, n);
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
} else { } else {
const char *res = lua_tostring(h->L,-1); const char *res = lua_tostring(h->L,-1);
lua_pop(h->L, 1); lua_settop(h->L, n);
return coap_make_response(scratch, outpkt, (const uint8_t *)res, c_strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); return coap_make_response(scratch, outpkt, (const uint8_t *)res, c_strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
} }
} }
@ -84,6 +91,7 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr
{ {
const coap_option_t *opt; const coap_option_t *opt;
uint8_t count; uint8_t count;
int n;
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count))) if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
{ {
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable] if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
@ -111,10 +119,11 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr
if(c_strlen(h->name)) if(c_strlen(h->name))
{ {
n = lua_gettop(h->L);
lua_getglobal(h->L, h->name); lua_getglobal(h->L, h->name);
if (lua_type(h->L, -1) != LUA_TFUNCTION) { if (lua_type(h->L, -1) != LUA_TFUNCTION) {
NODE_DBG ("should be a function\n"); NODE_DBG ("should be a function\n");
lua_pop(h->L, 1); lua_settop(h->L, n);
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
} else { } else {
lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring
@ -125,14 +134,17 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr
size_t len = 0; size_t len = 0;
const char *ret = luaL_checklstring( h->L, -1, &len ); const char *ret = luaL_checklstring( h->L, -1, &len );
if(len > MAX_PAYLOAD_SIZE){ if(len > MAX_PAYLOAD_SIZE){
lua_settop(h->L, n);
luaL_error( h->L, "return string:<MAX_PAYLOAD_SIZE" ); luaL_error( h->L, "return string:<MAX_PAYLOAD_SIZE" );
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
} }
NODE_DBG((char *)ret); NODE_DBG((char *)ret);
NODE_DBG("\n"); NODE_DBG("\n");
lua_settop(h->L, n);
return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
} }
} else { } else {
lua_settop(h->L, n);
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
} }
} }
@ -204,13 +216,13 @@ const coap_endpoint_t endpoints[] =
{(coap_method_t)0, NULL, NULL, NULL, NULL} {(coap_method_t)0, NULL, NULL, NULL, NULL}
}; };
void build_well_known_rsp(void) void build_well_known_rsp(char *rsp, uint16_t rsplen)
{ {
const coap_endpoint_t *ep = endpoints; const coap_endpoint_t *ep = endpoints;
int i; int i;
uint16_t len = rsplen; uint16_t len = rsplen;
c_memset(rsp, 0, sizeof(rsp)); c_memset(rsp, 0, len);
len--; // Null-terminated string len--; // Null-terminated string

View File

@ -24,6 +24,8 @@ coap_pdu_t * coap_new_pdu(void) {
c_free(pdu); c_free(pdu);
return NULL; return NULL;
} }
pdu->pkt->content.p = NULL;
pdu->pkt->content.len = 0;
pdu->msg.p = (uint8_t *)c_zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0' pdu->msg.p = (uint8_t *)c_zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0'
if(!pdu->msg.p){ if(!pdu->msg.p){

View File

@ -10,6 +10,7 @@
// #define FLASH_8M // #define FLASH_8M
// #define FLASH_16M // #define FLASH_16M
#define FLASH_AUTOSIZE #define FLASH_AUTOSIZE
#define FLASH_SAFE_API
// #define DEVELOP_VERSION // #define DEVELOP_VERSION
#define FULL_VERSION_FOR_USER #define FULL_VERSION_FOR_USER

View File

@ -27,7 +27,7 @@
#define LUA_USE_MODULES_OW #define LUA_USE_MODULES_OW
#define LUA_USE_MODULES_BIT #define LUA_USE_MODULES_BIT
#define LUA_USE_MODULES_MQTT #define LUA_USE_MODULES_MQTT
// #define LUA_USE_MODULES_COAP // need about 4k more ram for now #define LUA_USE_MODULES_COAP
#define LUA_USE_MODULES_U8G #define LUA_USE_MODULES_U8G
#define LUA_USE_MODULES_WS2812 #define LUA_USE_MODULES_WS2812
#endif /* LUA_USE_MODULES */ #endif /* LUA_USE_MODULES */

View File

@ -7,6 +7,6 @@
#define NODE_VERSION_INTERNAL 0U #define NODE_VERSION_INTERNAL 0U
#define NODE_VERSION "NodeMCU 0.9.5" #define NODE_VERSION "NodeMCU 0.9.5"
#define BUILD_DATE "build 20150306" #define BUILD_DATE "build 20150315"
#endif /* __USER_VERSION_H__ */ #endif /* __USER_VERSION_H__ */

View File

@ -63,7 +63,7 @@ int c_stderr = 1001;
#define ENDIAN_LITTLE 1234 #define ENDIAN_LITTLE 1234
#define ENDIAN_BIG 4321 #define ENDIAN_BIG 4321
#define ENDIAN_PDP 3412 #define ENDIAN_PDP 3412
#define ENDIAN ENDIAN_BIG #define ENDIAN ENDIAN_LITTLE
/* $Id: strichr.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */ /* $Id: strichr.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */

View File

@ -659,13 +659,19 @@ typedef struct LoadFSF {
static const char *getFSF (lua_State *L, void *ud, size_t *size) { static const char *getFSF (lua_State *L, void *ud, size_t *size) {
LoadFSF *lf = (LoadFSF *)ud; LoadFSF *lf = (LoadFSF *)ud;
(void)L; (void)L;
if (L == NULL && size == NULL) // Direct mode check
return NULL;
if (lf->extraline) { if (lf->extraline) {
lf->extraline = 0; lf->extraline = 0;
*size = 1; *size = 1;
return "\n"; return "\n";
} }
if (fs_eof(lf->f)) return NULL; if (fs_eof(lf->f)) return NULL;
*size = fs_read(lf->f, lf->buff, sizeof(lf->buff)); *size = fs_read(lf->f, lf->buff, sizeof(lf->buff));
return (*size > 0) ? lf->buff : NULL; return (*size > 0) ? lf->buff : NULL;
} }

View File

@ -37,20 +37,20 @@ static void coap_received(void *arg, char *pdata, unsigned short len)
struct espconn *pesp_conn = arg; struct espconn *pesp_conn = arg;
lcoap_userdata *cud = (lcoap_userdata *)pesp_conn->reverse; lcoap_userdata *cud = (lcoap_userdata *)pesp_conn->reverse;
static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' // static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
c_memset(buf, 0, sizeof(buf)); // wipe prev data c_memset(buf, 0, sizeof(buf)); // wipe prev data
if (len > MAX_MESSAGE_SIZE) { if (len > MAX_MESSAGE_SIZE) {
NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client... NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
return; return;
} else {
c_memcpy(buf, pdata, len);
} }
// c_memcpy(buf, pdata, len);
size_t rsplen = coap_server_respond(buf, len, MAX_MESSAGE_SIZE+1); size_t rsplen = coap_server_respond(pdata, len, buf, MAX_MESSAGE_SIZE+1);
espconn_sent(pesp_conn, (unsigned char *)buf, rsplen); espconn_sent(pesp_conn, (unsigned char *)buf, rsplen);
c_memset(buf, 0, sizeof(buf)); // c_memset(buf, 0, sizeof(buf));
} }
static void coap_sent(void *arg) static void coap_sent(void *arg)
@ -227,26 +227,19 @@ static void coap_response_handler(void *arg, char *pdata, unsigned short len)
struct espconn *pesp_conn = arg; struct espconn *pesp_conn = arg;
coap_packet_t pkt; coap_packet_t pkt;
static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' pkt.content.p = NULL;
pkt.content.len = 0;
// static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
c_memset(buf, 0, sizeof(buf)); // wipe prev data c_memset(buf, 0, sizeof(buf)); // wipe prev data
static int n = 0;
int rc; int rc;
if ((len == 1460) && (1460 <= MAX_MESSAGE_SIZE)){ if( len > MAX_MESSAGE_SIZE )
c_memcpy(buf, pdata, len); // max length is 1460, another part of data is coming in next callback {
n = len; NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
return; return;
} else {
if( len > MAX_MESSAGE_SIZE )
{
NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
c_memset(buf, 0, sizeof(buf)); // wipe prev data
n = 0;
return;
}
c_memcpy(buf + n, pdata, len);
len += n; // more than 1460
} }
c_memcpy(buf, pdata, len);
if (0 != (rc = coap_parse(&pkt, buf, len))){ if (0 != (rc = coap_parse(&pkt, buf, len))){
NODE_DBG("Bad packet rc=%d\n", rc); NODE_DBG("Bad packet rc=%d\n", rc);
@ -306,8 +299,7 @@ end:
if(pesp_conn->proto.udp->remote_port || pesp_conn->proto.udp->local_port) if(pesp_conn->proto.udp->remote_port || pesp_conn->proto.udp->local_port)
espconn_delete(pesp_conn); espconn_delete(pesp_conn);
} }
c_memset(buf, 0, sizeof(buf)); // c_memset(buf, 0, sizeof(buf));
n = 0;
} }
// Lua: client:request( [CON], uri, [payload] ) // Lua: client:request( [CON], uri, [payload] )
@ -431,7 +423,6 @@ static int coap_request( lua_State* L, coap_method_t m )
extern coap_luser_entry *variable_entry; extern coap_luser_entry *variable_entry;
extern coap_luser_entry *function_entry; extern coap_luser_entry *function_entry;
extern void build_well_known_rsp(void);
// Lua: coap:var/func( string ) // Lua: coap:var/func( string )
static int coap_regist( lua_State* L, const char* mt, int isvar ) static int coap_regist( lua_State* L, const char* mt, int isvar )
{ {
@ -466,8 +457,6 @@ static int coap_regist( lua_State* L, const char* mt, int isvar )
h->L = L; h->L = L;
h->name = name; h->name = name;
build_well_known_rsp(); // rebuild .well-known
NODE_DBG("coap_regist is called.\n"); NODE_DBG("coap_regist is called.\n");
return 0; return 0;
} }

View File

@ -175,6 +175,22 @@ static int file_rename( lua_State* L )
return 1; return 1;
} }
// Lua: fsinfo()
static int file_fsinfo( lua_State* L )
{
uint32_t total, used;
SPIFFS_info(&fs, &total, &used);
NODE_DBG("total: %d, used:%d\n", total, used);
if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
{
return luaL_error(L, "file system error");;
}
lua_pushinteger(L, total-used);
lua_pushinteger(L, used);
lua_pushinteger(L, total);
return 3;
}
#endif #endif
// g_read() // g_read()
@ -308,6 +324,7 @@ const LUA_REG_TYPE file_map[] =
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) }, { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
// { LSTRKEY( "check" ), LFUNCVAL( file_check ) }, // { LSTRKEY( "check" ), LFUNCVAL( file_check ) },
{ LSTRKEY( "rename" ), LFUNCVAL( file_rename ) }, { LSTRKEY( "rename" ), LFUNCVAL( file_rename ) },
{ LSTRKEY( "fsinfo" ), LFUNCVAL( file_fsinfo ) },
#endif #endif
#if LUA_OPTIMIZE_MEMORY > 0 #if LUA_OPTIMIZE_MEMORY > 0

View File

@ -200,10 +200,15 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
return; return;
} }
if(nud->self_ref == LUA_NOREF){
NODE_DBG("self_ref null.\n");
return;
}
if(ipaddr == NULL) if(ipaddr == NULL)
{ {
NODE_ERR( "DNS Fail!\n" ); NODE_ERR( "DNS Fail!\n" );
return; goto end;
} }
// ipaddr->addr is a uint32_t ip // ipaddr->addr is a uint32_t ip
@ -214,16 +219,12 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr))); c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr)));
} }
if(nud->self_ref == LUA_NOREF){
NODE_DBG("self_ref null.\n");
return;
}
lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function
lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua
lua_pushstring(gL, ip_str); // the ip para lua_pushstring(gL, ip_str); // the ip para
lua_call(gL, 2, 0); lua_call(gL, 2, 0);
end:
if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0) if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0)
|| (pesp_conn->type == ESPCONN_UDP && pesp_conn->proto.udp->remote_port == 0) ){ || (pesp_conn->type == ESPCONN_UDP && pesp_conn->proto.udp->remote_port == 0) ){
lua_gc(gL, LUA_GCSTOP, 0); lua_gc(gL, LUA_GCSTOP, 0);
@ -597,12 +598,22 @@ static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
NODE_DBG("pesp_conn null.\n"); NODE_DBG("pesp_conn null.\n");
return; return;
} }
lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
if(nud == NULL)
return;
if(gL == NULL)
return;
if(ipaddr == NULL) if(ipaddr == NULL)
{ {
dns_reconn_count++; dns_reconn_count++;
if( dns_reconn_count >= 5 ){ if( dns_reconn_count >= 5 ){
NODE_ERR( "DNS Fail!\n" ); NODE_ERR( "DNS Fail!\n" );
lua_gc(gL, LUA_GCSTOP, 0);
if(nud->self_ref != LUA_NOREF){
luaL_unref(gL, LUA_REGISTRYINDEX, nud->self_ref);
nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
}
lua_gc(gL, LUA_GCRESTART, 0);
return; return;
} }
NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); NODE_ERR( "DNS retry %d!\n", dns_reconn_count );

View File

@ -29,7 +29,7 @@
static int node_restart( lua_State* L ) static int node_restart( lua_State* L )
{ {
system_restart(); system_restart();
return 0; return 0;
} }
// Lua: dsleep( us, option ) // Lua: dsleep( us, option )
@ -56,7 +56,7 @@ static int node_deepsleep( lua_State* L )
else else
system_deep_sleep( us ); system_deep_sleep( us );
} }
return 0; return 0;
} }
// Lua: dsleep_set_options // Lua: dsleep_set_options
@ -80,10 +80,14 @@ static int node_info( lua_State* L )
lua_pushinteger(L, NODE_VERSION_REVISION); lua_pushinteger(L, NODE_VERSION_REVISION);
lua_pushinteger(L, system_get_chip_id()); // chip id lua_pushinteger(L, system_get_chip_id()); // chip id
lua_pushinteger(L, spi_flash_get_id()); // flash id lua_pushinteger(L, spi_flash_get_id()); // flash id
lua_pushinteger(L, flash_get_size_byte() / 1024); // flash size in KB #if defined(FLASH_SAFE_API)
lua_pushinteger(L, flash_get_mode()); lua_pushinteger(L, flash_safe_get_size_byte() / 1024); // flash size in KB
lua_pushinteger(L, flash_get_speed()); #else
return 8; lua_pushinteger(L, flash_rom_get_size_byte() / 1024); // flash size in KB
#endif // defined(FLASH_SAFE_API)
lua_pushinteger(L, flash_rom_get_mode());
lua_pushinteger(L, flash_rom_get_speed());
return 8;
} }
// Lua: chipid() // Lua: chipid()
@ -91,7 +95,7 @@ static int node_chipid( lua_State* L )
{ {
uint32_t id = system_get_chip_id(); uint32_t id = system_get_chip_id();
lua_pushinteger(L, id); lua_pushinteger(L, id);
return 1; return 1;
} }
// Lua: readvdd33() // Lua: readvdd33()
static int node_readvdd33( lua_State* L ) static int node_readvdd33( lua_State* L )
@ -106,24 +110,23 @@ static int node_flashid( lua_State* L )
{ {
uint32_t id = spi_flash_get_id(); uint32_t id = spi_flash_get_id();
lua_pushinteger( L, id ); lua_pushinteger( L, id );
return 1; return 1;
} }
// Lua: flashsize() // Lua: flashsize()
static int node_flashsize( lua_State* L ) static int node_flashsize( lua_State* L )
{ {
//uint32_t sz = 0; if (lua_type(L, 1) == LUA_TNUMBER)
//if(lua_type(L, 1) == LUA_TNUMBER) {
//{ flash_rom_set_size_byte(luaL_checkinteger(L, 1));
// sz = luaL_checkinteger(L, 1); }
// if(sz > 0) #if defined(FLASH_SAFE_API)
// { uint32_t sz = flash_safe_get_size_byte();
// flash_set_size_byte(sz); #else
// } uint32_t sz = flash_rom_get_size_byte();
//} #endif // defined(FLASH_SAFE_API)
uint32_t sz = flash_get_size_byte();
lua_pushinteger( L, sz ); lua_pushinteger( L, sz );
return 1; return 1;
} }
// Lua: heap() // Lua: heap()
@ -131,7 +134,7 @@ static int node_heap( lua_State* L )
{ {
uint32_t sz = system_get_free_heap_size(); uint32_t sz = system_get_free_heap_size();
lua_pushinteger(L, sz); lua_pushinteger(L, sz);
return 1; return 1;
} }
static lua_State *gL = NULL; static lua_State *gL = NULL;
@ -146,7 +149,7 @@ static int node_led( lua_State* L )
if ( lua_isnumber(L, 1) ) if ( lua_isnumber(L, 1) )
{ {
low = lua_tointeger(L, 1); low = lua_tointeger(L, 1);
if ( low < 0 ){ if ( low < 0 ) {
return luaL_error( L, "wrong arg type" ); return luaL_error( L, "wrong arg type" );
} }
} else { } else {
@ -155,7 +158,7 @@ static int node_led( lua_State* L )
if ( lua_isnumber(L, 2) ) if ( lua_isnumber(L, 2) )
{ {
high = lua_tointeger(L, 2); high = lua_tointeger(L, 2);
if ( high < 0 ){ if ( high < 0 ) {
return luaL_error( L, "wrong arg type" ); return luaL_error( L, "wrong arg type" );
} }
} else { } else {
@ -163,14 +166,14 @@ static int node_led( lua_State* L )
} }
led_high_count = (uint32_t)high / READLINE_INTERVAL; led_high_count = (uint32_t)high / READLINE_INTERVAL;
led_low_count = (uint32_t)low / READLINE_INTERVAL; led_low_count = (uint32_t)low / READLINE_INTERVAL;
return 0; return 0;
} }
static int long_key_ref = LUA_NOREF; static int long_key_ref = LUA_NOREF;
static int short_key_ref = LUA_NOREF; static int short_key_ref = LUA_NOREF;
void default_long_press(void *arg){ void default_long_press(void *arg) {
if(led_high_count == 12 && led_low_count == 12){ if (led_high_count == 12 && led_low_count == 12) {
led_low_count = led_high_count = 6; led_low_count = led_high_count = 6;
} else { } else {
led_low_count = led_high_count = 12; led_low_count = led_high_count = 12;
@ -180,32 +183,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); // 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(); system_restart();
} }
void key_long_press(void *arg){ void key_long_press(void *arg) {
NODE_DBG("key_long_press is called.\n"); NODE_DBG("key_long_press is called.\n");
if(long_key_ref == LUA_NOREF){ if (long_key_ref == LUA_NOREF) {
default_long_press(arg); default_long_press(arg);
return; return;
} }
if(!gL) if (!gL)
return; return;
lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref); lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref);
lua_call(gL, 0, 0); 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"); NODE_DBG("key_short_press is called.\n");
if(short_key_ref == LUA_NOREF){ if (short_key_ref == LUA_NOREF) {
default_short_press(arg); default_short_press(arg);
return; return;
} }
if(!gL) if (!gL)
return; return;
lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref); lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref);
lua_call(gL, 0, 0); lua_call(gL, 0, 0);
} }
// Lua: key(type, function) // Lua: key(type, function)
@ -213,32 +216,32 @@ static int node_key( lua_State* L )
{ {
int *ref = NULL; int *ref = NULL;
size_t sl; size_t sl;
const char *str = luaL_checklstring( L, 1, &sl ); const char *str = luaL_checklstring( L, 1, &sl );
if (str == NULL) if (str == NULL)
return luaL_error( L, "wrong arg type" ); 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; 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; ref = &long_key_ref;
}else{ } else {
ref = &short_key_ref; ref = &short_key_ref;
} }
gL = L; gL = L;
// luaL_checkanyfunction(L, 2); // 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 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); luaL_unref(L, LUA_REGISTRYINDEX, *ref);
*ref = luaL_ref(L, LUA_REGISTRYINDEX); *ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else { // unref the key press function } else { // unref the key press function
if(*ref != LUA_NOREF) if (*ref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, *ref); luaL_unref(L, LUA_REGISTRYINDEX, *ref);
*ref = LUA_NOREF; *ref = LUA_NOREF;
} }
return 0; return 0;
} }
#endif #endif
@ -248,15 +251,15 @@ extern void dojob(lua_Load *load);
// Lua: input("string") // Lua: input("string")
static int node_input( lua_State* L ) static int node_input( lua_State* L )
{ {
size_t l=0; size_t l = 0;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, 1, &l);
if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1) if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1)
{ {
lua_Load *load = &gLoad; lua_Load *load = &gLoad;
if(load->line_position == 0){ if (load->line_position == 0) {
c_memcpy(load->line, s, l); c_memcpy(load->line, s, l);
load->line[l+1] = '\0'; load->line[l + 1] = '\0';
load->line_position = c_strlen(load->line)+1; load->line_position = c_strlen(load->line) + 1;
load->done = 1; load->done = 1;
NODE_DBG("Get command:\n"); NODE_DBG("Get command:\n");
NODE_DBG(load->line); // buggy here NODE_DBG(load->line); // buggy here
@ -271,18 +274,18 @@ static int node_input( lua_State* L )
static int output_redir_ref = LUA_NOREF; static int output_redir_ref = LUA_NOREF;
static int serial_debug = 1; static int serial_debug = 1;
void output_redirect(const char *str){ void output_redirect(const char *str) {
// if(c_strlen(str)>=TX_BUFF_SIZE){ // if(c_strlen(str)>=TX_BUFF_SIZE){
// NODE_ERR("output too long.\n"); // NODE_ERR("output too long.\n");
// return; // return;
// } // }
if(output_redir_ref == LUA_NOREF || !gL){ if (output_redir_ref == LUA_NOREF || !gL) {
uart0_sendStr(str); uart0_sendStr(str);
return; return;
} }
if(serial_debug!=0){ if (serial_debug != 0) {
uart0_sendStr(str); uart0_sendStr(str);
} }
@ -296,15 +299,15 @@ static int node_output( lua_State* L )
{ {
gL = L; gL = L;
// luaL_checkanyfunction(L, 1); // 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 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); luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX); output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else { // unref the key press function } 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); luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
output_redir_ref = LUA_NOREF; output_redir_ref = LUA_NOREF;
serial_debug = 1; serial_debug = 1;
return 0; return 0;
} }
@ -312,26 +315,26 @@ static int node_output( lua_State* L )
if ( lua_isnumber(L, 2) ) if ( lua_isnumber(L, 2) )
{ {
serial_debug = lua_tointeger(L, 2); serial_debug = lua_tointeger(L, 2);
if(serial_debug!=0) if (serial_debug != 0)
serial_debug = 1; serial_debug = 1;
} else { } else {
serial_debug = 1; // default to 1 serial_debug = 1; // default to 1
} }
return 0; return 0;
} }
static int writer(lua_State* L, const void* p, size_t size, void* u) static int writer(lua_State* L, const void* p, size_t size, void* u)
{ {
UNUSED(L); UNUSED(L);
int file_fd = *( (int *)u ); int file_fd = *( (int *)u );
if((FS_OPEN_OK - 1)==file_fd) if ((FS_OPEN_OK - 1) == file_fd)
return 1; return 1;
NODE_DBG("get fd:%d,size:%d\n",file_fd,size); NODE_DBG("get fd:%d,size:%d\n", file_fd, size);
if(size!=0 && (size!=fs_write(file_fd, (const char *)p, size)) ) if (size != 0 && (size != fs_write(file_fd, (const char *)p, size)) )
return 1; 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; return 0;
} }
@ -343,45 +346,45 @@ 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];
c_strcpy(output, fname); c_strcpy(output, fname);
// check here that filename end with ".lua". // 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"); return luaL_error(L, "not a .lua file");
output[c_strlen(output)-2] = 'c'; output[c_strlen(output) - 2] = 'c';
output[c_strlen(output)-1] = '\0'; output[c_strlen(output) - 1] = '\0';
NODE_DBG(output); NODE_DBG(output);
NODE_DBG("\n"); NODE_DBG("\n");
if (luaL_loadfsfile(L,fname)!=0){ if (luaL_loadfsfile(L, fname) != 0) {
return luaL_error(L, lua_tostring(L,-1)); return luaL_error(L, lua_tostring(L, -1));
} }
f = toproto(L,-1); f = toproto(L, -1);
int stripping = 1; /* strip debug information? */ int stripping = 1; /* strip debug information? */
file_fd = fs_open(output, fs_mode2flag("w+")); 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"); return luaL_error(L, "cannot open/write to file");
} }
lua_lock(L); 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); lua_unlock(L);
fs_flush(file_fd); fs_flush(file_fd);
fs_close(file_fd); fs_close(file_fd);
file_fd = FS_OPEN_OK - 1; 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"); 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 luaL_error(L, "target lua_Number is integral but fractional value found");
} }
@ -391,7 +394,7 @@ static int node_compile( lua_State* L )
// Module function map // Module function map
#define MIN_OPT_LEVEL 2 #define MIN_OPT_LEVEL 2
#include "lrodefs.h" #include "lrodefs.h"
const LUA_REG_TYPE node_map[] = const LUA_REG_TYPE node_map[] =
{ {
{ LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) },
{ LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) },
@ -408,7 +411,7 @@ const LUA_REG_TYPE node_map[] =
{ LSTRKEY( "output" ), LFUNCVAL( node_output ) }, { LSTRKEY( "output" ), LFUNCVAL( node_output ) },
{ LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) },
{ LSTRKEY( "compile" ), LFUNCVAL( node_compile) }, { LSTRKEY( "compile" ), LFUNCVAL( node_compile) },
// Combined to dsleep(us, option) // Combined to dsleep(us, option)
// { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, // { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) },
#if LUA_OPTIMIZE_MEMORY > 0 #if LUA_OPTIMIZE_MEMORY > 0

View File

@ -148,24 +148,30 @@ static int tmr_wdclr( lua_State* L )
} }
static os_timer_t rtc_timer_updator; static os_timer_t rtc_timer_updator;
static uint64_t cur_count = 0; static uint32_t cur_count = 0;
static uint64_t rtc_us = 0; static uint32_t rtc_10ms = 0;
void rtc_timer_update_cb(void *arg){ void rtc_timer_update_cb(void *arg){
uint64_t t = (uint64_t)system_get_rtc_time(); uint32_t t = (uint32_t)system_get_rtc_time();
uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count); uint32_t delta = 0;
if(t>=cur_count){
delta = t-cur_count;
}else{
delta = 0xFFFFFFF - cur_count + t + 1;
}
// uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count);
// NODE_ERR("%x\n",t); // NODE_ERR("%x\n",t);
cur_count = t; cur_count = t;
unsigned c = system_rtc_clock_cali_proc(); uint32_t c = system_rtc_clock_cali_proc();
uint64_t itg = c >> 12; uint32_t itg = c >> 12; // ~=5
uint64_t dec = c & 0xFFF; uint32_t dec = c & 0xFFF; // ~=2ff
rtc_us += (delta*itg + ((delta*dec)>>12)); rtc_10ms += (delta*itg + ((delta*dec)>>12)) / 10000;
// TODO: store rtc_us to rtc memory. // TODO: store rtc_10ms to rtc memory.
} }
// Lua: time() , return rtc time in second // Lua: time() , return rtc time in second
static int tmr_time( lua_State* L ) static int tmr_time( lua_State* L )
{ {
uint64_t local = rtc_us; uint32_t local = rtc_10ms;
lua_pushinteger( L, ((uint32_t)(local/1000000)) & 0x7FFFFFFF ); lua_pushinteger( L, ((uint32_t)(local/100)) & 0x7FFFFFFF );
return 1; return 1;
} }

View File

@ -30,7 +30,11 @@
#elif defined(FLASH_16M) #elif defined(FLASH_16M)
#define FLASH_SEC_NUM 0x1000 #define FLASH_SEC_NUM 0x1000
#elif defined(FLASH_AUTOSIZE) #elif defined(FLASH_AUTOSIZE)
#define FLASH_SEC_NUM (flash_get_sec_num()) #if defined(FLASH_SAFE_API)
#define FLASH_SEC_NUM (flash_safe_get_sec_num())
#else
#define FLASH_SEC_NUM (flash_rom_get_sec_num())
#endif // defined(FLASH_SAFE_API)
#else #else
#define FLASH_SEC_NUM 0x80 #define FLASH_SEC_NUM 0x80
#endif #endif
@ -54,8 +58,14 @@
// SpiFlashOpResult spi_flash_erase_sector(uint16 sec); // SpiFlashOpResult spi_flash_erase_sector(uint16 sec);
// SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); // SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size);
// SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); // SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size);
#if defined(FLASH_SAFE_API)
#define flash_write flash_safe_write
#define flash_erase flash_safe_erase_sector
#define flash_read flash_safe_read
#else
#define flash_write spi_flash_write #define flash_write spi_flash_write
#define flash_erase spi_flash_erase_sector #define flash_erase spi_flash_erase_sector
#define flash_read spi_flash_read #define flash_read spi_flash_read
#endif // defined(FLASH_SAFE_API)
#endif // #ifndef __CPU_ESP8266_H__ #endif // #ifndef __CPU_ESP8266_H__

View File

@ -20,77 +20,154 @@ static volatile const uint8_t flash_init_data[128] ICACHE_STORE_ATTR ICACHE_RODA
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
SPIFlashInfo flash_get_info(void) uint32_t flash_detect_size_byte(void)
{ {
volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR; #define FLASH_BUFFER_SIZE_DETECT 32
spi_flash_info = *((SPIFlashInfo *)(FLASH_MAP_START_ADDRESS)); uint32_t dummy_size = FLASH_SIZE_256KBYTE;
// spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info)); uint8_t data_orig[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0};
return spi_flash_info; uint8_t data_new[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0};
} if (SPI_FLASH_RESULT_OK == flash_safe_read(0, (uint32 *)data_orig, FLASH_BUFFER_SIZE_DETECT))
uint8_t flash_get_size(void)
{
return flash_get_info().size;
}
uint32_t flash_get_size_byte(void)
{
uint32_t flash_size = 0;
switch (flash_get_info().size)
{ {
case SIZE_2MBIT: dummy_size = FLASH_SIZE_256KBYTE;
// 2Mbit, 256kByte while ((dummy_size < FLASH_SIZE_16MBYTE) &&
flash_size = 256 * 1024; (SPI_FLASH_RESULT_OK == flash_safe_read(dummy_size, (uint32 *)data_new, FLASH_BUFFER_SIZE_DETECT)) &&
break; (0 != os_memcmp(data_orig, data_new, FLASH_BUFFER_SIZE_DETECT))
case SIZE_4MBIT: )
// 4Mbit, 512kByte {
flash_size = 512 * 1024; dummy_size *= 2;
break; }
case SIZE_8MBIT: };
// 8Mbit, 1MByte return dummy_size;
flash_size = 1 * 1024 * 1024; #undef FLASH_BUFFER_SIZE_DETECT
break; }
case SIZE_16MBIT:
// 16Mbit, 2MByte uint32_t flash_safe_get_size_byte(void)
flash_size = 2 * 1024 * 1024; {
break; static uint32_t flash_size = 0;
case SIZE_32MBIT: if (flash_size == 0)
// 32Mbit, 4MByte {
flash_size = 4 * 1024 * 1024; flash_size = flash_detect_size_byte();
break;
case SIZE_64MBIT:
// 64Mbit, 8MByte
flash_size = 8 * 1024 * 1024;
break;
case SIZE_128MBIT:
// 128Mbit, 16MByte
flash_size = 16 * 1024 * 1024;
break;
default:
// Unknown flash size, fall back mode.
flash_size = 512 * 1024;
break;
} }
return flash_size; return flash_size;
} }
bool flash_set_size(uint8_t size) uint16_t flash_safe_get_sec_num(void)
{
return (flash_safe_get_size_byte() / (SPI_FLASH_SEC_SIZE));
}
SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size)
{
SpiFlashOpResult result = SPI_FLASH_RESULT_ERR;
FLASH_SAFEMODE_ENTER();
result = spi_flash_read(src_addr, (uint32 *) des_addr, size);
FLASH_SAFEMODE_LEAVE();
return result;
}
SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size)
{
SpiFlashOpResult result = SPI_FLASH_RESULT_ERR;
FLASH_SAFEMODE_ENTER();
result = spi_flash_write(des_addr, src_addr, size);
FLASH_SAFEMODE_LEAVE();
return result;
}
SpiFlashOpResult flash_safe_erase_sector(uint16 sec)
{
SpiFlashOpResult result = SPI_FLASH_RESULT_ERR;
FLASH_SAFEMODE_ENTER();
result = spi_flash_erase_sector(sec);
FLASH_SAFEMODE_LEAVE();
return result;
}
SPIFlashInfo flash_rom_getinfo(void)
{
volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR;
// Don't use it before cache read disabled
// FLASH_DISABLE_CACHE();
// spi_flash_info = *((SPIFlashInfo *)(FLASH_ADDRESS_START_MAP));
// FLASH_ENABLE_CACHE();
// Needn't safe mode.
spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info));
return spi_flash_info;
}
uint8_t flash_rom_get_size_type(void)
{
return flash_rom_getinfo().size;
}
uint32_t flash_rom_get_size_byte(void)
{
static uint32_t flash_size = 0;
if (flash_size == 0)
{
switch (flash_rom_getinfo().size)
{
case SIZE_2MBIT:
// 2Mbit, 256kByte
flash_size = 256 * 1024;
break;
case SIZE_4MBIT:
// 4Mbit, 512kByte
flash_size = 512 * 1024;
break;
case SIZE_8MBIT:
// 8Mbit, 1MByte
flash_size = 1 * 1024 * 1024;
break;
case SIZE_16MBIT:
// 16Mbit, 2MByte
flash_size = 2 * 1024 * 1024;
break;
case SIZE_32MBIT:
// 32Mbit, 4MByte
flash_size = 4 * 1024 * 1024;
break;
case SIZE_64MBIT:
// 64Mbit, 8MByte
flash_size = 8 * 1024 * 1024;
break;
case SIZE_128MBIT:
// 128Mbit, 16MByte
flash_size = 16 * 1024 * 1024;
break;
default:
// Unknown flash size, fall back mode.
flash_size = 512 * 1024;
break;
}
}
return flash_size;
}
bool flash_rom_set_size_type(uint8_t size)
{ {
// Dangerous, here are dinosaur infested!!!!! // Dangerous, here are dinosaur infested!!!!!
// Reboot required!!! // Reboot required!!!
// If you don't know what you're doing, your nodemcu may turn into stone ... // 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 data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR;
spi_flash_read(0, (uint32 *)data, sizeof(data)); if (SPI_FLASH_RESULT_OK == spi_flash_read(0, (uint32 *)data, SPI_FLASH_SEC_SIZE))
SPIFlashInfo *p_spi_flash_info = (SPIFlashInfo *)(data); {
p_spi_flash_info->size = size; ((SPIFlashInfo *)(&data[0]))->size = size;
spi_flash_erase_sector(0); if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(0 * SPI_FLASH_SEC_SIZE))
spi_flash_write(0, (uint32 *)data, sizeof(data)); {
//p_spi_flash_info = flash_get_info(); NODE_DBG("\nERASE SUCCESS\n");
//p_spi_flash_info->size = size; }
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; return true;
} }
bool flash_set_size_byte(uint32_t size) bool flash_rom_set_size_byte(uint32_t size)
{ {
// Dangerous, here are dinosaur infested!!!!! // Dangerous, here are dinosaur infested!!!!!
// Reboot required!!! // Reboot required!!!
@ -102,27 +179,37 @@ bool flash_set_size_byte(uint32_t size)
case 256 * 1024: case 256 * 1024:
// 2Mbit, 256kByte // 2Mbit, 256kByte
flash_size = SIZE_2MBIT; flash_size = SIZE_2MBIT;
flash_set_size(flash_size); flash_rom_set_size_type(flash_size);
break; break;
case 512 * 1024: case 512 * 1024:
// 4Mbit, 512kByte // 4Mbit, 512kByte
flash_size = SIZE_4MBIT; flash_size = SIZE_4MBIT;
flash_set_size(flash_size); flash_rom_set_size_type(flash_size);
break; break;
case 1 * 1024 * 1024: case 1 * 1024 * 1024:
// 8Mbit, 1MByte // 8Mbit, 1MByte
flash_size = SIZE_8MBIT; flash_size = SIZE_8MBIT;
flash_set_size(flash_size); flash_rom_set_size_type(flash_size);
break; break;
case 2 * 1024 * 1024: case 2 * 1024 * 1024:
// 16Mbit, 2MByte // 16Mbit, 2MByte
flash_size = SIZE_16MBIT; flash_size = SIZE_16MBIT;
flash_set_size(flash_size); flash_rom_set_size_type(flash_size);
break; break;
case 4 * 1024 * 1024: case 4 * 1024 * 1024:
// 32Mbit, 4MByte // 32Mbit, 4MByte
flash_size = SIZE_32MBIT; flash_size = SIZE_32MBIT;
flash_set_size(flash_size); 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; break;
default: default:
// Unknown flash size. // Unknown flash size.
@ -132,22 +219,22 @@ bool flash_set_size_byte(uint32_t size)
return result; return result;
} }
uint16_t flash_get_sec_num(void) uint16_t flash_rom_get_sec_num(void)
{ {
//static uint16_t sec_num = 0; //static uint16_t sec_num = 0;
// return flash_get_size_byte() / (SPI_FLASH_SEC_SIZE); // return flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE);
// c_printf("\nflash_get_size_byte()=%d\n", ( flash_get_size_byte() / (SPI_FLASH_SEC_SIZE) )); // c_printf("\nflash_rom_get_size_byte()=%d\n", ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) ));
// if( sec_num == 0 ) // if( sec_num == 0 )
//{ //{
// sec_num = 4 * 1024 * 1024 / (SPI_FLASH_SEC_SIZE); // sec_num = 4 * 1024 * 1024 / (SPI_FLASH_SEC_SIZE);
//} //}
//return sec_num; //return sec_num;
return ( flash_get_size_byte() / (SPI_FLASH_SEC_SIZE) ); return ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) );
} }
uint8_t flash_get_mode(void) uint8_t flash_rom_get_mode(void)
{ {
SPIFlashInfo spi_flash_info = flash_get_info(); SPIFlashInfo spi_flash_info = flash_rom_getinfo();
switch (spi_flash_info.mode) switch (spi_flash_info.mode)
{ {
// Reserved for future use // Reserved for future use
@ -163,10 +250,10 @@ uint8_t flash_get_mode(void)
return spi_flash_info.mode; return spi_flash_info.mode;
} }
uint32_t flash_get_speed(void) uint32_t flash_rom_get_speed(void)
{ {
uint32_t speed = 0; uint32_t speed = 0;
SPIFlashInfo spi_flash_info = flash_get_info(); SPIFlashInfo spi_flash_info = flash_rom_getinfo();
switch (spi_flash_info.speed) switch (spi_flash_info.speed)
{ {
case SPEED_40MHZ: case SPEED_40MHZ:
@ -189,11 +276,55 @@ uint32_t flash_get_speed(void)
return speed; 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) bool flash_init_data_written(void)
{ {
// FLASH SEC - 4 // FLASH SEC - 4
uint32_t data[2] ICACHE_STORE_ATTR; uint32_t data[2] ICACHE_STORE_ATTR;
if (SPI_FLASH_RESULT_OK == spi_flash_read((flash_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) #if defined(FLASH_SAFE_API)
if (SPI_FLASH_RESULT_OK == flash_safe_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data)))
#else
if (SPI_FLASH_RESULT_OK == spi_flash_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data)))
#endif // defined(FLASH_SAFE_API)
{ {
if (data[0] == 0xFFFFFFFF && data[1] == 0xFFFFFFFF) if (data[0] == 0xFFFFFFFF && data[1] == 0xFFFFFFFF)
{ {
@ -210,13 +341,23 @@ bool flash_init_data_default(void)
// Reboot required!!! // Reboot required!!!
// It will init system data to default! // It will init system data to default!
bool result = false; bool result = false;
if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 4))) #if defined(FLASH_SAFE_API)
if (SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_safe_get_sec_num() - 4)))
{ {
if (SPI_FLASH_RESULT_OK == spi_flash_write((flash_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128)) if (SPI_FLASH_RESULT_OK == flash_safe_write((flash_safe_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128))
{ {
result = true; result = true;
} }
} }
#else
if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 4)))
{
if (SPI_FLASH_RESULT_OK == spi_flash_write((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128))
{
result = true;
}
}
#endif // defined(FLASH_SAFE_API)
return result; return result;
} }
@ -227,8 +368,13 @@ bool flash_init_data_blank(void)
// Reboot required!!! // Reboot required!!!
// It will init system config to blank! // It will init system config to blank!
bool result = false; bool result = false;
if ((SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 2))) && #if defined(FLASH_SAFE_API)
(SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 1)))) if ((SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 2))) &&
(SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 1))))
#else
if ((SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 2))) &&
(SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 1))))
#endif // defined(FLASH_SAFE_API)
{ {
result = true; result = true;
} }
@ -254,3 +400,28 @@ uint8_t byte_of_aligned_array(const uint8_t *aligned_array, uint32_t index)
uint8_t *p = (uint8_t *) (&v); uint8_t *p = (uint8_t *) (&v);
return p[ (index % 4) ]; 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;
// }

View File

@ -4,34 +4,61 @@
#include "user_config.h" #include "user_config.h"
#include "cpu_esp8266.h" #include "cpu_esp8266.h"
#define FLASH_MAP_START_ADDRESS (INTERNAL_FLASH_START_ADDRESS) #define FLASH_ADDRESS_START_MAP (INTERNAL_FLASH_START_ADDRESS)
#define FLASH_SIZE_2MBIT (2 * 1024 * 1024)
#define FLASH_SIZE_4MBIT (4 * 1024 * 1024)
#define FLASH_SIZE_8MBIT (8 * 1024 * 1024)
#define FLASH_SIZE_16MBIT (16 * 1024 * 1024)
#define FLASH_SIZE_32MBIT (32 * 1024 * 1024)
#define FLASH_SIZE_64MBIT (64 * 1024 * 1024)
#define FLASH_SIZE_128MBIT (128 * 1024 * 1024)
#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_2MBYTE (FLASH_SIZE_16MBIT / 8)
#define FLASH_SIZE_4MBYTE (FLASH_SIZE_32MBIT / 8)
#define FLASH_SIZE_8MBYTE (FLASH_SIZE_64MBIT / 8)
#define FLASH_SIZE_16MBYTE (FLASH_SIZE_128MBIT/ 8)
#define FLASH_SAFEMODE_ENTER() \
do { \
extern SpiFlashChip * flashchip; \
flashchip->chip_size = FLASH_SIZE_16MBYTE
#define FLASH_SAFEMODE_LEAVE() \
flashchip->chip_size = flash_rom_get_size_byte(); \
} while(0)
/****************************************************************************** /******************************************************************************
* ROM Function definition * ROM Function definition
* Note: It is unsafe to use ROM function, but it may efficient. * Note: It is unsafe to use ROM function, but it may efficient.
* SPIEraseSector * SPIEraseSector
* unknown SPIEraseSector(uint16 sec); * SpiFlashOpResult SPIEraseSector(uint16 sec);
* The 1st parameter is flash sector number. * The 1st parameter is flash sector number.
* Note: Must disable cache read before using it.
* SPIRead (Unsafe) * SPIRead
* unknown SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); * SpiFlashOpResult SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
* The 1st parameter is source addresses. * The 1st parameter is source addresses.
* The 2nd parameter is destination addresses. * The 2nd parameter is destination addresses.
* The 3rd parameter is size. * The 3rd parameter is size.
* Note: Sometimes it have no effect, may be need a delay or other option(lock or unlock, etc.) with known reason. * Note: Must disable cache read before using it.
* SPIWrite (Unsafe) * SPIWrite
* unknown SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size); * SpiFlashOpResult SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size);
* The 1st parameter is destination addresses. * The 1st parameter is destination addresses.
* The 2nd parameter is source addresses. * The 2nd parameter is source addresses.
* The 3rd parameter is size. * The 3rd parameter is size.
* Note: Sometimes it have no effect, may be need a delay or other option(lock or unlock, etc.) with known reason. * Note: Must disable cache read before using it.
*******************************************************************************/ *******************************************************************************/
typedef struct typedef struct
{ {
uint8_t unknown0; uint8_t header_magic;
uint8_t unknown1; uint8_t segment_count;
enum enum
{ {
MODE_QIO = 0, MODE_QIO = 0,
@ -56,20 +83,31 @@ typedef struct
SIZE_64MBIT = 5, SIZE_64MBIT = 5,
SIZE_128MBIT = 6, SIZE_128MBIT = 6,
} size : 4; } size : 4;
uint32_t entry_point;
uint32_t memory_offset;
uint32_t segment_size;
} ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo; } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo;
SPIFlashInfo flash_get_info(void); uint32_t flash_detect_size_byte(void);
uint8_t flash_get_size(void); uint32_t flash_safe_get_size_byte(void);
uint32_t flash_get_size_byte(void); uint16_t flash_safe_get_sec_num(void);
bool flash_set_size(uint8_t); SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size);
bool flash_set_size_byte(uint32_t); SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size);
uint16_t flash_get_sec_num(void); SpiFlashOpResult flash_safe_erase_sector(uint16 sec);
uint8_t flash_get_mode(void); SPIFlashInfo flash_rom_getinfo(void);
uint32_t flash_get_speed(void); uint8_t flash_rom_get_size_type(void);
uint32_t flash_rom_get_size_byte(void);
bool flash_rom_set_size_type(uint8_t);
bool flash_rom_set_size_byte(uint32_t);
uint16_t flash_rom_get_sec_num(void);
uint8_t flash_rom_get_mode(void);
uint32_t flash_rom_get_speed(void);
bool flash_init_data_written(void); bool flash_init_data_written(void);
bool flash_init_data_default(void); bool flash_init_data_default(void);
bool flash_init_data_blank(void); bool flash_init_data_blank(void);
bool flash_self_destruct(void); bool flash_self_destruct(void);
uint8_t byte_of_aligned_array(const uint8_t* aligned_array, uint32_t index); 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__ #endif // __FLASH_API_H__

View File

@ -160,7 +160,7 @@ int myspiffs_error( int fd ){
return SPIFFS_errno(&fs); return SPIFFS_errno(&fs);
} }
void myspiffs_clearerr( int fd ){ void myspiffs_clearerr( int fd ){
fs.errno = SPIFFS_OK; SPIFFS_clearerr(&fs);
} }
int myspiffs_rename( const char *old, const char *newname ){ int myspiffs_rename( const char *old, const char *newname ){
return SPIFFS_rename(&fs, (char *)old, (char *)newname); return SPIFFS_rename(&fs, (char *)old, (char *)newname);

View File

@ -9,6 +9,10 @@
#ifndef SPIFFS_H_ #ifndef SPIFFS_H_
#define SPIFFS_H_ #define SPIFFS_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "c_stdio.h" #include "c_stdio.h"
#include "spiffs_config.h" #include "spiffs_config.h"
@ -181,7 +185,7 @@ typedef struct {
u32_t fd_count; u32_t fd_count;
// last error // last error
s32_t errno; s32_t err_code;
// current number of free blocks // current number of free blocks
u32_t free_blocks; u32_t free_blocks;
@ -375,9 +379,9 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh);
* Renames a file * Renames a file
* @param fs the file system struct * @param fs the file system struct
* @param old path of file to rename * @param old path of file to rename
* @param new new path of file * @param newPath new path of file
*/ */
s32_t SPIFFS_rename(spiffs *fs, char *old, char *new); s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath);
/** /**
* Returns last error of last file operation. * Returns last error of last file operation.
@ -385,6 +389,12 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new);
*/ */
s32_t SPIFFS_errno(spiffs *fs); s32_t SPIFFS_errno(spiffs *fs);
/**
* Clears last error.
* @param fs the file system struct
*/
void SPIFFS_clearerr(spiffs *fs);
/** /**
* Opens a directory stream corresponding to the given name. * Opens a directory stream corresponding to the given name.
* The stream is positioned at the first entry in the directory. * The stream is positioned at the first entry in the directory.
@ -416,6 +426,21 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
*/ */
s32_t SPIFFS_check(spiffs *fs); s32_t SPIFFS_check(spiffs *fs);
/**
* Returns number of total bytes available and number of used bytes.
* This is an estimation, and depends on if there a many files with little
* data or few files with much data.
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
* run. This indicates a power loss in midst of things. In worst case
* (repeated powerlosses in mending or gc) you might have to delete some files.
*
* @param fs the file system struct
* @param total total number of bytes in filesystem
* @param used used number of bytes in filesystem
*/
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
/** /**
* Check if EOF reached. * Check if EOF reached.
* @param fs the file system struct * @param fs the file system struct
@ -468,4 +493,8 @@ int myspiffs_check( void );
int myspiffs_rename( const char *old, const char *newname ); int myspiffs_rename( const char *old, const char *newname );
size_t myspiffs_size( int fd ); size_t myspiffs_size( int fd );
#if defined(__cplusplus)
}
#endif
#endif /* SPIFFS_H_ */ #endif /* SPIFFS_H_ */

View File

@ -30,19 +30,19 @@ typedef uint8_t u8_t;
// Set generic spiffs debug output call. // Set generic spiffs debug output call.
#ifndef SPIFFS_DGB #ifndef SPIFFS_DGB
#define SPIFFS_DBG(...) #define SPIFFS_DBG(...) //printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for garbage collecting. // Set spiffs debug output call for garbage collecting.
#ifndef SPIFFS_GC_DGB #ifndef SPIFFS_GC_DGB
#define SPIFFS_GC_DBG(...) #define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for caching. // Set spiffs debug output call for caching.
#ifndef SPIFFS_CACHE_DGB #ifndef SPIFFS_CACHE_DGB
#define SPIFFS_CACHE_DBG(...) #define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for system consistency checks. // Set spiffs debug output call for system consistency checks.
#ifndef SPIFFS_CHECK_DGB #ifndef SPIFFS_CHECK_DGB
#define SPIFFS_CHECK_DBG(...) #define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
#endif #endif
// Enable/disable API functions to determine exact number of bytes // Enable/disable API functions to determine exact number of bytes
@ -77,7 +77,7 @@ typedef uint8_t u8_t;
// Define maximum number of gc runs to perform to reach desired free pages. // Define maximum number of gc runs to perform to reach desired free pages.
#ifndef SPIFFS_GC_MAX_RUNS #ifndef SPIFFS_GC_MAX_RUNS
#define SPIFFS_GC_MAX_RUNS 3 #define SPIFFS_GC_MAX_RUNS 5
#endif #endif
// Enable/disable statistics on gc. Debug/test purpose only. // Enable/disable statistics on gc. Debug/test purpose only.

View File

@ -119,16 +119,21 @@ s32_t spiffs_gc_check(
spiffs *fs, spiffs *fs,
u32_t len) { u32_t len) {
s32_t res; s32_t res;
u32_t free_pages = s32_t free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
- fs->stats_p_allocated - fs->stats_p_deleted; - fs->stats_p_allocated - fs->stats_p_deleted;
int tries = 0; int tries = 0;
if (fs->free_blocks > 3 && if (fs->free_blocks > 3 &&
len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) { (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
return SPIFFS_OK; return SPIFFS_OK;
} }
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
return SPIFFS_ERR_FULL;
}
//printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len); //printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len);
do { do {
@ -168,16 +173,22 @@ s32_t spiffs_gc_check(
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
free_pages = free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
- fs->stats_p_allocated - fs->stats_p_deleted; - fs->stats_p_allocated - fs->stats_p_deleted;
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
SPIFFS_GC_DBG("gc_check: finished\n");
//printf("gcing finished %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", free_pages =
// fs->stats_p_allocated + fs->stats_p_deleted, (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
// fs->free_blocks, free_pages, tries, res); - fs->stats_p_allocated - fs->stats_p_deleted;
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
res = SPIFFS_ERR_FULL;
}
SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
fs->stats_p_allocated + fs->stats_p_deleted,
fs->free_blocks, free_pages, tries, res);
return res; return res;
} }

View File

@ -103,7 +103,11 @@ void SPIFFS_unmount(spiffs *fs) {
} }
s32_t SPIFFS_errno(spiffs *fs) { s32_t SPIFFS_errno(spiffs *fs) {
return fs->errno; return fs->err_code;
}
void SPIFFS_clearerr(spiffs *fs) {
fs->err_code = SPIFFS_OK;
} }
s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) { s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) {
@ -314,8 +318,6 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
#endif #endif
} }
SPIFFS_DBG("SPIFFS_write %i %04x offs:%i len %i\n", fh, fd->obj_id, offset, len);
#if SPIFFS_CACHE_WR #if SPIFFS_CACHE_WR
if ((fd->flags & SPIFFS_DIRECT) == 0) { if ((fd->flags & SPIFFS_DIRECT) == 0) {
if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
@ -328,12 +330,13 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
{ {
// boundary violation, write back cache first and allocate new // boundary violation, write back cache first and allocate new
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:&04x, boundary viol, offs:%i size:%i\n", SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd, res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
SPIFFS_API_CHECK_RES(fs, res);
} else { } else {
// writing within cache // writing within cache
alloc_cpage = 0; alloc_cpage = 0;
@ -379,6 +382,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
SPIFFS_API_CHECK_RES(fs, res);
res = spiffs_hydro_write(fs, fd, buf, offset, len); res = spiffs_hydro_write(fs, fd, buf, offset, len);
SPIFFS_API_CHECK_RES(fs, res); SPIFFS_API_CHECK_RES(fs, res);
} }
@ -580,7 +584,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
if (res < SPIFFS_OK) { if (res < SPIFFS_OK) {
fs->errno = res; fs->err_code = res;
} }
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
} }
@ -605,7 +609,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
void SPIFFS_close(spiffs *fs, spiffs_file fh) { void SPIFFS_close(spiffs *fs, spiffs_file fh) {
if (!SPIFFS_CHECK_MOUNT(fs)) { if (!SPIFFS_CHECK_MOUNT(fs)) {
fs->errno = SPIFFS_ERR_NOT_MOUNTED; fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return; return;
} }
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -661,7 +665,7 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) { spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) {
(void)name; (void)name;
if (!SPIFFS_CHECK_MOUNT(fs)) { if (!SPIFFS_CHECK_MOUNT(fs)) {
fs->errno = SPIFFS_ERR_NOT_MOUNTED; fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return 0; return 0;
} }
d->fs = fs; d->fs = fs;
@ -707,7 +711,7 @@ static s32_t spiffs_read_dir_v(
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
if (!SPIFFS_CHECK_MOUNT(d->fs)) { if (!SPIFFS_CHECK_MOUNT(d->fs)) {
d->fs->errno = SPIFFS_ERR_NOT_MOUNTED; d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return 0; return 0;
} }
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -732,7 +736,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
d->entry = entry + 1; d->entry = entry + 1;
ret = e; ret = e;
} else { } else {
d->fs->errno = res; d->fs->err_code = res;
} }
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
return ret; return ret;
@ -760,6 +764,29 @@ s32_t SPIFFS_check(spiffs *fs) {
return res; return res;
} }
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
u32_t blocks = fs->block_count;
u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs);
u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page
if (total) {
*total = total_data_pages * data_page_size;
}
if (used) {
*used = fs->stats_p_allocated * data_page_size;
}
SPIFFS_UNLOCK(fs);
return res;
}
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -878,11 +905,14 @@ s32_t SPIFFS_vis(spiffs *fs) {
} // per block } // per block
spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count); spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count);
spiffs_printf("last_errno: %i\n", fs->errno); spiffs_printf("last_errno: %i\n", fs->err_code);
spiffs_printf("blocks: %i\n", fs->block_count); spiffs_printf("blocks: %i\n", fs->block_count);
spiffs_printf("free_blocks: %i\n", fs->free_blocks); spiffs_printf("free_blocks: %i\n", fs->free_blocks);
spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated);
spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); spiffs_printf("page_delet: %i\n", fs->stats_p_deleted);
u32_t total, used;
SPIFFS_info(fs, &total, &used);
spiffs_printf("used: %i of %i\n", used, total);
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
return res; return res;

View File

@ -614,7 +614,7 @@ s32_t spiffs_object_create(
spiffs_page_object_ix_header oix_hdr; spiffs_page_object_ix_header oix_hdr;
int entry; int entry;
res = spiffs_gc_check(fs, 0); res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
obj_id |= SPIFFS_OBJ_ID_IX_FLAG; obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
@ -811,7 +811,17 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
u32_t written = 0; u32_t written = 0;
res = spiffs_gc_check(fs, len); SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size);
if (offset > fd->size) {
SPIFFS_DBG("append: offset reversed to size\n");
offset = fd->size;
}
res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta
if (res != SPIFFS_OK) {
SPIFFS_DBG("append: gc check fail %i\n", res);
}
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
@ -912,7 +922,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
} }
SPIFFS_DBG("append: %04x found object index at page %04x\n", fd->obj_id, pix); SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
@ -1003,8 +1013,8 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
// update size in object header index page // update size in object header index page
res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id
, offset+written, new_objix_hdr_page, 0, written); , offset+written, new_objix_hdr_page, 0, written, res2);
SPIFFS_CHECK_RES(res2); SPIFFS_CHECK_RES(res2);
} else { } else {
// wrote within object index header page // wrote within object index header page
@ -1042,7 +1052,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
u32_t written = 0; u32_t written = 0;
res = spiffs_gc_check(fs, len); res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
@ -1308,7 +1318,7 @@ s32_t spiffs_object_truncate(
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
spiffs *fs = fd->fs; spiffs *fs = fd->fs;
res = spiffs_gc_check(fs, 0); res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_ix objix_pix = fd->objix_hdr_pix; spiffs_page_ix objix_pix = fd->objix_hdr_pix;
@ -1386,13 +1396,26 @@ s32_t spiffs_object_truncate(
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE;
} }
SPIFFS_DBG("truncate: got data pix %04x\n", data_pix);
if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) {
// delete full data page // delete full data page
res = spiffs_page_data_check(fs, fd, data_pix, data_spix); res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
if (res != SPIFFS_OK) break; if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) {
SPIFFS_DBG("truncate: err validating data pix %i\n", res);
break;
}
if (res == SPIFFS_OK) {
res = spiffs_page_delete(fs, data_pix);
if (res != SPIFFS_OK) {
SPIFFS_DBG("truncate: err deleting data pix %i\n", res);
break;
}
} else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
res = SPIFFS_OK;
}
res = spiffs_page_delete(fs, data_pix);
if (res != SPIFFS_OK) break;
// update current size // update current size
if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) {
cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);

View File

@ -247,19 +247,19 @@
#define SPIFFS_API_CHECK_MOUNT(fs) \ #define SPIFFS_API_CHECK_MOUNT(fs) \
if (!SPIFFS_CHECK_MOUNT((fs))) { \ if (!SPIFFS_CHECK_MOUNT((fs))) { \
(fs)->errno = SPIFFS_ERR_NOT_MOUNTED; \ (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
return -1; \ return -1; \
} }
#define SPIFFS_API_CHECK_RES(fs, res) \ #define SPIFFS_API_CHECK_RES(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->errno = (res); \ (fs)->err_code = (res); \
return -1; \ return -1; \
} }
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->errno = (res); \ (fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \ SPIFFS_UNLOCK(fs); \
return -1; \ return -1; \
} }

7
app/spiffs/test/main.c Normal file
View File

@ -0,0 +1,7 @@
#include "testrunner.h"
#include <stdlib.h>
int main(int argc, char **args) {
run_tests(argc, args);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,36 @@
/*
* params_test.h
*
* Created on: May 26, 2013
* Author: petera
*/
#ifndef PARAMS_TEST_H_
#define PARAMS_TEST_H_
// total emulated spi flash size
#define PHYS_FLASH_SIZE (16*1024*1024)
// spiffs file system size
#define SPIFFS_FLASH_SIZE (2*1024*1024)
// spiffs file system offset in emulated spi flash
#define SPIFFS_PHYS_ADDR (4*1024*1024)
#define SECTOR_SIZE 65536
#define LOG_BLOCK (SECTOR_SIZE*2)
#define LOG_PAGE (SECTOR_SIZE/256)
#define FD_BUF_SIZE 64*6
#define CACHE_BUF_SIZE (LOG_PAGE + 32)*8
#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__);
typedef signed int s32_t;
typedef unsigned int u32_t;
typedef signed short s16_t;
typedef unsigned short u16_t;
typedef signed char s8_t;
typedef unsigned char u8_t;
void real_assert(int c, const char *n, const char *file, int l);
#endif /* PARAMS_TEST_H_ */

View File

@ -0,0 +1,160 @@
/*
* test_bugreports.c
*
* Created on: Mar 8, 2015
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(bug_tests)
void setup() {
_setup_test_only();
}
void teardown() {
_teardown();
}
TEST(nodemcu_full_fs_1) {
fs_reset_specific(0, 4096*20, 4096, 4096, 256);
int res;
spiffs_file fd;
printf(" fill up system by writing one byte a lot\n");
fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
int i;
spiffs_stat s;
res = SPIFFS_OK;
for (i = 0; i < 100*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
int errno = SPIFFS_errno(FS);
int res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(errno == SPIFFS_ERR_FULL);
SPIFFS_close(FS, fd);
printf(" remove big file\n");
res = SPIFFS_remove(FS, "test1.txt");
printf("res:%i errno:%i\n",res, SPIFFS_errno(FS));
TEST_CHECK(res == SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == -1);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res2 = SPIFFS_stat(FS, "test1.txt", &s);
TEST_CHECK(res2 == -1);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
printf(" create small file\n");
fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_OK;
for (i = 0; res >= 0 && i < 1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(res >= SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 1000);
SPIFFS_close(FS, fd);
return TEST_RES_OK;
} TEST_END(nodemcu_full_fs_1)
TEST(nodemcu_full_fs_2) {
fs_reset_specific(0, 4096*22, 4096, 4096, 256);
int res;
spiffs_file fd;
printf(" fill up system by writing one byte a lot\n");
fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
int i;
spiffs_stat s;
res = SPIFFS_OK;
for (i = 0; i < 100*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
int errno = SPIFFS_errno(FS);
int res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(errno == SPIFFS_ERR_FULL);
SPIFFS_close(FS, fd);
res2 = SPIFFS_stat(FS, "test1.txt", &s);
TEST_CHECK(res2 == SPIFFS_OK);
SPIFFS_clearerr(FS);
printf(" create small file\n");
fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
TEST_CHECK(fd > 0);
for (i = 0; i < 1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 0);
SPIFFS_clearerr(FS);
printf(" remove files\n");
res = SPIFFS_remove(FS, "test1.txt");
TEST_CHECK(res == SPIFFS_OK);
res = SPIFFS_remove(FS, "test2.txt");
TEST_CHECK(res == SPIFFS_OK);
printf(" create medium file\n");
fd = SPIFFS_open(FS, "test3.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
TEST_CHECK(fd > 0);
for (i = 0; i < 20*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 20*1000);
return TEST_RES_OK;
} TEST_END(nodemcu_full_fs_2)
SUITE_END(bug_tests)

View File

@ -0,0 +1,418 @@
/*
* test_dev.c
*
* Created on: Jul 14, 2013
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(check_tests)
void setup() {
_setup();
}
void teardown() {
_teardown();
}
TEST(evil_write) {
fs_set_validate_flashing(0);
printf("writing corruption to block 1 data range (leaving lu intact)\n");
u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) -
SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS));
u8_t *corruption = malloc(data_range);
memrand(corruption, data_range);
u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS);
area_write(addr, corruption, data_range);
free(corruption);
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
printf("CHECK1-----------------\n");
SPIFFS_check(FS);
printf("CHECK2-----------------\n");
SPIFFS_check(FS);
printf("CHECK3-----------------\n");
SPIFFS_check(FS);
res = test_create_and_write_file("file2", size, size);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(evil_write)
TEST(lu_check1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index 1
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
TEST_CHECK(res >= 0);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
return TEST_RES_OK;
} TEST_END(lu_check1)
TEST(page_cons1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 2 to a bad page
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = 0x55;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+2, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons1)
TEST(page_cons2) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// find data page span index 0
spiffs_page_ix dpix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix);
TEST_CHECK(res >= 0);
// set object index entry 1+2 to a data page 0
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = dpix;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons2)
TEST(page_cons3) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 1+2 lookup page
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2);
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons3)
TEST(page_cons_final) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify page header, make unfinalized
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
TEST_CHECK(res >= 0);
// set page span ix 1 as unfinalized
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
u8_t flags;
area_read(addr, (u8_t*)&flags, 1);
flags |= SPIFFS_PH_FLAG_FINAL;
area_write(addr, (u8_t*)&flags, 1);
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons_final)
TEST(index_cons1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" deleting lu entry pix %04x\n", pix);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons1)
TEST(index_cons2) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" writing lu entry for index page, ix %04x, as data page\n", pix);
spiffs_obj_id obj_id = 0x1234;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons2)
TEST(index_cons3) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" setting lu entry pix %04x to another index page\n", pix);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons3)
TEST(index_cons4) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header, flags
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" cue objix hdr deletion in page %04x\n", pix);
// set flags as deleting ix header
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE);
area_write(addr, (u8_t*)&flags, 1);
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
return TEST_RES_OK;
} TEST_END(index_cons4)
SUITE_END(check_tests)

120
app/spiffs/test/test_dev.c Normal file
View File

@ -0,0 +1,120 @@
/*
* test_dev.c
*
* Created on: Jul 14, 2013
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(dev_tests)
void setup() {
_setup();
}
void teardown() {
_teardown();
}
TEST(interrupted_write) {
char *name = "interrupt";
char *name2 = "interrupt2";
int res;
spiffs_file fd;
const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8;
u8_t *buf = malloc(sz);
memrand(buf, sz);
printf(" create reference file\n");
fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
clear_flash_ops_log();
res = SPIFFS_write(FS, fd, buf, sz);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
u32_t written = get_flash_ops_log_write_bytes();
printf(" written bytes: %i\n", written);
printf(" create error file\n");
fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
clear_flash_ops_log();
invoke_error_after_write_bytes(written/2, 0);
res = SPIFFS_write(FS, fd, buf, sz);
SPIFFS_close(FS, fd);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST);
clear_flash_ops_log();
#if SPIFFS_CACHE
// delete all cache
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
printf(" read error file\n");
fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
printf(" file size: %i\n", s.size);
if (s.size > 0) {
u8_t *buf2 = malloc(s.size);
res = SPIFFS_read(FS, fd, buf2, s.size);
TEST_CHECK(res >= 0);
u32_t ix = 0;
for (ix = 0; ix < s.size; ix += 16) {
int i;
printf(" ");
for (i = 0; i < 16; i++) {
printf("%02x", buf[ix+i]);
}
printf(" ");
for (i = 0; i < 16; i++) {
printf("%02x", buf2[ix+i]);
}
printf("\n");
}
free(buf2);
}
SPIFFS_close(FS, fd);
printf(" FS check\n");
SPIFFS_check(FS);
printf(" read error file again\n");
fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
printf(" file size: %i\n", s.size);
printf(" write file\n");
res = SPIFFS_write(FS, fd, buf, sz);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
free(buf);
return TEST_RES_OK;
} TEST_END(interrupted_write)
SUITE_END(dev_tests)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,746 @@
/*
* test_spiffs.c
*
* Created on: Jun 19, 2013
* Author: petera
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "params_test.h"
#include "spiffs.h"
#include "spiffs_nucleus.h"
#include "testrunner.h"
#include "test_spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
static unsigned char area[PHYS_FLASH_SIZE];
static int erases[256];
static char _path[256];
static u32_t bytes_rd = 0;
static u32_t bytes_wr = 0;
static u32_t reads = 0;
static u32_t writes = 0;
static u32_t error_after_bytes_written = 0;
static u32_t error_after_bytes_read = 0;
static char error_after_bytes_written_once_only = 0;
static char error_after_bytes_read_once_only = 0;
static char log_flash_ops = 1;
static u32_t fs_check_fixes = 0;
spiffs __fs;
static u8_t _work[LOG_PAGE*2];
static u8_t _fds[FD_BUF_SIZE];
static u8_t _cache[CACHE_BUF_SIZE];
static int check_valid_flash = 1;
#define TEST_PATH "test_data/"
char *make_test_fname(const char *name) {
sprintf(_path, "%s%s", TEST_PATH, name);
return _path;
}
void clear_test_path() {
DIR *dp;
struct dirent *ep;
dp = opendir(TEST_PATH);
if (dp != NULL) {
while ((ep = readdir(dp))) {
if (ep->d_name[0] != '.') {
sprintf(_path, "%s%s", TEST_PATH, ep->d_name);
remove(_path);
}
}
closedir(dp);
}
}
static s32_t _read(u32_t addr, u32_t size, u8_t *dst) {
if (log_flash_ops) {
bytes_rd += size;
reads++;
if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) {
if (error_after_bytes_read_once_only) {
error_after_bytes_read = 0;
}
return SPIFFS_ERR_TEST;
}
}
if (addr < __fs.cfg.phys_addr) {
printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
exit(0);
}
if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
exit(0);
}
memcpy(dst, &area[addr], size);
return 0;
}
static s32_t _write(u32_t addr, u32_t size, u8_t *src) {
int i;
//printf("wr %08x %i\n", addr, size);
if (log_flash_ops) {
bytes_wr += size;
writes++;
if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) {
if (error_after_bytes_written_once_only) {
error_after_bytes_written = 0;
}
return SPIFFS_ERR_TEST;
}
}
if (addr < __fs.cfg.phys_addr) {
printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
exit(0);
}
if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
exit(0);
}
for (i = 0; i < size; i++) {
if (((addr + i) & (__fs.cfg.log_page_size-1)) != offsetof(spiffs_page_header, flags)) {
if (check_valid_flash && ((area[addr + i] ^ src[i]) & src[i])) {
printf("trying to write %02x to %02x at addr %08x\n", src[i], area[addr + i], addr+i);
spiffs_page_ix pix = (addr + i) / LOG_PAGE;
dump_page(&__fs, pix);
return -1;
}
}
area[addr + i] &= src[i];
}
return 0;
}
static s32_t _erase(u32_t addr, u32_t size) {
if (addr & (__fs.cfg.phys_erase_block-1)) {
printf("trying to erase at addr %08x, out of boundary\n", addr);
return -1;
}
if (size & (__fs.cfg.phys_erase_block-1)) {
printf("trying to erase at with size %08x, out of boundary\n", size);
return -1;
}
erases[(addr-__fs.cfg.phys_addr)/__fs.cfg.phys_erase_block]++;
memset(&area[addr], 0xff, size);
return 0;
}
void hexdump_mem(u8_t *b, u32_t len) {
while (len--) {
if ((((intptr_t)b)&0x1f) == 0) {
printf("\n");
}
printf("%02x", *b++);
}
printf("\n");
}
void hexdump(u32_t addr, u32_t len) {
int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32);
u32_t a;
for (a = addr - remainder; a < addr+len; a++) {
if ((a & 0x1f) == 0) {
if (a != addr) {
printf(" ");
int j;
for (j = 0; j < 32; j++) {
if (a-32+j < addr)
printf(" ");
else {
printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
}
}
}
printf("%s %08x: ", a<=addr ? "":"\n", a);
}
if (a < addr) {
printf(" ");
} else {
printf("%02x", area[a]);
}
}
int j;
printf(" ");
for (j = 0; j < 32; j++) {
if (a-32+j < addr)
printf(" ");
else {
printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
}
}
printf("\n");
}
void dump_page(spiffs *fs, spiffs_page_ix p) {
printf("page %04x ", p);
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p);
if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
// obj lu page
printf("OBJ_LU");
} else {
u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , p)) +
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, p) * sizeof(spiffs_obj_id);
spiffs_obj_id obj_id = *((spiffs_obj_id *)&area[obj_id_addr]);
// data page
spiffs_page_header *ph = (spiffs_page_header *)&area[addr];
printf("DATA %04x:%04x ", obj_id, ph->span_ix);
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "FIN " : "fin ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "DEL " : "del ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "IDX " : "idx ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_USED) == 0) ? "USD " : "usd ");
printf("%s ", ((ph->flags & SPIFFS_PH_FLAG_IXDELE) == 0) ? "IDL " : "idl ");
if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
// object index
printf("OBJ_IX");
if (ph->span_ix == 0) {
printf("_HDR ");
spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&area[addr];
printf("'%s' %i bytes type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type);
}
} else {
// data page
printf("CONTENT");
}
}
printf("\n");
u32_t len = fs->cfg.log_page_size;
hexdump(addr, len);
}
void area_write(u32_t addr, u8_t *buf, u32_t size) {
int i;
for (i = 0; i < size; i++) {
area[addr + i] = *buf++;
}
}
void area_read(u32_t addr, u8_t *buf, u32_t size) {
int i;
for (i = 0; i < size; i++) {
*buf++ = area[addr + i];
}
}
void dump_erase_counts(spiffs *fs) {
spiffs_block_ix bix;
printf(" BLOCK |\n");
printf(" AGE COUNT|\n");
for (bix = 0; bix < fs->block_count; bix++) {
printf("----%3i ----|", bix);
}
printf("\n");
for (bix = 0; bix < fs->block_count; bix++) {
spiffs_obj_id erase_mark;
_spiffs_rd(fs, 0, 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&erase_mark);
if (erases[bix] == 0) {
printf(" |");
} else {
printf("%7i %4i|", (fs->max_erase_count - erase_mark), erases[bix]);
}
}
printf("\n");
}
void dump_flash_access_stats() {
printf(" RD: %10i reads %10i bytes %10i avg bytes/read\n", reads, bytes_rd, reads == 0 ? 0 : (bytes_rd / reads));
printf(" WR: %10i writes %10i bytes %10i avg bytes/write\n", writes, bytes_wr, writes == 0 ? 0 : (bytes_wr / writes));
}
static u32_t old_perc = 999;
static void spiffs_check_cb_f(spiffs_check_type type, spiffs_check_report report,
u32_t arg1, u32_t arg2) {
/* if (report == SPIFFS_CHECK_PROGRESS && old_perc != arg1) {
old_perc = arg1;
printf("CHECK REPORT: ");
switch(type) {
case SPIFFS_CHECK_LOOKUP:
printf("LU "); break;
case SPIFFS_CHECK_INDEX:
printf("IX "); break;
case SPIFFS_CHECK_PAGE:
printf("PA "); break;
}
printf("%i%%\n", arg1 * 100 / 256);
}*/
if (report != SPIFFS_CHECK_PROGRESS) {
if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++;
printf(" check: ");
switch (type) {
case SPIFFS_CHECK_INDEX:
printf("INDEX "); break;
case SPIFFS_CHECK_LOOKUP:
printf("LOOKUP "); break;
case SPIFFS_CHECK_PAGE:
printf("PAGE "); break;
default:
printf("???? "); break;
}
if (report == SPIFFS_CHECK_ERROR) {
printf("ERROR %i", arg1);
} else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) {
printf("DELETE BAD FILE %04x", arg1);
} else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) {
printf("DELETE ORPHANED INDEX %04x", arg1);
} else if (report == SPIFFS_CHECK_DELETE_PAGE) {
printf("DELETE PAGE %04x", arg1);
} else if (report == SPIFFS_CHECK_FIX_INDEX) {
printf("FIX INDEX %04x:%04x", arg1, arg2);
} else if (report == SPIFFS_CHECK_FIX_LOOKUP) {
printf("FIX INDEX %04x:%04x", arg1, arg2);
} else {
printf("??");
}
printf("\n");
}
}
void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
u32_t phys_sector_size,
u32_t log_block_size, u32_t log_page_size) {
memset(area, 0xcc, sizeof(area));
memset(&area[phys_addr], 0xff, phys_size);
spiffs_config c;
c.hal_erase_f = _erase;
c.hal_read_f = _read;
c.hal_write_f = _write;
c.log_block_size = log_block_size;
c.log_page_size = log_page_size;
c.phys_addr = phys_addr;
c.phys_erase_block = phys_sector_size;
c.phys_size = phys_size;
memset(erases,0,sizeof(erases));
memset(_cache,0,sizeof(_cache));
SPIFFS_mount(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache), spiffs_check_cb_f);
clear_flash_ops_log();
log_flash_ops = 1;
fs_check_fixes = 0;
}
void fs_reset() {
fs_reset_specific(SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE);
}
void set_flash_ops_log(int enable) {
log_flash_ops = enable;
}
void clear_flash_ops_log() {
bytes_rd = 0;
bytes_wr = 0;
reads = 0;
writes = 0;
error_after_bytes_read = 0;
error_after_bytes_written = 0;
}
u32_t get_flash_ops_log_read_bytes() {
return bytes_rd;
}
u32_t get_flash_ops_log_write_bytes() {
return bytes_wr;
}
void invoke_error_after_read_bytes(u32_t b, char once_only) {
error_after_bytes_read = b;
error_after_bytes_read_once_only = once_only;
}
void invoke_error_after_write_bytes(u32_t b, char once_only) {
error_after_bytes_written = b;
error_after_bytes_written_once_only = once_only;
}
void fs_set_validate_flashing(int i) {
check_valid_flash = i;
}
void real_assert(int c, const char *n, const char *file, int l) {
if (c == 0) {
printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l);
printf("fs errno:%i\n", __fs.err_code);
exit(0);
}
}
int read_and_verify(char *name) {
s32_t res;
int fd = SPIFFS_open(&__fs, name, SPIFFS_RDONLY, 0);
if (fd < 0) {
printf(" read_and_verify: could not open file %s\n", name);
return fd;
}
return read_and_verify_fd(fd, name);
}
int read_and_verify_fd(spiffs_file fd, char *name) {
s32_t res;
int pfd = open(make_test_fname(name), O_RDONLY);
spiffs_stat s;
res = SPIFFS_fstat(&__fs, fd, &s);
if (res < 0) {
printf(" read_and_verify: could not stat file %s\n", name);
return res;
}
if (s.size == 0) {
SPIFFS_close(&__fs, fd);
close(pfd);
return 0;
}
//printf("verifying %s, len %i\n", name, s.size);
int offs = 0;
u8_t buf_d[256];
u8_t buf_v[256];
while (offs < s.size) {
int read_len = MIN(s.size - offs, sizeof(buf_d));
res = SPIFFS_read(&__fs, fd, buf_d, read_len);
if (res < 0) {
printf(" read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size);
return res;
}
int pres = read(pfd, buf_v, read_len);
(void)pres;
//printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres);
int i;
int veri_ok = 1;
for (i = 0; veri_ok && i < read_len; i++) {
if (buf_d[i] != buf_v[i]) {
printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]);
int j = MAX(0, i-16);
int k = MIN(sizeof(buf_d), i+16);
k = MIN(s.size-offs, k);
int l;
for (l = j; l < k; l++) {
printf("%c", buf_d[l] > 31 ? buf_d[l] : '.');
}
printf("\n");
for (l = j; l < k; l++) {
printf("%c", buf_v[l] > 31 ? buf_v[l] : '.');
}
printf("\n");
veri_ok = 0;
}
}
if (!veri_ok) {
SPIFFS_close(&__fs, fd);
close(pfd);
printf("data mismatch\n");
return -1;
}
offs += read_len;
}
SPIFFS_close(&__fs, fd);
close(pfd);
return 0;
}
static void test_on_stop(test *t) {
printf(" spiffs errno:%i\n", SPIFFS_errno(&__fs));
#if SPIFFS_TEST_VISUALISATION
SPIFFS_vis(FS);
#endif
}
void memrand(u8_t *b, int len) {
int i;
for (i = 0; i < len; i++) {
b[i] = rand();
}
}
int test_create_file(char *name) {
spiffs_stat s;
spiffs_file fd;
int res = SPIFFS_creat(FS, name, 0);
CHECK_RES(res);
fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
CHECK(fd >= 0);
res = SPIFFS_fstat(FS, fd, &s);
CHECK_RES(res);
CHECK(strcmp((char*)s.name, name) == 0);
CHECK(s.size == 0);
SPIFFS_close(FS, fd);
return 0;
}
int test_create_and_write_file(char *name, int size, int chunk_size) {
int res;
spiffs_file fd;
printf(" create and write %s", name);
res = test_create_file(name);
if (res < 0) {
printf(" failed creation, %i\n",res);
}
CHECK(res >= 0);
fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
if (res < 0) {
printf(" failed open, %i\n",res);
}
CHECK(fd >= 0);
int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
int offset = 0;
int mark = 0;
while (offset < size) {
int len = MIN(size-offset, chunk_size);
if (offset > mark) {
mark += size/16;
printf(".");
fflush(stdout);
}
u8_t *buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
write(pfd, buf, len);
free(buf);
if (res < 0) {
printf("\n error @ offset %i, res %i\n", offset, res);
}
offset += len;
CHECK(res >= 0);
}
printf("\n");
close(pfd);
spiffs_stat stat;
res = SPIFFS_fstat(FS, fd, &stat);
if (res < 0) {
printf(" failed fstat, %i\n",res);
}
CHECK(res >= 0);
if (stat.size != size) {
printf(" failed size, %i != %i\n", stat.size, size);
}
CHECK(stat.size == size);
SPIFFS_close(FS, fd);
return 0;
}
#if SPIFFS_CACHE
#if SPIFFS_CACHE_STATS
static u32_t chits_tot = 0;
static u32_t cmiss_tot = 0;
#endif
#endif
void _setup_test_only() {
fs_set_validate_flashing(1);
test_init(test_on_stop);
}
void _setup() {
fs_reset();
_setup_test_only();
}
void _teardown() {
printf(" free blocks : %i of %i\n", (FS)->free_blocks, (FS)->block_count);
printf(" pages allocated : %i\n", (FS)->stats_p_allocated);
printf(" pages deleted : %i\n", (FS)->stats_p_deleted);
#if SPIFFS_GC_STATS
printf(" gc runs : %i\n", (FS)->stats_gc_runs);
#endif
#if SPIFFS_CACHE
#if SPIFFS_CACHE_STATS
chits_tot += (FS)->cache_hits;
cmiss_tot += (FS)->cache_misses;
printf(" cache hits : %i (sum %i)\n", (FS)->cache_hits, chits_tot);
printf(" cache misses : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot);
printf(" cache utiliz : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot)));
#endif
#endif
dump_flash_access_stats();
clear_flash_ops_log();
#if SPIFFS_GC_STATS
if ((FS)->stats_gc_runs > 0)
#endif
dump_erase_counts(FS);
printf(" fs consistency check:\n");
SPIFFS_check(FS);
clear_test_path();
//hexdump_mem(&area[SPIFFS_PHYS_ADDR - 16], 32);
//hexdump_mem(&area[SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE - 16], 32);
}
u32_t tfile_get_size(tfile_size s) {
switch (s) {
case EMPTY:
return 0;
case SMALL:
return SPIFFS_DATA_PAGE_SIZE(FS)/2;
case MEDIUM:
return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
case LARGE:
return (FS)->cfg.phys_size/3;
}
return 0;
}
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) {
int res;
tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files);
memset(tfiles, 0, sizeof(tfile) * max_concurrent_files);
int run = 0;
int cur_config_ix = 0;
char name[32];
while (run < max_runs) {
if (dbg) printf(" run %i/%i\n", run, max_runs);
int i;
for (i = 0; i < max_concurrent_files; i++) {
sprintf(name, "file%i_%i", (1+run), i);
tfile *tf = &tfiles[i];
if (tf->state == 0 && cur_config_ix < cfg_count) {
// create a new file
strcpy(tf->name, name);
tf->state = 1;
tf->cfg = cfgs[cur_config_ix];
int size = tfile_get_size(tf->cfg.tsize);
if (dbg) printf(" create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size);
if (tf->cfg.tsize == EMPTY) {
res = SPIFFS_creat(FS, name, 0);
CHECK_RES(res);
int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
close(pfd);
int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0);
CHECK(fd > 0);
tf->fd = fd;
} else {
int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
CHECK(fd > 0);
extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0;
int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
tf->fd = fd;
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, fd, buf, size);
CHECK_RES(res);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(name);
CHECK_RES(res);
}
cur_config_ix++;
} else if (tf->state > 0) {
// hande file lifecycle
switch (tf->cfg.ttype) {
case UNTAMPERED: {
break;
}
case APPENDED: {
if (dbg) printf(" appending %s\n", tf->name);
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, tf->fd, buf, size);
CHECK_RES(res);
int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
case MODIFIED: {
if (dbg) printf(" modify %s\n", tf->name);
spiffs_stat stat;
res = SPIFFS_fstat(FS, tf->fd, &stat);
CHECK_RES(res);
int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3;
int offs = (stat.size / tf->cfg.tlife) * tf->state;
res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET);
CHECK_RES(res);
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, tf->fd, buf, size);
CHECK_RES(res);
int pfd = open(make_test_fname(tf->name), O_RDWR);
lseek(pfd, offs, SEEK_SET);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
case REWRITTEN: {
if (tf->fd > 0) {
SPIFFS_close(FS, tf->fd);
}
if (dbg) printf(" rewriting %s\n", tf->name);
spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
CHECK(fd > 0);
int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
tf->fd = fd;
int size = tfile_get_size(tf->cfg.tsize);
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, fd, buf, size);
CHECK_RES(res);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
}
tf->state++;
if (tf->state > tf->cfg.tlife) {
// file outlived its time, kill it
if (tf->fd > 0) {
SPIFFS_close(FS, tf->fd);
}
if (dbg) printf(" removing %s\n", tf->name);
res = read_and_verify(tf->name);
CHECK_RES(res);
res = SPIFFS_remove(FS, tf->name);
CHECK_RES(res);
remove(make_test_fname(tf->name));
memset(tf, 0, sizeof(tf));
}
}
}
run++;
}
free(tfiles);
return 0;
}

View File

@ -0,0 +1,90 @@
/*
* test_spiffs.h
*
* Created on: Jun 19, 2013
* Author: petera
*/
#ifndef TEST_SPIFFS_H_
#define TEST_SPIFFS_H_
#include "spiffs.h"
#define FS &__fs
extern spiffs __fs;
#define CHECK(r) if (!(r)) return -1;
#define CHECK_RES(r) if (r < 0) return -1;
#define FS_PURE_DATA_PAGES(fs) \
((fs)->cfg.phys_size / (fs)->cfg.log_page_size - (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs))
#define FS_PURE_DATA_SIZE(fs) \
FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs)
typedef enum {
EMPTY,
SMALL,
MEDIUM,
LARGE,
} tfile_size;
typedef enum {
UNTAMPERED,
APPENDED,
MODIFIED,
REWRITTEN,
} tfile_type;
typedef enum {
SHORT = 4,
NORMAL = 20,
LONG = 100,
} tfile_life;
typedef struct {
tfile_size tsize;
tfile_type ttype;
tfile_life tlife;
} tfile_conf;
typedef struct {
int state;
spiffs_file fd;
tfile_conf cfg;
char name[32];
} tfile;
void fs_reset();
void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
u32_t phys_sector_size,
u32_t log_block_size, u32_t log_page_size);
int read_and_verify(char *name);
int read_and_verify_fd(spiffs_file fd, char *name);
void dump_page(spiffs *fs, spiffs_page_ix p);
void hexdump(u32_t addr, u32_t len);
char *make_test_fname(const char *name);
void clear_test_path();
void area_write(u32_t addr, u8_t *buf, u32_t size);
void area_read(u32_t addr, u8_t *buf, u32_t size);
void dump_erase_counts(spiffs *fs);
void dump_flash_access_stats();
void set_flash_ops_log(int enable);
void clear_flash_ops_log();
u32_t get_flash_ops_log_read_bytes();
u32_t get_flash_ops_log_write_bytes();
void invoke_error_after_read_bytes(u32_t b, char once_only);
void invoke_error_after_write_bytes(u32_t b, char once_only);
void memrand(u8_t *b, int len);
int test_create_file(char *name);
int test_create_and_write_file(char *name, int size, int chunk_size);
void _setup();
void _setup_test_only();
void _teardown();
u32_t tfile_get_size(tfile_size s);
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg);
#endif /* TEST_SPIFFS_H_ */

View File

@ -0,0 +1,175 @@
/*
* testrunner.c
*
* Created on: Jun 18, 2013
* Author: petera
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include "testrunner.h"
static struct {
test *tests;
test *_last_test;
int test_count;
void (*on_stop)(test *t);
test_res *failed;
test_res *failed_last;
test_res *stopped;
test_res *stopped_last;
FILE *spec;
} test_main;
void test_init(void (*on_stop)(test *t)) {
test_main.on_stop = on_stop;
}
static char check_spec(char *name) {
if (test_main.spec) {
fseek(test_main.spec, 0, SEEK_SET);
char *line = NULL;
size_t sz;
ssize_t read;
while ((read = getline(&line, &sz, test_main.spec)) != -1) {
if (strncmp(line, name, strlen(name)) == 0) {
free(line);
return 1;
}
}
free(line);
return 0;
} else {
return 1;
}
}
void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) {
if (f == 0) return;
if (!check_spec(name)) return;
DBGT("adding test %s\n", name);
test *t = malloc(sizeof(test));
memset(t, 0, sizeof(test));
t->f = f;
strcpy(t->name, name);
t->setup = setup;
t->teardown = teardown;
if (test_main.tests == 0) {
test_main.tests = t;
} else {
test_main._last_test->_next = t;
}
test_main._last_test = t;
test_main.test_count++;
}
static void add_res(test *t, test_res **head, test_res **last) {
test_res *tr = malloc(sizeof(test_res));
memset(tr,0,sizeof(test_res));
strcpy(tr->name, t->name);
if (*head == 0) {
*head = tr;
} else {
(*last)->_next = tr;
}
*last = tr;
}
static void dump_res(test_res **head) {
test_res *tr = (*head);
while (tr) {
test_res *next_tr = tr->_next;
printf(" %s\n", tr->name);
free(tr);
tr = next_tr;
}
}
void run_tests(int argc, char **args) {
memset(&test_main, 0, sizeof(test_main));
if (argc > 1) {
printf("running tests from %s\n", args[1]);
FILE *fd = fopen(args[1], "r");
if (fd == NULL) {
printf("%s not found\n", args[1]);
exit(EXIT_FAILURE);
}
test_main.spec = fd;
}
DBGT("adding suites...\n");
add_suites();
DBGT("%i tests added\n", test_main.test_count);
if (test_main.spec) {
fclose(test_main.spec);
}
if (test_main.test_count == 0) {
printf("No tests to run\n");
return;
}
int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
DBGT("running tests...\n");
int ok = 0;
int failed = 0;
int stopped = 0;
test *cur_t = test_main.tests;
int i = 1;
while (cur_t) {
cur_t->setup(cur_t);
test *next_test = cur_t->_next;
DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name);
i++;
int res = cur_t->f(cur_t);
cur_t->teardown(cur_t);
int fd = res == TEST_RES_OK ? fd_success : fd_bad;
write(fd, cur_t->name, strlen(cur_t->name));
write(fd, "\n", 1);
switch (res) {
case TEST_RES_OK:
ok++;
printf(" .. ok\n");
break;
case TEST_RES_FAIL:
failed++;
printf(" .. FAILED\n");
if (test_main.on_stop) test_main.on_stop(cur_t);
add_res(cur_t, &test_main.failed, &test_main.failed_last);
break;
case TEST_RES_ASSERT:
stopped++;
printf(" .. ABORTED\n");
if (test_main.on_stop) test_main.on_stop(cur_t);
add_res(cur_t, &test_main.stopped, &test_main.stopped_last);
break;
}
free(cur_t);
cur_t = next_test;
}
close(fd_success);
close(fd_bad);
DBGT("ran %i tests\n", test_main.test_count);
printf("Test report, %i tests\n", test_main.test_count);
printf("%i succeeded\n", ok);
printf("%i failed\n", failed);
dump_res(&test_main.failed);
printf("%i stopped\n", stopped);
dump_res(&test_main.stopped);
if (ok < test_main.test_count) {
printf("\nFAILED\n");
} else {
printf("\nALL TESTS OK\n");
}
}

View File

@ -0,0 +1,110 @@
/*
* testrunner.h
*
* Created on: Jun 19, 2013
* Author: petera
*/
/*
SUITE(mysuite)
void setup(test *t) {}
void teardown(test *t) {}
TEST(mytest) {
printf("mytest runs now..\n");
return 0;
} TEST_END(mytest)
SUITE_END(mysuite)
SUITE(mysuite2)
void setup(test *t) {}
void teardown(test *t) {}
TEST(mytest2a) {
printf("mytest2a runs now..\n");
return 0;
} TEST_END(mytest2a)
TEST(mytest2b) {
printf("mytest2b runs now..\n");
return 0;
} TEST_END(mytest2b)
SUITE_END(mysuite2)
void add_suites() {
ADD_SUITE(mysuite);
ADD_SUITE(mysuite2);
}
*/
#ifndef TESTS_H_
#define TESTS_H_
#define TEST_RES_OK 0
#define TEST_RES_FAIL -1
#define TEST_RES_ASSERT -2
struct test_s;
typedef int (*test_f)(struct test_s *t);
typedef struct test_s {
test_f f;
char name[256];
void *data;
void (*setup)(struct test_s *t);
void (*teardown)(struct test_s *t);
struct test_s *_next;
} test;
typedef struct test_res_s {
char name[256];
struct test_res_s *_next;
} test_res;
#define TEST_CHECK(x) if (!(x)) { \
printf(" TEST FAIL %s:%i\n", __FILE__, __LINE__); \
goto __fail_stop; \
}
#define TEST_ASSERT(x) if (!(x)) { \
printf(" TEST ASSERT %s:%i\n", __FILE__, __LINE__); \
goto __fail_assert; \
}
#define DBGT(...) printf(__VA_ARGS__)
#define str(s) #s
#define SUITE(sui) \
extern void __suite_##sui() {
#define SUITE_END(sui) \
}
#define ADD_SUITE(sui) \
__suite_##sui();
#define TEST(tf) \
int tf(struct test_s *t) { do
#define TEST_END(tf) \
while(0); \
__fail_stop: return TEST_RES_FAIL; \
__fail_assert: return TEST_RES_ASSERT; \
} \
add_test(tf, str(tf), setup, teardown);
void add_suites();
void test_init(void (*on_stop)(test *t));
void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t));
void run_tests(int argc, char **args);
#endif /* TESTS_H_ */

View File

@ -0,0 +1,15 @@
/*
* testsuites.c
*
* Created on: Jun 19, 2013
* Author: petera
*/
#include "testrunner.h"
void add_suites() {
//ADD_SUITE(dev_tests);
ADD_SUITE(check_tests);
ADD_SUITE(hydrogen_tests)
ADD_SUITE(bug_tests)
}

View File

@ -60,7 +60,19 @@ void nodemcu_init(void)
NODE_DBG("Can not init platform for modules.\n"); NODE_DBG("Can not init platform for modules.\n");
return; 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() ){ if( !flash_init_data_written() ){
NODE_ERR("Restore init data.\n"); NODE_ERR("Restore init data.\n");
// Flash init data at FLASHSIZE - 0x04000 Byte. // Flash init data at FLASHSIZE - 0x04000 Byte.

View File

@ -355,11 +355,29 @@ cs:listen(5683)
myvar=1 myvar=1
cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1 cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1
function myfun() -- function should tack one string, return one string.
function myfun(payload)
print("myfun called") print("myfun called")
respond = "hello"
return respond
end end
cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun
cc = coap.Client() cc = coap.Client()
cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core")
cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello")
file.open("test1.txt", "a+") for i = 1, 100*1000 do file.write("x") end file.close() print("Done.")
for n,s in pairs(file.list()) do print(n.." size: "..s) end
file.remove("test1.txt")
for n,s in pairs(file.list()) do print(n.." size: "..s) end
file.open("test2.txt", "a+") for i = 1, 1*1000 do file.write("x") end file.close() print("Done.")
function TestDNSLeak()
c=net.createConnection(net.TCP, 0)
c:connect(80, "bad-name.tlddfdf")
tmr.alarm(1, 3000, 0, function() print("hack socket close, MEM: "..node.heap()) c:close() end) -- socket timeout hack
print("MEM: "..node.heap())
end

View File

@ -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)

View File

@ -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]])

View File

@ -0,0 +1,41 @@
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
-- 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

View File

@ -3,6 +3,7 @@
-- NODEMCU TEAM -- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT -- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <vowstar@nodemcu.com> -- Vowstar <vowstar@nodemcu.com>
-- 2015/02/14 sza2 <sza2trash@gmail.com> Fix for negative values
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Set module name as parameter of require -- Set module name as parameter of require
@ -96,12 +97,16 @@ function readNumber(addr, unit)
crc = ow.crc8(string.sub(data,1,8)) crc = ow.crc8(string.sub(data,1,8))
-- print("CRC="..crc) -- print("CRC="..crc)
if (crc == data:byte(9)) then if (crc == data:byte(9)) then
t = (data:byte(1) + data:byte(2) * 256)
if (t > 32767) then
t = t - 65536
end
if(unit == nil or unit == C) then if(unit == nil or unit == C) then
t = (data:byte(1) + data:byte(2) * 256) * 625 t = t * 625
elseif(unit == F) then elseif(unit == F) then
t = (data:byte(1) + data:byte(2) * 256) * 1125 + 320000 t = t * 1125 + 320000
elseif(unit == K) then elseif(unit == K) then
t = (data:byte(1) + data:byte(2) * 256) * 625 + 2731500 t = t * 625 + 2731500
else else
return nil return nil
end end

205
lua_modules/email/imap.lua Normal file
View File

@ -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
-- "<tag> OK <command> 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

1
pre_build/README.md Normal file
View File

@ -0,0 +1 @@
[Downloads](https://github.com/nodemcu/nodemcu-firmware/releases/latest)