Unverified Commit 11592951 authored by Arnim Läuger's avatar Arnim Läuger Committed by GitHub
Browse files

Merge pull request #2582 from nodemcu/dev

Next master drop
parents 4095c408 61433c44
uzlib - Deflate/Zlib-compatible LZ77 compression library
======================================================
This is a heavily modified and cut down version of Paul Sokolovsky's
uzlib library. This library has exported routines which
- Can compress data to a Deflate-compatible bitstream, albeit with lower
compression ratio than the Zlib Deflate algorithm as a static Deflate Huffman
tree encoding is used for bitstream). Note that since this compression is
in RAM and requires ~4 bytes per byte of the input record, should only be
called for compressing small records on the ESP8266.
- Can decompress any valid Deflate, Zlib, and Gzip (further called just
"Deflate") bitstream less than 16Kb, and any arbitrary length stream
compressed by the uzlib compressor.
uzlib aims for minimal code size and runtime memory requirements, and thus
is suitable for embedded systems and IoT devices such as the ESP8266.
uzlib is based on:
- tinf library by Joergen Ibsen (Deflate decompression)
- Deflate Static Huffman tree routines by Simon Tatham
- LZ77 compressor by Paul Sokolovsky provided my initial inspiration, but
I ended up rewriting this following RFC 1951 to get improved compression
performance.
The above 16Kb limitation arises from the RFC 1951 use of a 32Kb dictionary,
which is impractical on a chipset with only ~40 Kb RAM avialable to
applications.
The relevant copyright statements are provided in the source files which
use this code.
uzlib library is licensed under Zlib license.
/*
* CRC32 checksum
*
* Copyright (c) 1998-2003 by Joergen Ibsen / Jibz
* All Rights Reserved
*
* http://www.ibsensoftware.com/
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
/*
* CRC32 algorithm taken from the zlib source, which is
* Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
*/
#include <stdint.h>
static const unsigned int tinf_crc32tab[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190,
0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344,
0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278,
0xbdbdf21c
};
/* crc is previous value for incremental computation, 0xffffffff initially */
uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc)
{
const unsigned char *buf = (const unsigned char *)data;
unsigned int i;
for (i = 0; i < length; ++i)
{
crc ^= buf[i];
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4);
}
// return value suitable for passing in next time, for final value invert it
return crc/* ^ 0xffffffff*/;
}
#
# This Make file is called from the core Makefile hierarchy with is a hierarchical
# make wwhich uses parent callbacks to implement inheritance. However is luac_cross
# build stands outside this and uses the host toolchain to implement a separate
# host build of the luac.cross image.
#
.NOTPARALLEL:
CCFLAGS:= -I..
LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld -lm -ldl -Wl,-Map=mapfile
CCFLAGS += -Wall
#DEFINES +=
TARGET = host
ifeq ($(FLAVOR),debug)
CCFLAGS += -O0 -g
TARGET_LDFLAGS += -O0 -g
DEFINES += -DDEBUG_COUNTS
else
FLAVOR = release
CCFLAGS += -O2
TARGET_LDFLAGS += -O2
endif
#
# This relies on the files being unique on the vpath
#
SRC := uz_unzip.c uz_zip.c crc32.c uzlib_inflate.c uzlib_deflate.c
vpath %.c .:..
ODIR := .output/$(TARGET)/$(FLAVOR)/obj
CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
ROOT = ../../..
CC := gcc
ECHO := echo
IMAGES := $(ROOT)/uz_zip $(ROOT)/uz_unzip
.PHONY: test clean all
all: $(IMAGES)
$(ROOT)/uz_zip : $(ODIR)/uz_zip.o $(ODIR)/crc32.o $(ODIR)/uzlib_deflate.o
$(CC) $^ -o $@ $(LDFLAGS)
$(ROOT)/uz_unzip : $(ODIR)/uz_unzip.o $(ODIR)/crc32.o $(ODIR)/uzlib_inflate.o
$(CC) $^ -o $@ $(LDFLAGS)
test :
@echo CC: $(CC)
@echo SRC: $(SRC)
@echo DEPS: $(DEPS)
clean :
$(RM) -r $(ODIR)
$(RM) $(IMAGES)
$(ODIR)/%.o: %.c
@mkdir -p $(ODIR);
$(CC) $(CFLAGS) -o $@ -c $<
/************************************************************************
* NodeMCU unzip wrapper code for uzlib_inflate
*
* Note that whilst it would be more straightforward to implement a
* simple in memory approach, this utility adopts the same streaming
* callback architecture as app/lua/lflash.c to enable this code to be
* tested in a pure host development environment
*/
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "uzlib.h"
/* Test wrapper */
#define DICTIONARY_WINDOW 16384
#define READ_BLOCKSIZE 2048
#define WRITE_BLOCKSIZE 2048
#define WRITE_BLOCKS ((DICTIONARY_WINDOW/WRITE_BLOCKSIZE)+1)
#define FREE(v) if (v) uz_free(v)
typedef uint8_t uchar;
typedef uint16_t ushort;
typedef uint32_t uint;
struct INPUT {
FILE *fin;
int len;
uint8_t block[READ_BLOCKSIZE];
uint8_t *inPtr;
int bytesRead;
int left;
} *in;
typedef struct {
uint8_t byte[WRITE_BLOCKSIZE];
} outBlock;
struct OUTPUT {
FILE *fout;
outBlock *block[WRITE_BLOCKS];
int ndx;
int written;
int len;
uint32_t crc;
int breakNdx;
int (*fullBlkCB) (void);
} *out;
/*
* uzlib_inflate does a stream inflate on an RFC 1951 encoded data stream.
* It uses three application-specific CBs passed in the call to do the work:
*
* - get_byte() CB to return next byte in input stream
* - put_byte() CB to output byte to output buffer
* - recall_byte() CB to output byte to retrieve a historic byte from
* the output buffer.
*
* Note that put_byte() also triggers secondary CBs to do further processing.
*/
uint8_t get_byte (void) {
if (--in->left < 0) {
/* Read next input block */
int remaining = in->len - in->bytesRead;
int wanted = remaining >= READ_BLOCKSIZE ? READ_BLOCKSIZE : remaining;
if (fread(in->block, 1, wanted, in->fin) != wanted)
UZLIB_THROW(UZLIB_DATA_ERROR);
in->bytesRead += wanted;
in->inPtr = in->block;
in->left = wanted-1;
}
return *in->inPtr++;
}
void put_byte (uint8_t value) {
int offset = out->ndx % WRITE_BLOCKSIZE; /* counts from 0 */
out->block[0]->byte[offset++] = value;
out->ndx++;
if (offset == WRITE_BLOCKSIZE || out->ndx == out->len) {
if (out->fullBlkCB)
out->fullBlkCB();
/* circular shift the block pointers (redundant on last block, but so what) */
outBlock *nextBlock = out->block[WRITE_BLOCKS - 1];
memmove(out->block+1, out->block, (WRITE_BLOCKS-1)*sizeof(void*));
out->block[0] = nextBlock;
}
}
uint8_t recall_byte (uint offset) {
if(offset > DICTIONARY_WINDOW || offset >= out->ndx)
UZLIB_THROW(UZLIB_DICT_ERROR);
/* ndx starts at 1. Need relative to 0 */
uint n = out->ndx - offset;
uint pos = n % WRITE_BLOCKSIZE;
uint blockNo = out->ndx / WRITE_BLOCKSIZE - n / WRITE_BLOCKSIZE;
return out->block[blockNo]->byte[pos];
}
int processOutRec (void) {
int len = (out->ndx % WRITE_BLOCKSIZE) ? out->ndx % WRITE_BLOCKSIZE :
WRITE_BLOCKSIZE;
if (fwrite(out->block[0], 1, len, out->fout) != len)
UZLIB_THROW(UZLIB_DATA_ERROR);
out->crc = uzlib_crc32(out->block[0], len, out->crc);
out->written += len;
if (out->written == out->len) {
fclose(out->fout);
out->fullBlkCB = NULL;
}
return 1;
}
int main(int argc, char *argv[]) {
assert (argc==3);
const char *inFile = argv[1], *outFile = argv[2];
int i, n, res;
uint crc;
void *cxt_not_used;
assert(sizeof(unsigned int) == 4);
/* allocate and zero the in and out Blocks */
assert((in = uz_malloc(sizeof(*in))) != NULL);
assert((out = uz_malloc(sizeof(*out))) != NULL);
memset(in, 0, sizeof(*in));
memset(out, 0, sizeof(*out));
/* open input files and probe end to read the expanded length */
assert((in->fin = fopen(inFile, "rb")) != NULL);
fseek(in->fin, -4, SEEK_END);
assert(fread((uchar*)&(out->len), 1, 4, in->fin) == 4);
in->len = ftell(in->fin);
fseek(in->fin, 0, SEEK_SET);
assert((out->fout = fopen(outFile, "wb")) != NULL);
printf ("Inflating in=%s out=%s\n", inFile, outFile);
/* Allocate the out buffers (number depends on the unpacked length) */
n = (out->len > DICTIONARY_WINDOW) ? WRITE_BLOCKS :
1 + (out->len-1) / WRITE_BLOCKSIZE;
for(i = WRITE_BLOCKS - n + 1; i <= WRITE_BLOCKS; i++)
assert((out->block[i % WRITE_BLOCKS] = uz_malloc(sizeof(outBlock))) != NULL);
out->breakNdx = (out->len < WRITE_BLOCKSIZE) ? out->len : WRITE_BLOCKSIZE;
out->fullBlkCB = processOutRec;
out->crc = ~0;
/* Call inflate to do the business */
res = uzlib_inflate (get_byte, put_byte, recall_byte, in->len, &crc, &cxt_not_used);
if (res > 0 && crc != ~out->crc)
res = UZLIB_CHKSUM_ERROR;
for (i = 0; i < WRITE_BLOCKS; i++)
FREE(out->block[i]);
fclose(in->fin);
FREE(in);
FREE(out);
if (res < 0)
printf("Error during decompression: %d\n", res);
return (res != 0) ? 1: 0;
}
/************************************************************************
* NodeMCU zip wrapper code for uzlib_compress
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include "uzlib.h"
#define fwriterec(r) fwrite(&(r), sizeof(r), 1, fout);
#define BAD_FILE (-1)
int main (int argc, char **argv) {
const char *in = argv[1], *out = argv[2];
if (argc!=3)
return 1;
printf ("Compressing in=%s out=%s\n", in, out);
FILE *fin, *fout;
int status = -1;
uint32_t iLen, oLen;
uint8_t *iBuf, *oBuf;
if (!(fin = fopen(in, "rb")) || fseek(fin, 0, SEEK_END) ||
(iLen = ftell(fin)) <= 0 || fseek(fin, 0, SEEK_SET))
return 1;
if ((fout = fopen(out, "wb")) == NULL ||
(iBuf = (uint8_t *) uz_malloc(iLen)) == NULL ||
fread(iBuf, 1, iLen, fin) != iLen)
return 1;
if (uzlib_compress (&oBuf, &oLen, iBuf, iLen) == UZLIB_OK &&
oLen == fwrite(oBuf, oLen, 1, fout))
status = UZLIB_OK;
uz_free(iBuf);
if (oBuf) uz_free(oBuf);
fclose(fin);
fclose(fout);
if (status == UZLIB_OK)
unlink(out);
return (status == UZLIB_OK) ? 1: 0;
}
/*
* uzlib - tiny deflate/inflate library (deflate, gzip, zlib)
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2016 by Paul Sokolovsky
*/
#ifndef UZLIB_INFLATE_H
#define UZLIB_INFLATE_H
#include <setjmp.h>
#if defined(__XTENSA__)
#include "c_stdint.h"
#include "mem.h"
#define UZLIB_THROW(v) longjmp(unwindAddr, (v))
#define UZLIB_SETJMP setjmp
#define uz_malloc os_malloc
#define uz_free os_free
#else /* POSIX */
#include <stdint.h>
#include <stdlib.h>
extern int dbg_break(void);
#define UZLIB_THROW(v) {dbg_break();_longjmp(unwindAddr, (v));}
#define UZLIB_SETJMP _setjmp
#define uz_malloc malloc
#define uz_free free
#endif
extern jmp_buf unwindAddr;
/* ok status, more data produced */
#define UZLIB_OK 0
/* end of compressed stream reached */
#define UZLIB_DONE 1
#define UZLIB_DATA_ERROR (-3)
#define UZLIB_CHKSUM_ERROR (-4)
#define UZLIB_DICT_ERROR (-5)
#define UZLIB_MEMORY_ERROR (-6)
/* checksum types */
#define UZLIB_CHKSUM_NONE 0
#define UZLIB_CHKSUM_ADLER 1
#define UZLIB_CHKSUM_CRC 2
/* Gzip header codes */
#define UZLIB_FTEXT 1
#define UZLIB_FHCRC 2
#define UZLIB_FEXTRA 4
#define UZLIB_FNAME 8
#define UZLIB_FCOMMENT 16
/* Compression API */
typedef struct uzlib_data UZLIB_DATA;
int uzlib_inflate (uint8_t (*)(void), void (*)(uint8_t),
uint8_t (*)(uint32_t), uint32_t len, uint32_t *crc, void **state);
int uzlib_compress (uint8_t **dest, uint32_t *destLen,
const uint8_t *src, uint32_t srcLen);
/* Checksum API */
/* crc is previous value for incremental computation, 0xffffffff initially */
uint32_t uzlib_crc32(const void *data, uint32_t length, uint32_t crc);
#endif /* UZLIB_INFLATE_H */
/*
* This implementation draws heavily on the work down by Paul Sokolovsky
* (https://github.com/pfalcon) and his uzlib library which in turn uses
* work done by Joergen Ibsen, Simon Tatham and others. All of this work
* is under an unrestricted right to use subject to copyright attribution.
* Two copyright wordings (variants A and B) are following.
*
* (c) statement A initTables, copy, literal
*
* The remainder of this code has been written by me, Terry Ellison 2018,
* under the standard NodeMCU MIT licence, but is available to the other
* contributors to this source under any permissive licence.
*
* My primary algorthmic reference is RFC 1951: "DEFLATE Compressed Data
* Format Specification version 1.3", dated May 1996.
*
* Also because the code in this module is drawn from different sources,
* the different coding practices can be confusing, I have standardised
* the source by:
*
* - Adopting the 2 indent rule as in the rest of the firmware
*
* - I have replaced the various mix of char, unsigned char and uchar
* by the single uchar type; ditto for ushort and uint.
*
* - All internal (non-exported) functions and data are static
*
* - Only exported functions and data have the module prefix. All
* internal (static) variables and fields are lowerCamalCase.
*
***********************************************************************
* Copyright statement A for Zlib (RFC1950 / RFC1951) compression for PuTTY.
PuTTY is copyright 1997-2014 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
Kuhn, Colin Watson, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE COP--YRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
************************************************************************
Copyright statement B for genlz77 functions:
*
* genlz77 - Generic LZ77 compressor
*
* Copyright (c) 2014 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "uzlib.h"
jmp_buf unwindAddr;
/* Minimum and maximum length of matches to look for, inclusive */
#define MIN_MATCH 3
#define MAX_MATCH 258
/* Max offset of the match to look for, inclusive */
#define MAX_OFFSET 16384 // 32768 //
#define OFFSET16_MASK 0x7FFF
#define NULL_OFFSET 0xFFFF
#if MIN_MATCH < 3
#error "Encoding requires a minium match of 3 bytes"
#endif
#define SIZE(a) (sizeof(a)/sizeof(*a)) /* no of elements in array */
#ifdef __XTENSA__
#define RAM_COPY_BYTE_ARRAY(c,s,sl) uchar *c = alloca(sl); memcpy(c,s,(sl))
#else
#define RAM_COPY_BYTE_ARRAY(c,s,sl) uchar *c = s;
#endif
#define FREE(v) if (v) uz_free(v)
typedef uint8_t uchar;
typedef uint16_t ushort;
typedef uint32_t uint;
#ifdef DEBUG_COUNTS
#define DBG_PRINT(...) printf(__VA_ARGS__)
#define DBG_COUNT(n) (debugCounts[n]++)
#define DBG_ADD_COUNT(n,m) (debugCounts[n]+=m)
int debugCounts[20];
#else
#define DBG_PRINT(...)
#define DBG_COUNT(n)
#define DBG_ADD_COUNT(n,m)
#endif
int dbg_break(void) {return 1;}
typedef struct {
ushort code, extraBits, min, max;
} codeRecord;
struct dynTables {
ushort *hashChain;
ushort *hashTable;
ushort hashMask;
ushort hashSlots;
ushort hashBits;
ushort dictLen;
const uchar bitrevNibble[16];
const codeRecord lenCodes[285-257+1];
const codeRecord distCodes[29-0+1];
} *dynamicTables;
struct outputBuf {
uchar *buffer;
uint len, size;
uint inLen, inNdx;
uint bits, nBits;
uint compDisabled;
} *oBuf;
/*
* Set up the constant tables used to drive the compression
*
* Constants are stored in flash memory on the ESP8266 NodeMCU firmware
* builds, but only word aligned data access are supported in hardare so
* short and byte accesses are handled by a S/W exception handler and are
* SLOW. RAM is also at premium, so these short routines are driven by
* byte vectors copied into RAM and then used to generate temporary RAM
* tables, which are the same as the above statically declared versions.
*
* This might seem a bit convolved but this runs faster and takes up less
* memory than the original version. This code also works fine on the
* x86-64s so we just use one code variant.
*
* Note that fixed Huffman trees as defined in RFC 1951 Sec 3.2.5 are
* always used. Whilst dynamic trees can give better compression for
* larger blocks, this comes at a performance hit of having to compute
* these trees. Fixed trees give better compression performance on short
* blocks and significantly reduce compression times.
*
* The following defines are used to initialise these tables.
*/
#define lenCodes_GEN \
"\x03\x01\x01\x01\x01\x01\x01\x01\xff\x02\x02\x02\x02\xff\x04\x04\x04\x04" \
"\xff\x08\x08\x08\x08\xff\x10\x10\x10\x10\xff\x20\x20\x20\x1f\xff\x01\x00"
#define lenCodes_LEN 29
#define distCodes_GEN \
"\x01\x01\x01\x01\xff\x02\x02\xff\x04\x04\xff\x08\x08\xff\x10\x10\xff" \
"\x20\x20\xff\x40\x40\xff\x86\x86\xff\x87\x87\xff\x88\x88\xff" \
"\x89\x89\xff\x8a\x8a\xff\x8b\x8b\xff\x8c\x8c"
#define distCodes_LEN 30
#define BITREV16 "\x0\x8\x4\xc\x2\xa\x6\xe\x1\x9\x5\xd\x3\xb\x7\xf"
static void genCodeRecs (const codeRecord *rec, ushort len,
char *init, int initLen,
ushort start, ushort m0) {
DBG_COUNT(0);
int i, b=0, m=0, last=m0;
RAM_COPY_BYTE_ARRAY(c, (uchar *)init,initLen);
codeRecord *p = (codeRecord *) rec;
for (i = start; i < start+len; i++, c++) {
if (*c == 0xFF)
b++, c++;
m += (*c & 0x80) ? 2 << (*c & 0x1F) : *c;
*p++ = (codeRecord) {i, b, last + 1, (last = m)};
}
}
static void initTables (uint chainLen, uint hashSlots) {
DBG_COUNT(1);
uint dynamicSize = sizeof(struct dynTables) +
sizeof(struct outputBuf) +
chainLen * sizeof(ushort) +
hashSlots * sizeof(ushort);
struct dynTables *dt = uz_malloc(dynamicSize);
memset(dt, 0, dynamicSize);
dynamicTables = dt;
/* Do a single malloc for dymanic tables and assign addresses */
if(!dt )
UZLIB_THROW(UZLIB_MEMORY_ERROR);
memcpy((uchar*)dt->bitrevNibble, BITREV16, 16);
oBuf = (struct outputBuf *)(dt+1);
dt->hashTable = (ushort *)(oBuf+1);
dt->hashChain = dt->hashTable + hashSlots;
dt->hashSlots = hashSlots;
dt->hashMask = hashSlots - 1;
/* As these are offset rather than pointer, 0 is a valid offset */
/* (unlike NULL), so 0xFFFF is used to denote an unset value */
memset(dt->hashTable, -1, sizeof(ushort)*hashSlots);
memset(dt->hashChain, -1, sizeof(ushort)*chainLen);
/* Generate the code recors for the lenth and distance code tables */
genCodeRecs(dt->lenCodes, SIZE(dt->lenCodes),
lenCodes_GEN, sizeof(lenCodes_GEN),
257,2);
((codeRecord *)(dynamicTables->lenCodes+285-257))->extraBits=0; /* odd ball entry */
genCodeRecs(dt->distCodes, SIZE(dt->distCodes),
distCodes_GEN, sizeof(distCodes_GEN),
0,0);
}
/*
* Routines to output bit streams and byte streams to the output buffer
*/
void resizeBuffer(void) {
uchar *nb;
DBG_COUNT(2);
/* The outbuf is given an initial size estimate but if we are running */
/* out of space then extropolate size using current compression */
double newEstimate = (((double) oBuf->len)*oBuf->inLen) / oBuf->inNdx;
oBuf->size = 128 + (uint) newEstimate;
if (!(nb = realloc(oBuf->buffer, oBuf->size)))
UZLIB_THROW(UZLIB_MEMORY_ERROR);
oBuf->buffer = nb;
}
void outBits(ushort bits, int nBits) {
DBG_COUNT(3);
oBuf->bits |= bits << oBuf->nBits;
oBuf->nBits += nBits;
if (oBuf->len >= oBuf->size - sizeof(bits))
resizeBuffer();
while (oBuf->nBits >= 8) {
DBG_PRINT("%02x-", oBuf->bits & 0xFF);
oBuf->buffer[oBuf->len++] = oBuf->bits & 0xFF;
oBuf->bits >>= 8;
oBuf->nBits -= 8;
}
}
void outBitsRev(uchar bits, int nBits) {
DBG_COUNT(4);
/* Note that bit reversal only operates on an 8-bit bits field */
uchar bitsRev = (dynamicTables->bitrevNibble[bits & 0x0f]<<4) |
dynamicTables->bitrevNibble[bits>>4];
outBits(bitsRev, nBits);
}
void outBytes(void *bytes, int nBytes) {
DBG_COUNT(5);
int i;
if (oBuf->len >= oBuf->size - nBytes)
resizeBuffer();
/* Note that byte output dumps any bits data so the caller must */
/* flush this first, if necessary */
oBuf->nBits = oBuf->bits = 0;
for (i = 0; i < nBytes; i++) {
DBG_PRINT("%02x-", *((uchar*)bytes+i));
oBuf->buffer[oBuf->len++] = *((uchar*)bytes+i);
}
}
/*
* Output an literal byte as an 8 or 9 bit code
*/
void literal (uchar c) {
DBG_COUNT(6);
DBG_PRINT("sym: %02x %c\n", c, c);
if (oBuf->compDisabled) {
/* We're in an uncompressed block, so just output the byte. */
outBits(c, 8);
} else if (c <= 143) {
/* 0 through 143 are 8 bits long starting at 00110000. */
outBitsRev(0x30 + c, 8);
} else {
/* 144 through 255 are 9 bits long starting at 110010000. */
outBits(1, 1);
outBitsRev(0x90 - 144 + c, 8);
}
}
/*
* Output a dictionary (distance, length) pars as bitstream codes
*/
void copy (int distance, int len) {
DBG_COUNT(7);
const codeRecord *lenCodes = dynamicTables->lenCodes, *l;
const codeRecord *distCodes = dynamicTables->distCodes, *d;
int i, j, k;
assert(!oBuf->compDisabled);
while (len > 0) {
/*
* We can transmit matches of lengths 3 through 258
* inclusive. So if len exceeds 258, we must transmit in
* several steps, with 258 or less in each step.
*
* Specifically: if len >= 261, we can transmit 258 and be
* sure of having at least 3 left for the next step. And if
* len <= 258, we can just transmit len. But if len == 259
* or 260, we must transmit len-3.
*/
int thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3);
len -= thislen;
/*
* Binary-search to find which length code we're
* transmitting.
*/
i = -1;
j = lenCodes_LEN;
while (1) {
assert(j - i >= 2);
k = (j + i) / 2;
if (thislen < lenCodes[k].min)
j = k;
else if (thislen > lenCodes[k].max)
i = k;
else {
l = &lenCodes[k];
break; /* found it! */
}
}
/*
* Transmit the length code. 256-279 are seven bits
* starting at 0000000; 280-287 are eight bits starting at
* 11000000.
*/
if (l->code <= 279) {
outBitsRev((l->code - 256) * 2, 7);
} else {
outBitsRev(0xc0 - 280 + l->code, 8);
}
/*
* Transmit the extra bits.
*/
if (l->extraBits)
outBits(thislen - l->min, l->extraBits);
/*
* Binary-search to find which distance code we're
* transmitting.
*/
i = -1;
j = distCodes_LEN;
while (1) {
assert(j - i >= 2);
k = (j + i) / 2;
if (distance < distCodes[k].min)
j = k;
else if (distance > distCodes[k].max)
i = k;
else {
d = &distCodes[k];
break; /* found it! */
}
}
/*
* Transmit the distance code. Five bits starting at 00000.
*/
outBitsRev(d->code * 8, 5);
/*
* Transmit the extra bits.
*/
if (d->extraBits)
outBits(distance - d->min, d->extraBits);
}
}
/*
* Block compression uses a hashTable to index into a set of search
* chainList, where each chain links together the triples of chars within
* the dictionary (the last MAX_OFFSET bytes of the input buffer) with
* the same hash index. So for compressing a file of 200Kb, say, with a
* 16K dictionary (the largest that we can inflate within the memory
* constraints of the ESP8266), the chainList is 16K slots long, and the
* hashTable is 4K slots long, so a typical chain will have 4 links.
*
* These two tables use 16-bit ushort offsets rather than pointers to
* save memory (essential on the ESP8266).
*
* As per RFC 1951 sec 4, we also implement a "lazy match" procedure
*/
void uzlibCompressBlock(const uchar *src, uint srcLen) {
int i, j, k, l;
uint hashMask = dynamicTables->hashMask;
ushort *hashChain = dynamicTables->hashChain;
ushort *hashTable = dynamicTables->hashTable;
uint hashShift = 24 - dynamicTables->hashBits;
uint lastOffset = 0, lastLen = 0;
oBuf->inLen = srcLen; /* used for output buffer resizing */
DBG_COUNT(9);
for (i = 0; i <= ((int)srcLen) - MIN_MATCH; i++) {
/*
* Calculate a hash on the next three chars using the liblzf hash
* function, then use this via the hashTable to index into the chain
* of triples within the dictionary window which have the same hash.
*
* Note that using 16-bit offsets requires a little manipulation to
* handle wrap-around and recover the correct offset, but all other
* working uses uint offsets simply because the compiler generates
* faster (and smaller in the case of the ESP8266) code.
*
* Also note that this code also works for any tail 2 literals; the
* hash will access beyond the array and will be incorrect, but
* these can't match and will flush the last cache.
*/
const uchar *this = src + i, *comp;
uint base = i & ~OFFSET16_MASK;
uint iOffset = i - base;
uint maxLen = srcLen - i;
uint matchLen = MIN_MATCH - 1;
uint matchOffset = 0;
uint v = (this[0] << 16) | (this[1] << 8) | this[2];
uint hash = ((v >> hashShift) - v) & hashMask;
uint nextOffset = hashTable[hash];
oBuf->inNdx = i; /* used for output buffer resizing */
DBG_COUNT(10);
if (maxLen>MAX_MATCH)
maxLen = MAX_MATCH;
hashTable[hash] = iOffset;
hashChain[iOffset & (MAX_OFFSET-1)] = nextOffset;
for (l = 0; nextOffset != NULL_OFFSET && l<60; l++) {
DBG_COUNT(11);
/* handle the case where base has bumped */
j = base + nextOffset - ((nextOffset < iOffset) ? 0 : (OFFSET16_MASK + 1));
if (i - j > MAX_OFFSET)
break;
for (k = 0, comp = src + j; this[k] == comp[k] && k < maxLen; k++)
{}
DBG_ADD_COUNT(12, k);
if (k > matchLen) {
matchOffset = i - j;
matchLen = k;
}
nextOffset = hashChain[nextOffset & (MAX_OFFSET-1)];
}
if (lastOffset) {
if (matchOffset == 0 || lastLen >= matchLen ) {
/* ignore this match (or not) and process last */
DBG_COUNT(14);
copy(lastOffset, lastLen);
DBG_PRINT("dic: %6x %6x %6x\n", i-1, lastLen, lastOffset);
i += lastLen - 1 - 1;
lastOffset = lastLen = 0;
} else {
/* ignore last match and emit a symbol instead; cache this one */
DBG_COUNT(15);
literal(this[-1]);
lastOffset = matchOffset;
lastLen = matchLen;
}
} else { /* no last match */
if (matchOffset) {
DBG_COUNT(16);
/* cache this one */
lastOffset = matchOffset;
lastLen = matchLen;
} else {
DBG_COUNT(17);
/* emit a symbol; last already clear */
literal(this[0]);
}
}
}
if (lastOffset) { /* flush cached match if any */
copy(lastOffset, lastLen);
DBG_PRINT("dic: %6x %6x %6x\n", i, lastLen, lastOffset);
i += lastLen - 1;
}
while (i < srcLen)
literal(src[i++]); /* flush the last few bytes if needed */
}
/*
* This compress wrapper treats the input stream as a single block for
* compression using the default Static huffman block encoding
*/
int uzlib_compress (uchar **dest, uint *destLen, const uchar *src, uint srcLen) {
uint crc = ~uzlib_crc32(src, srcLen, ~0);
uint chainLen = srcLen < MAX_OFFSET ? srcLen : MAX_OFFSET;
uint hashSlots, i, j;
int status;
uint FLG_MTIME[] = {0x00088b1f, 0};
ushort XFL_OS = 0x0304;
/* The hash table has 4K slots for a 16K chain and scaling down */
/* accordingly, for an average chain length of 4 links or thereabouts */
for (i = 256, j = 8 - 2; i < chainLen; i <<= 1)
j++;
hashSlots = i >> 2;
if ((status = UZLIB_SETJMP(unwindAddr)) == 0) {
initTables(chainLen, hashSlots);
oBuf->size = srcLen/5; /* initial guess of a 5x compression ratio */
oBuf->buffer = uz_malloc(oBuf->size);
dynamicTables->hashSlots = hashSlots;
dynamicTables->hashBits = j;
if(!oBuf->buffer ) {
status = UZLIB_MEMORY_ERROR;
} else {
/* Output gzip and block headers */
outBytes(FLG_MTIME, sizeof(FLG_MTIME));
outBytes(&XFL_OS, sizeof(XFL_OS));
outBits(1, 1); /* Final block */
outBits(1, 2); /* Static huffman block */
uzlibCompressBlock(src, srcLen); /* Do the compress */
/* Output block finish */
outBits(0, 7); /* close block */
outBits(0, 7); /* Make sure all bits are flushed */
outBytes(&crc, sizeof(crc));
outBytes(&srcLen, sizeof(srcLen));
status = UZLIB_OK;
}
} else {
status = UZLIB_OK;
}
FREE(dynamicTables);
for (i=0; i<20;i++) DBG_PRINT("count %u = %u\n",i,debugCounts[i]);
if (status == UZLIB_OK) {
uchar *trimBuf = realloc(oBuf->buffer, oBuf->len);
*dest = trimBuf ? trimBuf : oBuf->buffer;
*destLen = oBuf->len;
} else {
*dest = NULL;
*destLen = 0;
FREE(oBuf->buffer);
}
return status;
}
/*
* tinfgzip.c - tiny gzip decompressor
* tinflate.c - tiny inflate
*
* The original source headers as below for licence compliance and in
* full acknowledgement of the originitor contributions. Modified by
* Terry Ellison 2018 to provide lightweight stream inflate for NodeMCU
* Lua. Modifications are under the standard NodeMCU MIT licence.
*
* Copyright (c) 2003 by Joergen Ibsen / Jibz
* All Rights Reserved
* http://www.ibsensoftware.com/
*
* Copyright (c) 2014-2016 by Paul Sokolovsky
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
#include <string.h>
#ifdef __XTENSA__
#include "c_stdio.h"
#else
#include <stdio.h>
#endif
#include "uzlib.h"
#ifdef DEBUG_COUNTS
#define DBG_PRINT(...) printf(__VA_ARGS__)
#define DBG_COUNT(n) (debugCounts[n]++)
#define DBG_ADD_COUNT(n,m) (debugCounts[n]+=m)
int debugCounts[20];
#else
#define NDEBUG
#define DBG_PRINT(...)
#define DBG_COUNT(n)
#define DBG_ADD_COUNT(n,m)
#endif
#define SIZE(arr) (sizeof(arr) / sizeof(*(arr)))
jmp_buf unwindAddr;
int dbg_break(void) {return 1;}
typedef uint8_t uchar;
typedef uint16_t ushort;
typedef uint32_t uint;
/* data structures */
typedef struct {
ushort table[16]; /* table of code length counts */
ushort trans[288]; /* code -> symbol translation table */
} UZLIB_TREE;
struct uzlib_data {
/*
* extra bits and base tables for length and distance codes
*/
uchar lengthBits[30];
ushort lengthBase[30];
uchar distBits[30];
ushort distBase[30];
/*
* special ordering of code length codes
*/
uchar clcidx[19];
/*
* dynamic length/symbol and distance trees
*/
UZLIB_TREE ltree;
UZLIB_TREE dtree;
/*
* methods encapsulate handling of the input and output streams
*/
uchar (*get_byte)(void);
void (*put_byte)(uchar b);
uchar (*recall_byte)(uint offset);
/*
* Other state values
*/
uint destSize;
uint tag;
uint bitcount;
uint lzOffs;
int bType;
int bFinal;
uint curLen;
uint checksum;
};
/*
* Note on changes to layout, naming, etc. This module combines extracts
* from 3 code files from two sources (Sokolovsky, Ibsen et al) with perhaps
* 30% from me Terry Ellison. These sources had inconsistent layout and
* naming conventions, plus extra condtional handling of platforms that
* cannot support NodeMCU. (This is intended to be run compiled and executed
* on GCC POSIX and XENTA newlib environments.) So I have (1) reformatted
* this file in line with NodeMCU rules; (2) demoted all private data and
* functions to static and removed the redundant name prefixes; (3) reordered
* functions into a more logic order; (4) added some ESP architecture
* optimisations, for example these IoT devices are very RAM limited, so
* statically allocating large RAM blocks is against programming guidelines.
*/
static void skip_bytes(UZLIB_DATA *d, int num) {
if (num) /* Skip a fixed number of bytes */
while (num--) (void) d->get_byte();
else /* Skip to next nullchar */
while (d->get_byte()) {}
}
static uint16_t get_uint16(UZLIB_DATA *d) {
uint16_t v = d->get_byte();
return v | (d->get_byte() << 8);
}
static uint get_le_uint32 (UZLIB_DATA *d) {
uint v = get_uint16(d);
return v | ((uint) get_uint16(d) << 16);
}
/* get one bit from source stream */
static int getbit (UZLIB_DATA *d) {
uint bit;
/* check if tag is empty */
if (!d->bitcount--) {
/* load next tag */
d->tag = d->get_byte();
d->bitcount = 7;
}
/* shift bit out of tag */
bit = d->tag & 0x01;
d->tag >>= 1;
return bit;
}
/* read a num bit value from a stream and add base */
static uint read_bits (UZLIB_DATA *d, int num, int base) {
/* This is an optimised version which doesn't call getbit num times */
if (!num)
return base;
uint i, n = (((uint)-1)<<num);
for (i = d->bitcount; i < num; i +=8)
d->tag |= ((uint)d->get_byte()) << i;
n = d->tag & ~n;
d->tag >>= num;
d->bitcount = i - num;
return base + n;
}
/* --------------------------------------------------- *
* -- uninitialized global data (static structures) -- *
* --------------------------------------------------- */
/*
* Constants are stored in flash memory on the ESP8266 NodeMCU firmware
* builds, but only word aligned data access are supported in hardare so
* short and byte accesses are handled by a S/W exception handler and
* are SLOW. RAM is also at premium, especially static initialised vars,
* so we malloc a single block on first call to hold all tables and call
* the dynamic generator to generate malloced RAM tables that have the
* same content as the above statically declared versions.
*
* This might seem a bit convolved but this runs faster and takes up
* less memory than the static version on the ESP8266.
*/
#define CLCIDX_INIT \
"\x10\x11\x12\x00\x08\x07\x09\x06\x0a\x05\x0b\x04\x0c\x03\x0d\x02\x0e\x01\x0f"
/* ----------------------- *
* -- utility functions -- *
* ----------------------- */
/* build extra bits and base tables */
static void build_bits_base (uchar *bits, ushort *base,
int delta, int first) {
int i, sum;
/* build bits table */
for (i = 0; i < delta; ++i) bits[i] = 0;
for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta;
/* build base table */
for (sum = first, i = 0; i < 30; ++i) {
base[i] = sum;
sum += 1 << bits[i];
}
}
/* build the fixed huffman trees */
static void build_fixed_trees (UZLIB_TREE *lt, UZLIB_TREE *dt) {
int i;
/* build fixed length tree */
for (i = 0; i < 7; ++i) lt->table[i] = 0;
lt->table[7] = 24;
lt->table[8] = 152;
lt->table[9] = 112;
for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i;
for (i = 0; i < 144; ++i) lt->trans[24 + i] = i;
for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i;
for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i;
/* build fixed distance tree */
for (i = 0; i < 5; ++i) dt->table[i] = 0;
dt->table[5] = 32;
for (i = 0; i < 32; ++i) dt->trans[i] = i;
}
/* given an array of code lengths, build a tree */
static void build_tree (UZLIB_TREE *t, const uchar *lengths, uint num) {
ushort offs[16];
uint i, sum;
/* clear code length count table */
for (i = 0; i < 16; ++i)
t->table[i] = 0;
/* scan symbol lengths, and sum code length counts */
for (i = 0; i < num; ++i)
t->table[lengths[i]]++;
t->table[0] = 0;
/* compute offset table for distribution sort */
for (sum = 0, i = 0; i < 16; ++i) {
offs[i] = sum;
sum += t->table[i];
}
/* create code->symbol translation table (symbols sorted by code) */
for (i = 0; i < num; ++i) {
if (lengths[i])
t->trans[offs[lengths[i]]++] = i;
}
}
/* ---------------------- *
* -- decode functions -- *
* ---------------------- */
/* given a data stream and a tree, decode a symbol */
static int decode_symbol (UZLIB_DATA *d, UZLIB_TREE *t) {
int sum = 0, cur = 0, len = 0;
/* get more bits while code value is above sum */
do {
cur = 2*cur + getbit(d);
if (++len == SIZE(t->table))
return UZLIB_DATA_ERROR;
sum += t->table[len];
cur -= t->table[len];
} while (cur >= 0);
sum += cur;
if (sum < 0 || sum >= SIZE(t->trans))
return UZLIB_DATA_ERROR;
return t->trans[sum];
}
/* given a data stream, decode dynamic trees from it */
static int decode_trees (UZLIB_DATA *d, UZLIB_TREE *lt, UZLIB_TREE *dt) {
uchar lengths[288+32];
uint hlit, hdist, hclen, hlimit;
uint i, num, length;
/* get 5 bits HLIT (257-286) */
hlit = read_bits(d, 5, 257);
/* get 5 bits HDIST (1-32) */
hdist = read_bits(d, 5, 1);
/* get 4 bits HCLEN (4-19) */
hclen = read_bits(d, 4, 4);
for (i = 0; i < 19; ++i) lengths[i] = 0;
/* read code lengths for code length alphabet */
for (i = 0; i < hclen; ++i) {
/* get 3 bits code length (0-7) */
uint clen = read_bits(d, 3, 0);
lengths[d->clcidx[i]] = clen;
}
/* build code length tree, temporarily use length tree */
build_tree(lt, lengths, 19);
/* decode code lengths for the dynamic trees */
hlimit = hlit + hdist;
for (num = 0; num < hlimit; ) {
int sym = decode_symbol(d, lt);
uchar fill_value = 0;
int lbits, lbase = 3;
/* error decoding */
if (sym < 0)
return sym;
switch (sym) {
case 16:
/* copy previous code length 3-6 times (read 2 bits) */
fill_value = lengths[num - 1];
lbits = 2;
break;
case 17:
/* repeat code length 0 for 3-10 times (read 3 bits) */
lbits = 3;
break;
case 18:
/* repeat code length 0 for 11-138 times (read 7 bits) */
lbits = 7;
lbase = 11;
break;
default:
/* values 0-15 represent the actual code lengths */
lengths[num++] = sym;
/* continue the for loop */
continue;
}
/* special code length 16-18 are handled here */
length = read_bits(d, lbits, lbase);
if (num + length > hlimit)
return UZLIB_DATA_ERROR;
for (; length; --length)
lengths[num++] = fill_value;
}
/* build dynamic trees */
build_tree(lt, lengths, hlit);
build_tree(dt, lengths + hlit, hdist);
return UZLIB_OK;
}
/* ----------------------------- *
* -- block inflate functions -- *
* ----------------------------- */
/* given a stream and two trees, inflate a block of data */
static int inflate_block_data (UZLIB_DATA *d, UZLIB_TREE *lt, UZLIB_TREE *dt) {
if (d->curLen == 0) {
int dist;
int sym = decode_symbol(d, lt);
/* literal byte */
if (sym < 256) {
DBG_PRINT("huff sym: %02x %c\n", sym, sym);
d->put_byte(sym);
return UZLIB_OK;
}
/* end of block */
if (sym == 256)
return UZLIB_DONE;
/* substring from sliding dictionary */
sym -= 257;
/* possibly get more bits from length code */
d->curLen = read_bits(d, d->lengthBits[sym], d->lengthBase[sym]);
dist = decode_symbol(d, dt);
/* possibly get more bits from distance code */
d->lzOffs = read_bits(d, d->distBits[dist], d->distBase[dist]);
DBG_PRINT("huff dict: -%u for %u\n", d->lzOffs, d->curLen);
}
/* copy next byte from dict substring */
uchar b = d->recall_byte(d->lzOffs);
DBG_PRINT("huff dict byte(%u): -%u - %02x %c\n\n",
d->curLen, d->lzOffs, b, b);
d->put_byte(b);
d->curLen--;
return UZLIB_OK;
}
/* inflate an uncompressed block of data */
static int inflate_uncompressed_block (UZLIB_DATA *d) {
if (d->curLen == 0) {
uint length = get_uint16(d);
uint invlength = get_uint16(d);
/* check length */
if (length != (~invlength & 0x0000ffff))
return UZLIB_DATA_ERROR;
/* increment length to properly return UZLIB_DONE below, without
producing data at the same time */
d->curLen = length + 1;
/* make sure we start next block on a byte boundary */
d->bitcount = 0;
}
if (--d->curLen == 0) {
return UZLIB_DONE;
}
d->put_byte(d->get_byte());
return UZLIB_OK;
}
/* -------------------------- *
* -- main parse functions -- *
* -------------------------- */
static int parse_gzip_header(UZLIB_DATA *d) {
/* check id bytes */
if (d->get_byte() != 0x1f || d->get_byte() != 0x8b)
return UZLIB_DATA_ERROR;
if (d->get_byte() != 8) /* check method is deflate */
return UZLIB_DATA_ERROR;
uchar flg = d->get_byte();/* get flag byte */
if (flg & 0xe0)/* check that reserved bits are zero */
return UZLIB_DATA_ERROR;
skip_bytes(d, 6); /* skip rest of base header of 10 bytes */
if (flg & UZLIB_FEXTRA) /* skip extra data if present */
skip_bytes(d, get_uint16(d));
if (flg & UZLIB_FNAME) /* skip file name if present */
skip_bytes(d,0);
if (flg & UZLIB_FCOMMENT) /* skip file comment if present */
skip_bytes(d,0);
if (flg & UZLIB_FHCRC) /* ignore header crc if present */
skip_bytes(d,2);
return UZLIB_OK;
}
/* inflate next byte of compressed stream */
static int uncompress_stream (UZLIB_DATA *d) {
do {
int res;
/* start a new block */
if (d->bType == -1) {
next_blk:
/* read final block flag */
d->bFinal = getbit(d);
/* read block type (2 bits) */
d->bType = read_bits(d, 2, 0);
DBG_PRINT("Started new block: type=%d final=%d\n", d->bType, d->bFinal);
if (d->bType == 1) {
/* build fixed huffman trees */
build_fixed_trees(&d->ltree, &d->dtree);
} else if (d->bType == 2) {
/* decode trees from stream */
res = decode_trees(d, &d->ltree, &d->dtree);
if (res != UZLIB_OK)
return res;
}
}
/* process current block */
switch (d->bType) {
case 0:
/* decompress uncompressed block */
res = inflate_uncompressed_block(d);
break;
case 1:
case 2:
/* decompress block with fixed or dynamic huffman trees. These */
/* trees were decoded previously, so it's the same routine for both */
res = inflate_block_data(d, &d->ltree, &d->dtree);
break;
default:
return UZLIB_DATA_ERROR;
}
if (res == UZLIB_DONE && !d->bFinal) {
/* the block has ended (without producing more data), but we
can't return without data, so start procesing next block */
goto next_blk;
}
if (res != UZLIB_OK)
return res;
} while (--d->destSize);
return UZLIB_OK;
}
/*
* This implementation has a different usecase to Paul Sokolovsky's
* uzlib implementation, in that it is designed to target IoT devices
* such as the ESP8266. Here clarity and compact code size is an
* advantage, but the ESP8266 only has 40-45Kb free heap, and has to
* process files with an unpacked size of up 256Kb, so a streaming
* implementation is essential.
*
* I have taken the architectural decision to hide the implementation
* detials from the uncompress routines and the caller must provide
* three support routines to handle the streaming:
*
* void get_byte(void)
* void put_byte(uchar b)
* uchar recall_byte(uint offset)
*
* This last must be able to recall an output byte with an offet up to
* the maximum dictionary size.
*/
int uzlib_inflate (
uchar (*get_byte)(void),
void (*put_byte)(uchar v),
uchar (*recall_byte)(uint offset),
uint len, uint *crc, void **state) {
int res;
/* initialize decompression structure */
UZLIB_DATA *d = (UZLIB_DATA *) uz_malloc(sizeof(*d));
if (!d)
return UZLIB_MEMORY_ERROR;
*state = d;
d->bitcount = 0;
d->bFinal = 0;
d->bType = -1;
d->curLen = 0;
d->destSize = len;
d->get_byte = get_byte;
d->put_byte = put_byte;
d->recall_byte = recall_byte;
if ((res = UZLIB_SETJMP(unwindAddr)) != 0) {
if (crc)
*crc = d->checksum;
/* handle long jump */
if (d) {
uz_free(d);
*state = NULL;
}
return res;
}
/* create RAM copy of clcidx byte array */
memcpy(d->clcidx, CLCIDX_INIT, sizeof(d->clcidx));
/* build extra bits and base tables */
build_bits_base(d->lengthBits, d->lengthBase, 4, 3);
build_bits_base(d->distBits, d->distBase, 2, 1);
d->lengthBits[28] = 0; /* fix a special case */
d->lengthBase[28] = 258;
if ((res = parse_gzip_header(d))== UZLIB_OK)
while ((res = uncompress_stream(d)) == UZLIB_OK)
{}
if (res == UZLIB_DONE) {
d->checksum = get_le_uint32(d);
(void) get_le_uint32(d); /* already got length so ignore */
}
UZLIB_THROW(res);
}
......@@ -9,7 +9,14 @@ NodeMCU "application developers" just need a ready-made firmware. There's a [clo
Occasional NodeMCU firmware hackers don't need full control over the complete tool chain. They might not want to setup a Linux VM with the build environment. Docker to the rescue. Give [Docker NodeMCU build](https://hub.docker.com/r/marcelstoer/nodemcu-build/) a try.
### Linux Build Environment
NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. There is a [post in the esp8266.com Wiki](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain) that describes this.
NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. The NodeMCU project embeds a [ready-made tool chain](https://github.com/nodemcu/nodemcu-firmware/blob/401fa56b863873c4cbf0b6581eb36fc61046ff6c/Makefile#L225) for Linux/x86-64 by default. After working through the [Build Options](#build-options) below, simply start the build process with
```
make
```
!!! note
Building the tool chain from scratch is out of NodeMCU's scope. Refer to [ESP toolchains](https://github.com/jmattsson/esp-toolchains) for related information.
### Git
If you decide to build with either the Docker image or the native environment then use Git to clone the firmware sources instead of downloading the ZIP file from GitHub. Only cloning with Git will retrieve the referenced submodules:
......
Whilst the Lua Virtual Machine (LVM) can compile Lua source dynamically and this can prove
very flexible during development, you will use less RAM resources if you precompile
your sources before execution.
## Compiling Lua directly on your ESP8266
- The standard [string.dump \(function)](https://www.lua.org/manual/5.1/manual.html#pdf-string.dump) returns a string containing the binary code for the specified function and you can write this to a SPIFFS file.
- [`node.compile()`](modules/node/#nodecompile) wraps this 'load and dump to file' operation into a single atomic library call.
The issue with both of these approaches is that compilation is RAM-intensive and hence
you will find that you will need to break your application into a lot of small and
compilable modules in order to avoid hitting RAM constraints. This can be mitigated
by doing all compiles immediately after a [node.restart()`](modules/node/#noderestart).
## Compiling Lua on your PC for Uploading
If you install `lua` on your development PC or Laptop then you can use the standard Lua
compiler to syntax check any Lua source before downloading it to the ESP8266 module. However,
the NodeMCU compiler output uses different data types (e.g. it supports ROMtables) so the
compiled output from standard `luac` cannot run on the ESP8266.
Compiling source on one platform for use on another (e.g. Intel 64-bit Windows to ESP8266) is
known as _cross-compilation_ and the NodeMCU firmware build now automatically generates
a `luac.cross` image as standard in the firmware root directory; this can be used to
compile and to syntax-check Lua source on the Development machine for execution under
NodeMCU Lua on the ESP8266.
`luac.cross` will translate Lua source files into binary files that can be later loaded
and executed by the LVM. Such binary files, which normally have the `.lc` (lua code)
extension are loaded directly by the LVM without the RAM overhead of compilation.
Each `luac.cross` execution produces a single output file containing the bytecodes
for all source files given in the output file `luac.out`, but you would normally
change this with the `-o` option. If you wish you can mix Lua source files (and
even Lua binary files) on the command line. You can use '-' to indicate the
standard input as a source file and '--' to signal the end of options (that is, all
remaining arguments will be treated as files even if they start with '-').
`luac.cross` supports the standard `luac` options `-l`, `-o`, `-p`, `-s` and `-v`,
as well as the `-h` option which produces the current help overview.
NodeMCU also implements some major extensions to support the use of the
[Lua Flash Store (LFS)](lfs.md)), in that it can produce an LFS image file which
is loaded as an overlay into the firmware in flash memory; the LVM can access and
execute this code directly from flash without needing to store code in RAM. This
mode is enabled by specifying the `-f`option.
`luac.cross` supports two separate image formats:
- **Compact relocatable**. This is selected by the `-f` option. Here the compiler compresses the compiled binary so that image is small for downloading over Wifi/WAN (e.g. a full 64Kb LFS image is compressed down to a 22Kb file.) The LVM processes such image in two passes with the integrity of the image validated on the first, and the LFS itself gets upated on the second. The LVM also checks that the image will fit in the allocated LFS region before loading, but you can also use the `-m` option to throw a compile error if the image is too large, for example `-m 0x10000` will raise an error if the image will not load into a 64Kb regions.
- **Absolute**. This is selected by the `-a <baseAddr>` option. Here the compiler fixes all addresses relative to the base address specified. This allows an LFS absolute image to be loaded directly into the ESP flash using a tool such as `esptool.py`.
These two modes target two separate use cases: the compact relocatable format
facilitates simple OTA updates to an LFS based Lua application; the absolute format
facilitates factory installation of LFS based applicaitons.
Also note that the `app/lua/luac_cross` make and Makefile can be executed to build
just the `luac.cross` image. You must first ensure that the following options in
`app/include/user_config.h` are matched to your target configuration:
```c
//#define LUA_NUMBER_INTEGRAL // uncomment if you want an integer build
//#define LUA_FLASH_STORE 0x10000 // uncomment if you LFS support
```
Developers have successfully built this on Linux (including docker builds), MacOS, Win10/WSL and WinX/Cygwin.
......@@ -19,6 +19,12 @@ Arithmetic right shift a number equivalent to `value >> shift` in C.
#### Returns
the number shifted right (arithmetically)
#### Example
```lua
bit.arshift(3, 1) -- returns 1
-- Using a 4 bits representation: 0011 >> 1 == 0001
```
## bit.band()
Bitwise AND, equivalent to `val1 & val2 & ... & valn` in C.
......@@ -34,6 +40,12 @@ Bitwise AND, equivalent to `val1 & val2 & ... & valn` in C.
#### Returns
the bitwise AND of all the arguments (number)
### Example
```lua
bit.band(3, 2) -- returns 2
-- Using a 4 bits representation: 0011 & 0010 == 0010
```
## bit.bit()
Generate a number with a 1 bit (used for mask generation). Equivalent to `1 << position` in C.
......@@ -47,6 +59,11 @@ Generate a number with a 1 bit (used for mask generation). Equivalent to `1 << p
#### Returns
a number with only one 1 bit at position (the rest are set to 0)
### Example
```lua
bit.bit(4) -- returns 16
```
## bit.bnot()
Bitwise negation, equivalent to `~value in C.`
......@@ -74,6 +91,12 @@ Bitwise OR, equivalent to `val1 | val2 | ... | valn` in C.
#### Returns
the bitwise OR of all the arguments (number)
### Example
```lua
bit.bor(3, 2) -- returns 3
-- Using a 4 bits representation: 0011 | 0010 == 0011
```
## bit.bxor()
Bitwise XOR, equivalent to `val1 ^ val2 ^ ... ^ valn` in C.
......@@ -89,6 +112,12 @@ Bitwise XOR, equivalent to `val1 ^ val2 ^ ... ^ valn` in C.
#### Returns
the bitwise XOR of all the arguments (number)
### Example
```lua
bit.bxor(3, 2) -- returns 1
-- Using a 4 bits representation: 0011 ^ 0010 == 0001
```
## bit.clear()
Clear bits in a number.
......@@ -103,8 +132,12 @@ Clear bits in a number.
#### Returns
the number with the bit(s) cleared in the given position(s)
## bit.isclear()
### Example
```lua
bit.clear(3, 0) -- returns 2
```
## bit.isclear()
Test if a given bit is cleared.
#### Syntax
......@@ -117,6 +150,11 @@ Test if a given bit is cleared.
#### Returns
true if the bit at the given position is 0, false othewise
### Example
```lua
bit.isclear(2, 0) -- returns true
```
## bit.isset()
Test if a given bit is set.
......@@ -131,6 +169,11 @@ Test if a given bit is set.
#### Returns
true if the bit at the given position is 1, false otherwise
### Example
```lua
bit.isset(2, 0) -- returns false
```
## bit.lshift()
Left-shift a number, equivalent to `value << shift` in C.
......@@ -144,6 +187,12 @@ Left-shift a number, equivalent to `value << shift` in C.
#### Returns
the number shifted left
### Example
```lua
bit.lshift(2, 2) -- returns 8
-- Using a 4 bits representation: 0010 << 2 == 1000
```
## bit.rshift()
Logical right shift a number, equivalent to `( unsigned )value >> shift` in C.
......@@ -158,6 +207,12 @@ Logical right shift a number, equivalent to `( unsigned )value >> shift` in C.
#### Returns
the number shifted right (logically)
### Example
```lua
bit.rshift(2, 1) -- returns 1
-- Using a 4 bits representation: 0010 >> 1 == 0001
```
## bit.set()
Set bits in a number.
......@@ -172,3 +227,8 @@ Set bits in a number.
#### Returns
the number with the bit(s) set in the given position(s)
### Example
```lua
bit.set(2, 0) -- returns 3
```
......@@ -8,6 +8,9 @@
!!! important
This module needs RTC time to operate correctly. Do not forget to include the [`rtctime`](rtctime.md) module **and** initialize it properly.
!!! important
The cron expression has to be in GMT/UTC!
## cron.schedule()
Creates a new schedule entry.
......
......@@ -3,7 +3,14 @@
| :----- | :-------------------- | :---------- | :------ |
| 2017-06-11 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ds18b20.c](../../../app/modules/ds18b20.c)|
This module provides access to the DS18B20 1-Wire digital thermometer. Note that NodeMCU offers both a C module (this one) and [a Lua module for this sensor](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_modules/ds18b20). See [#2003](https://github.com/nodemcu/nodemcu-firmware/pull/2003) for a discussion on the respective merits of them.
This module provides access to the DS18B20 1-Wire digital thermometer.
## Deprecation Notice
Note that NodeMCU offers both a C module (this one) and [a Lua module for this
sensor](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_modules/ds18b20).
The C implementation is deprecated and will be removed soon; please transition
to Lua code.
## ds18b20.read()
Issues a temperature conversion of all connected sensors on the onewire bus and returns the measurment results after a conversion delay in a callback function.
......
......@@ -16,6 +16,12 @@ At this point, you can just poke around and see what happened, but you cannot co
In order to do interactive debugging, add a call to `gdbstub.brk()` in your Lua code. This will trigger a break instruction and will trap into gdb as above. However, continuation is supported from a break instruction and so you can single step, set breakpoints, etc. Note that the lx106 processor as configured by Espressif only supports a single hardware breakpoint. This means that you can only put a single breakpoint in flash code. You can single step as much as you like.
## gdbstub.open()
Runs gdbstub initialization routine. It has to be run only once in code.
#### Syntax
`gdbstub.open()`
## gdbstub.brk()
Enters gdb by executing a `break 0,0` instruction.
......@@ -40,6 +46,7 @@ function entergdb()
print("Active")
end
gdbstub.open()
entergdb()
```
......
......@@ -11,7 +11,7 @@ The client adheres to version 3.1.1 of the [MQTT](https://en.wikipedia.org/wiki/
Creates a MQTT client.
#### Syntax
`mqtt.Client(clientid, keepalive[, username, password, cleansession])`
`mqtt.Client(clientid, keepalive[, username, password, cleansession, max_message_length])`
#### Parameters
- `clientid` client ID
......@@ -19,10 +19,38 @@ Creates a MQTT client.
- `username` user name
- `password` user password
- `cleansession` 0/1 for `false`/`true`. Default is 1 (`true`).
- `max_message_length`, how large messages to accept. Default is 1024.
#### Returns
MQTT client
#### Notes
According to MQTT specification the max PUBLISH length is 256Mb. This is too large for NodeMCU to realistically handle. To avoid
an out-of-memory situation, there is a limit on how big messages to accept. This is controlled by the `max_message_length` parameter.
In practice, this only affects incoming PUBLISH messages since all regular control packets are small.
The default 1024 was chosen as this was the implicit limit in NodeMCU 2.2.1 and older (where this was not handled at all).
Note that "message length" refers to the full MQTT message size, including fixed & variable headers, topic name, packet ID (if applicable),
and payload. For exact details, please see [the MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718037).
Any message *larger* than `max_message_length` will be (partially) delivered to the `overflow` callback, if defined. The rest
of the message will be discarded. Any subsequent messages should be handled as expected.
Discarded messages will still be ACK'ed if QoS level 1 or 2 was requested, even if the application stack cannot handle them.
Heap memory will be used to buffer any message which spans more than a single TCP packet. A single allocation for the full
message will be performed when the message header is first seen, to avoid heap fragmentation.
If allocation fails, the MQTT session will be disconnected.
Naturally, messages larger than `max_message_length` will not be stored.
Note that heap allocation may occur even if the individual messages are not larger than the configured max! For example,
the broker may send multiple smaller messages in quick succession, which could go into the same TCP packet. If the last message
in the TCP packet did not fit fully, a heap buffer will be allocated to hold the incomplete message while waiting for the next TCP packet.
The typical maximum size for a message to fit into a single TCP packet is 1460 bytes, but this depends on the network's MTU
configuration, any packet fragmentation, and as described above, multiple messages in the same TCP packet.
#### Example
```lua
-- init mqtt client without logins, keepalive timer 120s
......@@ -47,6 +75,11 @@ m:on("message", function(client, topic, data)
end
end)
-- on publish overflow receive event
m:on("overflow", function(client, topic, data)
print(topic .. " partial overflowed message: " .. data )
end)
-- for TLS: m:connect("192.168.11.118", secure-port, 1)
m:connect("192.168.11.118", 1883, 0, function(client)
print("connected")
......@@ -180,7 +213,7 @@ Registers a callback function for an event.
`mqtt:on(event, function(client[, topic[, message]]))`
#### Parameters
- `event` can be "connect", "message" or "offline"
- `event` can be "connect", "message", "offline" or "overflow"
- `function(client[, topic[, message]])` callback function. The first parameter is the client. If event is "message", the 2nd and 3rd param are received topic and message (strings).
#### Returns
......
......@@ -199,11 +199,10 @@ Reload the [LFS (Lua Flash Store)](../lfs.md) with the flash image provided. Fla
`node.flashreload(imageName)`
#### Parameters
`imageName` The of name of a image file in the filesystem to be loaded into the LFS.
`imageName` The name of a image file in the filesystem to be loaded into the LFS.
#### Returns
If the LFS image has the incorrect signature or size, then `false` is returned.
In the case of the `imagename` being a valid LFS image, this is then loaded into flash. The ESP is then immediately rebooted so control is not returned to the calling application.
`Error message` LFS images are now gzip compressed. In the case of the `imagename` being a valid LFS image, this is expanded and loaded into flash. The ESP is then immediately rebooted, _so control is not returned to the calling Lua application_ in the case of a successful reload. This reload process internally makes two passes through the LFS image file; and on the first it validates the file and header formats and detects any errors. If any is detected then an error string is returned.
## node.flashsize()
......
......@@ -41,12 +41,17 @@ The NodeMCU firmware supports the following displays in I²C and SPI mode:
- ld7032 60x32
- sh1106 128x64
- sh1107 - variants 64x128, seeed 96x96, 128x128
- sh1108 160x160
- ssd1305 128x32
- ssd1306 - variants 128x32, 128x64, 64x48, and 96x16
- ssd1309 128x64
- ssd1325 128x63
- ssd1327 96x96
- ssd1326 er 256x32
- ssd1327 - variants 96x96, ea w128128, and midas 128x128
- st7567 64x32
- st7588 jlx12864
- st75256 - variants jlx256128, jlx256160, jlx240160, jlx25664, and jlx172104
- uc1601 128x32
- uc1604 jlx19264
- uc1608 - 240x180 and erc24064 variants
......@@ -55,24 +60,28 @@ The NodeMCU firmware supports the following displays in I²C and SPI mode:
SPI only:
- hx1230 96x68
- il3820 v2 296x128
- ist3020 erc19264
- lc9081 - variants 160x80, 160x160, 240x128, and 240x64
- ls013b7dh03 128x128
- max7219 32x8
- nt7534 tg12864r
- pcd8544 84x48
- pcf8812 96x65
- sed1520 122x32
- ssd1322 nhd 256x64
- ssd1322 nhd 256x64 and nhd 128x64 variants
- ssd1329 128x96
- ssd1606 172x72
- ssd1607 200x200
- st7565 - variants 64128n, dogm128/132, erc12864, lm6059, c12832/c12864, and zolen 128x64
- st7567 - 132x64 and jlx12864 variants
- st7567 - variants 132x64, jlx12864, and enh_dg128064i
- st7586 - s028hn118a and erc240160 variants
- st75256 - jlx172104 and jlx256128 variants
- t6963 - variants 240x128, 240x64, 256x64, 128x64, and 160x80
- uc1701 - dogs102 and mini12864 variants
This integration uses full "RAM" memory buffer without picture loop and calls u8g2's `begin()` internally when creating a display object. It is based on [v2.19.8](https://github.com/olikraus/U8g2_Arduino/releases/tag/2.19.8).
This integration uses full "RAM" memory buffer without picture loop and calls u8g2's `begin()` internally when creating a display object. It is based on [v2.23.18](https://github.com/olikraus/U8g2_Arduino/releases/tag/2.23.18).
## Overview
......@@ -152,17 +161,31 @@ Initialize a display via I²C.
- `u8g2.ld7032_i2c_60x32()`
- `u8g2.sh1106_i2c_128x64_noname()`
- `u8g2.sh1106_i2c_128x64_vcomh0()`
- `u8g2.sh1107_i2c_64x128()`
- `u8g2.sh1107_i2c_seeed_96x96()`
- `u8g2.sh1107_i2c_128x128()`
- `u8g2.sh1108_i2c_160x160()`
- `u8g2.ssd1305_i2c_128x32_noname()`
- `u8g2.ssd1306_i2c_128x32_univision()`
- `u8g2.ssd1306_i2c_128x64_noname()`
- `u8g2.ssd1306_i2c_128x64_vcomh0()`
- `u8g2.ssd1309_i2c_128x64_noname0()`
- `u8g2.ssd1309_i2c_128x64_noname2()`
- `u8g2.ssd1306_i2c_128x64_alt0()`
- `u8g2.ssd1306_i2c_64x48_er()`
- `u8g2.ssd1306_i2c_96x16_er()`
- `u8g2.ssd1325_i2c_nhd_128x64()`
- `u8g2.ssd1326_i2c_er_256x32()`
- `u8g2.ssd1327_i2c_seeed_96x96()`
- `u8g2.ssd1327_i2c_ea_w128128()`
- `u8g2.ssd1327_i2c_midas_128x128()`
- `u8g2.st7567_i2c_64x32()`
- `u8g2.st7588_i2c_jlx12864()`
- `u8g2.st75256_i2c_jlx25664()`
- `u8g2.st75256_i2c_jlx172104()`
- `u8g2.st75256_i2c_jlx240160()`
- `u8g2.st75256_i2c_jlx256128()`
- `u8g2.st75256_i2c_jlx256160()`
- `u8g2.uc1602_i2c_128X32()`
- `u8g2.uc1604_i2c_jlx19264()`
- `u8g2.uc1608_i2c_erc24064()`
......@@ -206,8 +229,13 @@ disp = u8g2.ssd1306_i2c_128x64_noname(id, sla)
## SPI Display Drivers
Initialize a display via Hardware SPI.
- `u8g2.hx1230_96x68()`
- `u8g2.il3820_v2_296x128()`
- `u8g2.ist3020_erc19264()`
- `u8g2.lc7981_160x80()`
- `u8g2.lc7981_160x80()`
- `u8g2.lc7981_160x80()`
- `u8g2.lc7981_160x80()`
- `u8g2.ld7032_60x32()`
- `u8g2.ls013b7dh03_128x128()`
- `u8g2.max7219_32x8()`
......@@ -216,19 +244,29 @@ Initialize a display via Hardware SPI.
- `u8g2.pcf8812_96x65()`
- `u8g2.sh1106_128x64_noname()`
- `u8g2.sh1106_128x64_vcomh0()`
- `u8g2.sh1107_64x128()`
- `u8g2.sh1107_seeed_96x96()`
- `u8g2.sh1107_128x128()`
- `u8g2.sh1108_160x160()`
- `u8g2.sh1122_256x64()`
- `u8g2.ssd1305_128x32_noname()`
- `u8g2.ssd1306_128x32_univision()`
- `u8g2.ssd1306_128x64_noname()`
- `u8g2.ssd1306_128x64_vcomh0()`
- `u8g2.ssd1306_128x64_alt0()`
- `u8g2.ssd1306_64x48_er()`
- `u8g2.ssd1306_96x16_er()`
- `u8g2.ssd1309_128x64_noname0()`
- `u8g2.ssd1309_128x64_noname2()`
- `u8g2.sed1520_122x32()`
- `u8g2.ssd1322_nhd_128x64()`
- `u8g2.ssd1326_er_256x32()`
- `u8g2.ssd1327_ea_w128128()`
- `u8g2.ssd1327_midas_128x128()`
- `u8g2.ssd1322_nhd_256x64()`
- `u8g2.ssd1325_nhd_128x64()`
- `u8g2.ssd1327_seeed_96x96()`
- `u8g2.ssd1329_128x96_noname()`
- `u8g2.sed1520_122x32()`
- `u8g2.ssd1606_172x72()`
- `u8g2.ssd1607_200x200()`
- `u8g2.st7565_64128n()`
......@@ -239,13 +277,25 @@ Initialize a display via Hardware SPI.
- `u8g2.st7565_nhd_c12832()`
- `u8g2.st7565_nhd_c12864()`
- `u8g2.st7565_zolen_128x64()`
- `u8g2.st7567_enh_dg128064i()`
- `u8g2.st7567_64x32()`
- `u8g2.st7567_jxl12864()`
- `u8g2.st7567_pi_132x64()`
- `u8g2.st7586s_s028hn118a()`
- `u8g2.st7586s_erc240160()`
- `u8g2.st7588_jlx12864()`
- `u8g2.st7920_s_128x64()`
- `u8g2.st7920_s_192x32()`
- `u8g2.st75256_jlx25664()`
- `u8g2.st75256_jlx172104()`
- `u8g2.st75256_jlx240160()`
- `u8g2.st75256_jlx256128()`
- `u8g2.st75256_jlx256160()`
- `u8g2.t6963_240x128()`
- `u8g2.t6963_240x64()`
- `u8g2.t6963_256x64()`
- `u8g2.t6963_128x64()`
- `u8g2.t6963_160x80()`
- `u8g2.uc1601_128X32()`
- `u8g2.uc1604_jlx19264()`
- `u8g2.uc1608_240x128()`
......
......@@ -3,17 +3,52 @@
| :----- | :-------------------- | :---------- | :------ |
| 2015-08-05 | [Oli Kraus](https://github.com/olikraus/ucglib), [Arnim Läuger](https://github.com/devsaurus) | [Arnim Läuger](https://github.com/devsaurus) | [ucglib](../../../app/ucglib/)|
Ucglib is a graphics library developed at [olikraus/ucglib](https://github.com/olikraus/ucglib) with support for color TFT displays. The NodeMCU firmware supports a subset of these:
Ucglib is a graphics library developed at [olikraus/ucglib](https://github.com/olikraus/ucglib) with support for color TFT displays.
!!! note "BSD License for Ucglib Code"
Universal 8bit Graphics Library (http://code.google.com/p/u8glib/)
Copyright (c) 2014, olikraus@gmail.com
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The NodeMCU firmware supports a subset of these:
- HX8352C
- ILI9163
- ILI9341
- ILI9486
- PCF8833
- SEPS225
- SSD1331
- SSD1351
- ST7735
This integration is based on [v1.3.3](https://github.com/olikraus/Ucglib_Arduino/releases/tag/v1.3.3).
This integration is based on [v1.5.2](https://github.com/olikraus/Ucglib_Arduino/releases/tag/v1.5.2).
## Overview
......@@ -50,16 +85,31 @@ disp = ucg.ili9341_18x240x320_hw_spi(cs, dc, res)
This object provides all of ucglib's methods to control the display.
Again, refer to [GraphicsTest.lua](https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/ucglib/GraphicsTest.lua) to get an impression how this is achieved with Lua code. Visit the [ucglib homepage](https://github.com/olikraus/ucglib) for technical details.
### Displays
To get access to the display constructors, add the desired entries to the display table in [app/include/ucg_config.h](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/include/ucg_config.h):
### Display selection
HW SPI based displays with support in u8g2 can be enabled.
The procedure is different for ESP8266 and ESP32 platforms.
#### ESP8266
Add the desired entries to the display table in [app/include/ucg_config.h](../../../app/include/ucg_config.h):
```c
#define UCG_DISPLAY_TABLE \
UCG_DISPLAY_TABLE_ENTRY(ili9341_18x240x320_hw_spi, ucg_dev_ili9341_18x240x320, ucg_ext_ili9341_18) \
UCG_DISPLAY_TABLE_ENTRY(st7735_18x128x160_hw_spi, ucg_dev_st7735_18x128x160, ucg_ext_st7735_18) \
```
#### ESP32
Enable the desired entries for SPI displays in ucg's sub-menu (run `make menuconfig`).
### Fonts
ucglib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in [app/include/ucg_config.h](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/include/ucg_config.h) and recompile. Simply add the desired fonts to the font table:
ucglib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image.
The procedure is different for ESP8266 and ESP32 platforms.
#### ESP8266
Add the desired fonts to the font table in [app/include/ucg_config.h](../../../app/include/ucg_config.h):
```c
#define UCG_FONT_TABLE \
UCG_FONT_TABLE_ENTRY(font_7x13B_tr) \
......@@ -68,14 +118,19 @@ ucglib comes with a wide range of fonts for small displays. Since they need to b
UCG_FONT_TABLE_ENTRY(font_ncenR12_tr) \
UCG_FONT_TABLE_ENTRY(font_ncenR14_hr)
```
They'll be available as `ucg.<font_name>` in Lua.
They will be available as `ucg.<font_name>` in Lua.
#### ESP32
Add the desired fonts to the font selection sub-entry via `make menuconfig`.
## Display Drivers
Initialize a display via Hardware SPI.
- `hx8352c_18x240x400_hw_spi()`
- `ili9163_18x128x128_hw_spi()`
- `ili9341_18x240x320_hw_spi()`
- `ili9486_18x320x480_hw_spi()`
- `pcf8833_16x132x132_hw_spi()`
- `seps225_16x128x128_uvis_hw_spi()`
- `ssd1351_18x128x128_hw_spi()`
......@@ -84,24 +139,43 @@ Initialize a display via Hardware SPI.
- `st7735_18x128x160_hw_spi()`
#### Syntax
`ucg.st7735_18x128x160_hw_spi(cs, dc[, res])`
`ucg.st7735_18x128x160_hw_spi(bus, cs, dc[, res])`
#### Parameters
- `bus` SPI master bus
- `cs` GPIO pin for /CS
- `dc` GPIO pin for DC
- `res` GPIO pin for /RES (optional)
- `res` GPIO pin for /RES, none if omitted
#### Returns
ucg display object
#### Example
#### Example for ESP8266
```lua
spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0)
-- Hardware SPI CLK = GPIO14
-- Hardware SPI MOSI = GPIO13
-- Hardware SPI MISO = GPIO12 (not used)
-- Hardware SPI /CS = GPIO15 (not used)
cs = 8 -- GPIO15, pull-down 10k to GND
dc = 4 -- GPIO2
res = 0 -- GPIO16, RES is optional YMMV
disp = ucg.st7735_18x128x160_hw_spi(cs, dc, res)
bus = 1
spi.setup(bus, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0)
-- we won't be using the HSPI /CS line, so disable it again
gpio.mode(8, gpio.INPUT, gpio.PULLUP)
disp = ucg.st7735_18x128x160_hw_spi(bus, cs, dc, res)
```
#### Example for ESP32
```lua
sclk = 19
mosi = 23
cs = 22
dc = 16
res = 17
bus = spi.master(spi.HSPI, {sclk=sclk, mosi=mosi})
disp = ucg.st7735_18x128x160_hw_spi(bus, cs, dc, res)
```
## Constants
......@@ -196,16 +270,7 @@ See [ucglib setClipRange()](https://github.com/olikraus/ucglib/wiki/reference#se
See [ucglib setColor()](https://github.com/olikraus/ucglib/wiki/reference#setcolor).
## ucg.disp:setFont()
ucglib comes with a wide range of fonts for small displays. Since they need to be compiled into the firmware image, you'd need to include them in [app/include/ucg_config.h](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/include/ucg_config.h) and recompile. Simply add the desired fonts to the font table:
```c
#define UCG_FONT_TABLE \
UCG_FONT_TABLE_ENTRY(font_7x13B_tr) \
UCG_FONT_TABLE_ENTRY(font_helvB12_hr) \
UCG_FONT_TABLE_ENTRY(font_helvB18_hr) \
UCG_FONT_TABLE_ENTRY(font_ncenR12_tr) \
UCG_FONT_TABLE_ENTRY(font_ncenR14_hr)
```
They'll be available as `ucg.<font_name>` in Lua.
Define a ucg font for the glyph and string drawing functions. They are available as `ucg.<font_name>` in Lua.
#### Syntax
`disp:setFont(font)`
......
......@@ -78,3 +78,24 @@ end
G.module = nil -- disable Lua 5.0 style modules to save RAM
package.seeall = nil
--[[-------------------------------------------------------------------------------
These replaces the builtins loadfile & dofile with ones which preferentially
loads the corresponding module from LFS if present. Flipping the search order
is an exercise left to the reader.-
---------------------------------------------------------------------------------]]
local lf, df = loadfile, dofile
G.loadfile = function(n)
local mod, ext = n:match("(.*)%.(l[uc]a?)");
local fn, ba = index(mod)
if ba or (ext ~= 'lc' and ext ~= 'lua') then return lf(n) else return fn end
end
G.dofile = function(n)
local mod, ext = n:match("(.*)%.(l[uc]a?)");
local fn, ba = index(mod)
if ba or (ext ~= 'lc' and ext ~= 'lua') then return df(n) else return fn() end
end
......@@ -8,15 +8,16 @@ function init_spi_display()
local cs = 8 -- GPIO15, pull-down 10k to GND
local dc = 4 -- GPIO2
local res = 0 -- GPIO16
local bus = 1
spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8)
spi.setup(bus, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8)
-- we won't be using the HSPI /CS line, so disable it again
gpio.mode(8, gpio.INPUT, gpio.PULLUP)
-- initialize the matching driver for your display
-- see app/include/ucg_config.h
--disp = ucg.ili9341_18x240x320_hw_spi(cs, dc, res)
disp = ucg.st7735_18x128x160_hw_spi(cs, dc, res)
--disp = ucg.ili9341_18x240x320_hw_spi(bus, cs, dc, res)
disp = ucg.st7735_18x128x160_hw_spi(bus, cs, dc, res)
end
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment