Commit 81ec3665 authored by Marcel Stör's avatar Marcel Stör Committed by GitHub
Browse files

Merge pull request #1653 from nodemcu/dev-for-drop

New December master drop
parents ecf9c644 85c3a249
static const char http_html_backup[] = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x95, 0x59,
0xfd, 0x72, 0xdb, 0x36, 0x12, 0xff, 0xbb, 0x37, 0x73, 0xef, 0xb0, 0x1e,
0xf7, 0x42, 0xb9, 0xa1, 0x64, 0x7d, 0xd8, 0x69, 0xc6, 0x12, 0xd5, 0xc9,
0xe5, 0x92, 0x26, 0x37, 0x49, 0xda, 0xa9, 0xdd, 0xe9, 0x75, 0x32, 0x99,
0x0e, 0x44, 0x82, 0x26, 0x62, 0x0a, 0xe0, 0x01, 0xa0, 0x65, 0x5d, 0xea,
0x77, 0x3f, 0x2c, 0x01, 0x5a, 0x86, 0x40, 0x87, 0xb5, 0x5a, 0x57, 0x04,
0xb0, 0xdf, 0xfb, 0xc3, 0xee, 0x52, 0x5d, 0x1c, 0xfc, 0xeb, 0xa7, 0x97,
0x17, 0xbf, 0xff, 0xfc, 0x0a, 0x0a, 0xbd, 0x2e, 0x97, 0x7f, 0xff, 0xdb,
0xc2, 0x7d, 0xe3, 0x13, 0x25, 0x99, 0x79, 0xfa, 0x66, 0xb1, 0xa6, 0x9a,
0x00, 0x27, 0x6b, 0x9a, 0x44, 0xd7, 0x8c, 0x6e, 0x2a, 0x21, 0x75, 0x04,
0xa9, 0xe0, 0x9a, 0x72, 0x9d, 0x44, 0x1b, 0x96, 0xe9, 0x22, 0xc9, 0xe8,
0x35, 0x4b, 0xe9, 0xb0, 0x59, 0xc4, 0xc0, 0x38, 0xd3, 0x8c, 0x94, 0x43,
0x95, 0x92, 0x92, 0x26, 0x93, 0xd1, 0x38, 0x6a, 0x04, 0x69, 0xa6, 0x4b,
0xba, 0xfc, 0x8d, 0xbd, 0x66, 0xf0, 0x4e, 0x5c, 0x32, 0xbe, 0x38, 0xb6,
0x3b, 0x78, 0xa6, 0xf4, 0xb6, 0xa4, 0xa0, 0xb7, 0x15, 0x4d, 0x34, 0xbd,
0xd1, 0xc7, 0xa9, 0x52, 0xb8, 0xff, 0xcd, 0x77, 0xf0, 0x05, 0xbf, 0xbe,
0x59, 0x13, 0x69, 0x38, 0xce, 0x60, 0x3c, 0x6f, 0x96, 0x15, 0xc9, 0x32,
0xc6, 0x2f, 0xdb, 0xf5, 0x2d, 0xfe, 0x07, 0xff, 0xd0, 0xfc, 0x18, 0x1f,
0x56, 0x22, 0xdb, 0x3a, 0xd6, 0x82, 0xb2, 0xcb, 0x42, 0x9f, 0xc1, 0x64,
0x3c, 0xfe, 0x87, 0xe5, 0xce, 0x8d, 0xf1, 0xc3, 0x9c, 0xac, 0x59, 0xb9,
0x3d, 0x03, 0x45, 0xb8, 0x1a, 0x2a, 0x2a, 0x59, 0x6e, 0x0f, 0x51, 0xfd,
0x90, 0x94, 0xec, 0xd2, 0x68, 0x4b, 0x29, 0xd7, 0x54, 0xda, 0xfd, 0x15,
0x49, 0xaf, 0x2e, 0xa5, 0xa8, 0x79, 0x76, 0x06, 0x87, 0x27, 0x27, 0x27,
0xd9, 0xc9, 0x89, 0xaf, 0xfb, 0xd0, 0xc5, 0xc4, 0xa9, 0xad, 0x84, 0x62,
0x9a, 0x09, 0x23, 0x85, 0xac, 0x94, 0x28, 0x6b, 0x4d, 0x9d, 0x7c, 0x51,
0xdd, 0xb9, 0x21, 0xad, 0x65, 0x6e, 0xb5, 0x12, 0x5a, 0x8b, 0xf5, 0xdd,
0xb2, 0xa4, 0xf9, 0xee, 0xac, 0x89, 0xec, 0x19, 0xcc, 0xa6, 0xe3, 0xea,
0x66, 0xee, 0xb9, 0x75, 0xf2, 0x1c, 0xb7, 0xbc, 0x20, 0x91, 0x5a, 0x0b,
0xdf, 0x36, 0xc6, 0xab, 0x5a, 0xdb, 0xc0, 0xd4, 0x46, 0x09, 0x6f, 0x1e,
0x15, 0x2d, 0x69, 0xda, 0x9a, 0x3b, 0xdc, 0xd0, 0xd5, 0x15, 0x33, 0x9e,
0x57, 0x15, 0x25, 0x92, 0xf0, 0x94, 0x9e, 0x01, 0x17, 0x9c, 0xb6, 0xa6,
0xc9, 0x8c, 0xca, 0xa1, 0x24, 0x19, 0xab, 0x55, 0x10, 0xf5, 0x9c, 0xd1,
0x32, 0x53, 0xd4, 0x89, 0x72, 0xc4, 0xf7, 0xdc, 0xba, 0x19, 0xaa, 0x82,
0x64, 0x62, 0x63, 0xb6, 0xcc, 0x3f, 0x93, 0xd3, 0xea, 0x06, 0x26, 0xe6,
0x4f, 0x5e, 0xae, 0xc8, 0x60, 0x1c, 0x83, 0xfd, 0x77, 0x74, 0x72, 0x74,
0x8f, 0x9e, 0xfd, 0xaf, 0x49, 0xaf, 0xd3, 0x6b, 0xb6, 0xf6, 0xf2, 0x8e,
0x71, 0x80, 0x19, 0x7a, 0x1e, 0x26, 0x27, 0xcf, 0x73, 0x17, 0x0f, 0xc6,
0x87, 0x36, 0x4c, 0x18, 0xb9, 0x20, 0x4c, 0x43, 0x63, 0x44, 0x18, 0xa6,
0x7d, 0x2f, 0xd0, 0x52, 0x93, 0x3f, 0x96, 0xc1, 0x61, 0x9a, 0xa6, 0xf7,
0x25, 0x0c, 0xdb, 0x84, 0x4d, 0x9c, 0x68, 0x97, 0x26, 0x07, 0xb5, 0x5e,
0x67, 0x52, 0x51, 0x0a, 0x69, 0xec, 0x9d, 0x4e, 0xa7, 0x3b, 0x5c, 0x1a,
0xe6, 0x67, 0x46, 0xe3, 0x5a, 0x70, 0xa1, 0x2a, 0x92, 0x52, 0xdf, 0x6f,
0x0c, 0x9e, 0x6f, 0xb3, 0x9f, 0xc5, 0x6e, 0x09, 0x7e, 0x84, 0x86, 0x4e,
0xaf, 0x36, 0x59, 0x36, 0x04, 0x92, 0x72, 0xdd, 0xa7, 0xc4, 0xa1, 0xc6,
0x29, 0xb1, 0xfc, 0x2e, 0xce, 0x41, 0xc2, 0x03, 0xb0, 0xcc, 0xaa, 0xd6,
0xdf, 0x5a, 0x2a, 0x64, 0xac, 0x04, 0xdb, 0x5d, 0xab, 0x8c, 0xa9, 0xaa,
0x24, 0x5b, 0x13, 0x9d, 0x52, 0xa4, 0x57, 0x41, 0x1c, 0x3a, 0x6f, 0x67,
0x46, 0x53, 0x21, 0x89, 0xbd, 0x5c, 0x0e, 0xa3, 0x9e, 0xf9, 0x88, 0x8d,
0xd3, 0x4e, 0x68, 0xcc, 0x26, 0xab, 0x93, 0xd3, 0xef, 0xc3, 0x5c, 0x75,
0x38, 0x7b, 0x96, 0x8b, 0xb4, 0x56, 0xf1, 0xbd, 0x8d, 0x42, 0x5c, 0x53,
0x09, 0x5f, 0x3a, 0x41, 0x3d, 0x86, 0xa9, 0x51, 0x8a, 0x21, 0x89, 0xed,
0x1a, 0xdd, 0xde, 0xe9, 0xf3, 0x6a, 0xd4, 0xec, 0x5e, 0xb2, 0x10, 0x1c,
0xd4, 0xfa, 0xea, 0x83, 0xe2, 0xd9, 0xb3, 0x67, 0x9d, 0x58, 0xb3, 0x30,
0xf6, 0xe4, 0x9d, 0x78, 0x79, 0x71, 0x30, 0xf5, 0x23, 0x12, 0x30, 0x8d,
0x6a, 0xcd, 0x4a, 0xa6, 0xb7, 0xad, 0x29, 0xa5, 0x20, 0x26, 0xe4, 0x4d,
0x3d, 0x72, 0x76, 0x94, 0x94, 0x48, 0xc4, 0xac, 0x2e, 0x5a, 0x3b, 0x6e,
0x86, 0x2e, 0x64, 0xdf, 0x9f, 0xda, 0x88, 0x79, 0x1e, 0xcc, 0xaa, 0x6e,
0x58, 0xb7, 0x1e, 0x58, 0x2b, 0x82, 0x02, 0xde, 0x14, 0x83, 0xee, 0x7b,
0xec, 0xfc, 0x08, 0x8d, 0x76, 0xa9, 0xf1, 0xb6, 0x1e, 0x9f, 0x9c, 0x50,
0xfe, 0x61, 0x26, 0x45, 0x65, 0xb8, 0x78, 0xdc, 0xac, 0xf2, 0xa9, 0xfb,
0x9e, 0xd9, 0xef, 0xd5, 0xd5, 0xd4, 0x29, 0x70, 0xa0, 0x75, 0xe8, 0xeb,
0x96, 0x11, 0xb4, 0x01, 0x49, 0x4b, 0xa2, 0xd9, 0x35, 0x7d, 0xa0, 0x52,
0xa0, 0xfd, 0x26, 0x0d, 0x1b, 0x57, 0xbe, 0xbd, 0x1a, 0x7f, 0xea, 0x2a,
0x55, 0x77, 0xe5, 0xf1, 0xf5, 0x93, 0xaa, 0x64, 0xaa, 0xa7, 0x09, 0x85,
0xda, 0xc3, 0x26, 0xa9, 0x45, 0x15, 0xb4, 0xa2, 0xa0, 0x4d, 0xf5, 0xd4,
0x49, 0xaf, 0xd1, 0x7a, 0x05, 0x29, 0xb8, 0xae, 0xa1, 0x1f, 0x52, 0x8a,
0xcd, 0x1e, 0xb2, 0x9f, 0x3f, 0x7f, 0x3e, 0xff, 0xaa, 0x5f, 0xae, 0xa1,
0x3e, 0xb7, 0xd2, 0x9c, 0x13, 0x93, 0x50, 0x38, 0x83, 0x2f, 0x41, 0xb3,
0xff, 0x5c, 0x2b, 0xcd, 0xf2, 0xed, 0x1d, 0xe5, 0xe2, 0xb8, 0x19, 0x4a,
0x70, 0x2a, 0x3a, 0x76, 0xc3, 0x90, 0x79, 0xc4, 0xc1, 0x62, 0x89, 0xa7,
0x19, 0xbb, 0x06, 0x96, 0x25, 0xae, 0xe9, 0x2f, 0x91, 0x6b, 0xe1, 0x1a,
0xa1, 0x5d, 0xb5, 0x24, 0x6e, 0x38, 0x7a, 0x9b, 0x2d, 0x17, 0xc7, 0x66,
0xc7, 0x3f, 0xcb, 0x27, 0xb8, 0xc6, 0x8d, 0x62, 0xb6, 0x7c, 0x29, 0x38,
0xc7, 0x5a, 0x6e, 0x19, 0x40, 0x0b, 0xd8, 0x8a, 0x5a, 0xc2, 0x6f, 0x6c,
0xf8, 0x9a, 0x19, 0x1b, 0x66, 0x2d, 0xa9, 0xad, 0x47, 0xc8, 0xce, 0xa9,
0xde, 0x08, 0x79, 0xa5, 0xec, 0xe4, 0xe4, 0xf6, 0xd3, 0x92, 0x28, 0x95,
0xb8, 0x8b, 0x61, 0x94, 0xda, 0x6d, 0xc7, 0xdc, 0x2a, 0x76, 0x30, 0xb5,
0xdb, 0xb8, 0x6f, 0x52, 0xd3, 0x88, 0x6c, 0xe2, 0xbe, 0x7c, 0x72, 0x78,
0x33, 0x3d, 0x5d, 0xa5, 0x73, 0x13, 0x84, 0x8a, 0xec, 0x88, 0x6c, 0xb3,
0x41, 0x32, 0x07, 0x33, 0x9c, 0x0b, 0xdd, 0xb3, 0xd1, 0x64, 0x8f, 0x1d,
0xb5, 0xf3, 0xd6, 0x3e, 0xdb, 0xce, 0x6a, 0xf8, 0x94, 0x62, 0xd9, 0x6e,
0xce, 0x6b, 0xb0, 0x9e, 0x0a, 0x29, 0x0d, 0x5b, 0x22, 0xf2, 0xdc, 0xae,
0x49, 0xc5, 0x34, 0x29, 0x4d, 0x59, 0x49, 0xf0, 0x7e, 0x41, 0x55, 0x92,
0x94, 0x16, 0xa2, 0xcc, 0xa8, 0x4c, 0xa2, 0x26, 0x18, 0xf0, 0xc1, 0xa8,
0x8d, 0xe0, 0x38, 0x90, 0xbe, 0x61, 0x39, 0xfb, 0xa3, 0x32, 0xee, 0x9b,
0xa8, 0x3c, 0x52, 0x8d, 0xa3, 0x59, 0x57, 0x25, 0xd5, 0x14, 0x89, 0x3c,
0xbd, 0x3f, 0xb7, 0x32, 0x8f, 0xc3, 0x24, 0xa8, 0x7a, 0xb5, 0x66, 0xfa,
0x7e, 0x0a, 0x96, 0xe7, 0xe4, 0x9a, 0xfa, 0x81, 0xef, 0xca, 0xfd, 0xf4,
0x2e, 0xf7, 0x93, 0xe5, 0x79, 0x9d, 0xa6, 0x54, 0xa9, 0x03, 0x93, 0xe7,
0xc9, 0x5e, 0xaa, 0x98, 0x5d, 0x5b, 0x8c, 0xfc, 0x8e, 0x88, 0x70, 0x00,
0x29, 0x88, 0x02, 0x65, 0xf9, 0xf2, 0xba, 0x2c, 0xb7, 0x90, 0x5a, 0x00,
0xd1, 0x0c, 0xb1, 0xa3, 0x0b, 0x0a, 0x36, 0x5a, 0x0e, 0x25, 0x23, 0xc4,
0x50, 0x98, 0x9c, 0x2e, 0xcb, 0xee, 0xc8, 0x8a, 0xe9, 0xf2, 0x42, 0x6e,
0xcd, 0x3d, 0x1d, 0x8d, 0x90, 0x7d, 0x1a, 0x7a, 0x8f, 0x55, 0x31, 0x44,
0x5f, 0xe4, 0xe0, 0x17, 0x2d, 0x7f, 0x14, 0xf0, 0x4f, 0x53, 0xd5, 0xd1,
0x22, 0x6b, 0xcd, 0x39, 0xd5, 0x75, 0xd5, 0x1d, 0x1c, 0x7c, 0xf2, 0xee,
0xd0, 0xa2, 0x38, 0x41, 0x1d, 0x91, 0xd2, 0xd1, 0xf2, 0xd7, 0x2a, 0x23,
0xda, 0x58, 0x02, 0xe7, 0x9a, 0xe8, 0x5a, 0x59, 0x83, 0x4e, 0x90, 0xec,
0x8e, 0x7d, 0xa1, 0x52, 0xc9, 0x2a, 0x64, 0x05, 0xf7, 0xb9, 0x26, 0x12,
0xbe, 0x85, 0x04, 0xf2, 0x9a, 0xa7, 0x58, 0x30, 0x60, 0x60, 0x31, 0x2a,
0xe4, 0x11, 0x7c, 0x01, 0x69, 0x4c, 0x91, 0x1c, 0x32, 0x91, 0xd6, 0x6b,
0xca, 0xf5, 0xe8, 0xbf, 0x35, 0x95, 0xdb, 0x73, 0x47, 0xb0, 0xa3, 0x9c,
0xc3, 0xed, 0xdc, 0x17, 0x49, 0x56, 0x46, 0xe6, 0xb7, 0x83, 0xe8, 0xb0,
0xbd, 0x80, 0xd1, 0x51, 0x0c, 0xa4, 0x72, 0x9b, 0xf6, 0x3e, 0x44, 0x47,
0x7b, 0x4c, 0x4a, 0x8b, 0xea, 0x45, 0x59, 0xa2, 0x35, 0xa4, 0x54, 0x34,
0x06, 0x49, 0xcc, 0x9f, 0x32, 0x54, 0x77, 0x74, 0x3b, 0x3b, 0x55, 0x21,
0x36, 0x83, 0x3c, 0x86, 0xad, 0xb1, 0xd3, 0x1d, 0xbb, 0x0f, 0xcb, 0x61,
0xb0, 0x85, 0x24, 0x01, 0x6e, 0x72, 0x7e, 0x04, 0x5b, 0x94, 0x37, 0xf7,
0x48, 0x8c, 0x11, 0xf9, 0xd1, 0xa8, 0xa9, 0x5e, 0x23, 0xdb, 0xaa, 0x90,
0xa8, 0xe1, 0xc9, 0xe1, 0x07, 0x88, 0x9a, 0x51, 0x2b, 0x82, 0x33, 0x88,
0x10, 0xf8, 0xd1, 0x3d, 0xe6, 0xdb, 0x0e, 0x4b, 0x0a, 0x96, 0xd1, 0x41,
0x8e, 0x66, 0xf4, 0xea, 0xf8, 0x6b, 0x02, 0xb5, 0x18, 0xa4, 0xab, 0x18,
0x6e, 0x02, 0x91, 0x2e, 0x1d, 0x8a, 0xea, 0x0b, 0xb6, 0xa6, 0xa2, 0xd6,
0x48, 0x87, 0x2d, 0x69, 0x0c, 0xdf, 0x19, 0xf2, 0x1e, 0xb1, 0x92, 0xe6,
0x72, 0xd0, 0x19, 0xad, 0x03, 0x17, 0xf9, 0x23, 0xef, 0xc8, 0x72, 0x53,
0x9d, 0x16, 0x83, 0xe8, 0x58, 0x59, 0x4c, 0x7d, 0x56, 0x82, 0xff, 0xc0,
0x93, 0x08, 0x9e, 0xc2, 0x7b, 0xa2, 0x8b, 0x91, 0x24, 0x3c, 0x13, 0xeb,
0x81, 0x49, 0x6e, 0xf4, 0xe3, 0xab, 0x8b, 0x28, 0x06, 0x4e, 0x37, 0xe7,
0x3a, 0x86, 0x69, 0x9f, 0x31, 0x69, 0x2d, 0xc3, 0x98, 0xd9, 0xa4, 0x46,
0x87, 0xf9, 0xc4, 0x48, 0xca, 0x9d, 0x88, 0xe0, 0x70, 0xfa, 0xb5, 0xc3,
0x19, 0x1e, 0xf6, 0x28, 0xb7, 0x46, 0x0e, 0x54, 0x0c, 0x59, 0x60, 0x41,
0x33, 0xd6, 0xb5, 0xd1, 0x95, 0x6a, 0x5f, 0x8d, 0x54, 0x90, 0x60, 0x7e,
0x30, 0x96, 0x31, 0xcc, 0xf0, 0x38, 0x0c, 0xa7, 0x82, 0x83, 0xc4, 0xcc,
0xa1, 0xe3, 0x40, 0x38, 0x7e, 0x10, 0xff, 0x88, 0xfd, 0x11, 0xe3, 0x9c,
0xca, 0x0b, 0x7a, 0xa3, 0x11, 0x16, 0x2f, 0x36, 0x84, 0xdd, 0xbb, 0xbb,
0x30, 0xc0, 0x08, 0x2b, 0xf3, 0x17, 0x1d, 0x45, 0x7b, 0x26, 0xdc, 0x02,
0x2d, 0x15, 0x0d, 0x44, 0x3b, 0xdd, 0x58, 0x69, 0x44, 0x0e, 0x99, 0xc1,
0xb2, 0x11, 0xab, 0xb4, 0x34, 0x42, 0xa3, 0xc0, 0x10, 0xf7, 0x41, 0x32,
0xf8, 0xf7, 0xf9, 0x4f, 0x1f, 0x46, 0x15, 0x91, 0x8a, 0x0e, 0xb2, 0x7d,
0x77, 0x5d, 0x00, 0xbb, 0xbd, 0x68, 0xdb, 0xf6, 0x9e, 0x2f, 0xd9, 0xc8,
0x1e, 0xb0, 0x2c, 0x08, 0x4e, 0x7b, 0xd1, 0xd3, 0x86, 0xac, 0x22, 0x0c,
0xad, 0x7b, 0x90, 0x0a, 0x43, 0xfd, 0xb1, 0xdb, 0xee, 0xe8, 0x6d, 0x56,
0xd2, 0x28, 0x7e, 0xe0, 0xd0, 0x8d, 0x09, 0xb6, 0x28, 0x3f, 0x48, 0xf5,
0x9a, 0xb0, 0x92, 0x66, 0x30, 0x84, 0x8d, 0x14, 0x26, 0xf0, 0x6d, 0x53,
0xec, 0xa7, 0x77, 0x25, 0x0d, 0xb8, 0xd0, 0x90, 0x8b, 0x9a, 0xf7, 0xb1,
0x3c, 0x78, 0x6c, 0xcb, 0x7d, 0x77, 0x83, 0x3a, 0x88, 0x42, 0xa6, 0x4f,
0x1f, 0xb3, 0x91, 0xbd, 0x87, 0x9f, 0xda, 0xa0, 0xf5, 0x43, 0x4b, 0x39,
0xd2, 0x10, 0x2a, 0xad, 0xb0, 0x06, 0x2a, 0xa7, 0x0f, 0x61, 0x04, 0x6f,
0xaa, 0xbd, 0x75, 0x01, 0x38, 0xdc, 0x67, 0x57, 0xb6, 0xb5, 0xac, 0x69,
0x40, 0xd4, 0x75, 0xaf, 0x48, 0x87, 0x2c, 0x07, 0x6c, 0xdf, 0xb4, 0x25,
0x4c, 0xfa, 0x0c, 0x9b, 0x44, 0x0f, 0xa0, 0xb6, 0x7b, 0xd9, 0x59, 0x10,
0xec, 0x84, 0x12, 0x16, 0x47, 0x84, 0x61, 0x2d, 0xd1, 0x35, 0x53, 0x01,
0xa9, 0xc6, 0xe9, 0xe9, 0x87, 0x66, 0x84, 0xc2, 0x29, 0xad, 0xa9, 0x82,
0x94, 0xa7, 0x22, 0xa3, 0xbf, 0xfe, 0xf2, 0xf6, 0xa5, 0x58, 0x57, 0x82,
0x53, 0xae, 0x07, 0x98, 0x06, 0x3c, 0x37, 0x89, 0xb8, 0x26, 0x65, 0x4d,
0x8f, 0xf0, 0x1a, 0x3f, 0xf1, 0x26, 0xaf, 0xaf, 0xb1, 0x7a, 0x84, 0x77,
0x32, 0xe6, 0xbd, 0x65, 0x2a, 0xac, 0xda, 0xc6, 0xf2, 0xee, 0xba, 0x1c,
0x06, 0x71, 0x16, 0xf5, 0xd5, 0xcc, 0x7b, 0x22, 0xd7, 0x54, 0x17, 0x22,
0x8b, 0x21, 0x25, 0x65, 0x89, 0x2f, 0xa4, 0x31, 0x68, 0x63, 0xc9, 0x1f,
0xc6, 0x94, 0xce, 0x00, 0xde, 0x14, 0x12, 0x12, 0x34, 0x00, 0xfe, 0xf3,
0xfe, 0xdd, 0x1b, 0xad, 0xab, 0x5f, 0xa8, 0x99, 0x23, 0x94, 0x1e, 0xec,
0xd9, 0x82, 0x84, 0x23, 0xc1, 0x4b, 0x41, 0x32, 0xca, 0x33, 0x6f, 0x26,
0xe9, 0xc4, 0x40, 0xab, 0x7e, 0x80, 0x7c, 0x16, 0x2f, 0x71, 0x23, 0x43,
0x52, 0x65, 0xe2, 0xa9, 0x28, 0xde, 0x80, 0x7d, 0x1d, 0xb7, 0x5d, 0x2a,
0xb5, 0x0d, 0xe4, 0x23, 0x54, 0x0e, 0x27, 0xb1, 0x1d, 0x2f, 0xfe, 0x82,
0xf8, 0x8a, 0xf2, 0x41, 0x1b, 0x32, 0x8c, 0x5f, 0x73, 0x4d, 0xba, 0x7c,
0x57, 0x54, 0xbb, 0xd0, 0xbc, 0xa1, 0x24, 0xa3, 0x26, 0x31, 0x2f, 0xd2,
0x94, 0x56, 0xda, 0xa4, 0x2f, 0x22, 0x55, 0x55, 0xb2, 0x94, 0xa0, 0x6d,
0xc7, 0xd8, 0x84, 0xa3, 0x2e, 0x01, 0x3b, 0x3f, 0x06, 0x6d, 0x46, 0xe0,
0xcf, 0x3f, 0x61, 0x62, 0x3a, 0xd1, 0x77, 0xcd, 0x98, 0xd0, 0xa9, 0x94,
0x67, 0x83, 0xbe, 0xe4, 0x5f, 0x0a, 0xfd, 0xa2, 0xc2, 0x86, 0x89, 0xaa,
0x3b, 0x93, 0x8c, 0x03, 0xdd, 0xbc, 0xab, 0x15, 0x26, 0x49, 0xd3, 0x0b,
0xe1, 0xc9, 0x93, 0x86, 0x19, 0x0e, 0xdc, 0x5c, 0xd6, 0xd3, 0xc0, 0x90,
0xd6, 0xeb, 0x61, 0xad, 0x80, 0x51, 0x49, 0xf9, 0xa5, 0x2e, 0x60, 0x09,
0xe3, 0x07, 0x2b, 0x03, 0x1a, 0xe3, 0x77, 0x36, 0xe4, 0xec, 0xa9, 0x39,
0x81, 0x62, 0xb1, 0xfa, 0x4c, 0x53, 0x1d, 0xf5, 0x69, 0x41, 0x86, 0x9e,
0xb6, 0xe9, 0xb1, 0x8c, 0x94, 0x90, 0x7a, 0xb0, 0x03, 0x1a, 0x89, 0x61,
0xe5, 0xeb, 0x08, 0x27, 0xbe, 0xd5, 0x48, 0x2a, 0xc5, 0x60, 0x08, 0xa4,
0x79, 0xe8, 0x52, 0x17, 0x78, 0xe7, 0x12, 0x23, 0x2a, 0x85, 0xe5, 0x6b,
0x21, 0x2a, 0x54, 0xb7, 0xb4, 0xc3, 0x3b, 0x10, 0xf8, 0x60, 0xbb, 0x58,
0xf3, 0x96, 0xe0, 0xce, 0xa2, 0x0e, 0x11, 0xb9, 0x90, 0x30, 0x40, 0x39,
0xcc, 0x48, 0x19, 0xcf, 0xcd, 0xd7, 0xc2, 0x7a, 0x61, 0xd3, 0x30, 0x87,
0xa7, 0x4f, 0xd9, 0x83, 0xd6, 0xa3, 0xee, 0xa7, 0xf7, 0x94, 0x63, 0xd5,
0x43, 0xe6, 0x8f, 0xec, 0xd3, 0x08, 0x2b, 0x24, 0x16, 0xc6, 0xaf, 0x6a,
0xbf, 0x0d, 0xb7, 0x48, 0x65, 0xdb, 0xdb, 0x9b, 0x8b, 0xf7, 0xef, 0x20,
0x41, 0x15, 0x1d, 0x7c, 0x64, 0x85, 0x33, 0x37, 0x59, 0x95, 0x34, 0x6b,
0xdf, 0x26, 0x3a, 0xa8, 0xb4, 0xb8, 0x34, 0xa8, 0x46, 0x34, 0x76, 0xdf,
0x49, 0x27, 0x49, 0xf0, 0xb4, 0x64, 0xe9, 0x15, 0x24, 0x96, 0xa1, 0x67,
0x1c, 0xf3, 0x58, 0xbd, 0x19, 0xef, 0x83, 0x68, 0x47, 0x07, 0x65, 0xe7,
0x86, 0xae, 0x29, 0xcf, 0x4b, 0x3e, 0xd9, 0x0d, 0x9b, 0x2f, 0xaa, 0xd8,
0xf4, 0xe8, 0xf9, 0x63, 0xba, 0x9a, 0xf3, 0x8f, 0x5e, 0xc7, 0x98, 0xc5,
0x94, 0x76, 0x8f, 0xfe, 0xcd, 0x11, 0x56, 0x09, 0x52, 0xed, 0xbf, 0xad,
0x24, 0xed, 0x9b, 0x50, 0x57, 0x82, 0xed, 0x6b, 0x4f, 0xd4, 0xfe, 0x9c,
0xe7, 0x17, 0x24, 0x7f, 0x2a, 0x77, 0xdd, 0x70, 0xde, 0x1f, 0xa2, 0xf3,
0x94, 0x70, 0x34, 0xb6, 0x45, 0xa7, 0x8a, 0xfa, 0x52, 0x82, 0xb1, 0xe9,
0xc9, 0x49, 0x60, 0x50, 0x60, 0x74, 0xe8, 0xd8, 0x63, 0x8c, 0x7e, 0x4f,
0x78, 0x4d, 0x4a, 0x78, 0xc5, 0xb5, 0xdc, 0x46, 0x8f, 0xca, 0x90, 0xb3,
0x3e, 0xec, 0x35, 0x41, 0x5c, 0x28, 0x91, 0x69, 0x61, 0xca, 0x20, 0x06,
0xe7, 0x0e, 0x45, 0xe6, 0xee, 0x3a, 0x7d, 0xdd, 0xc0, 0x0f, 0xe7, 0xb1,
0xe0, 0xfa, 0xb4, 0x57, 0x13, 0x5a, 0xae, 0x25, 0xa6, 0x80, 0x1b, 0x4d,
0x61, 0x61, 0x78, 0xf4, 0x3b, 0xa3, 0x7d, 0xcd, 0xff, 0xda, 0xeb, 0xa2,
0x6d, 0x2c, 0xf8, 0x0a, 0xfb, 0x40, 0x07, 0xda, 0x30, 0xc3, 0xb0, 0x71,
0x53, 0xc1, 0xbd, 0xfe, 0xec, 0x85, 0xec, 0xf1, 0x40, 0xea, 0x81, 0x90,
0xf7, 0x23, 0x05, 0x52, 0x16, 0x84, 0x5f, 0xd2, 0xbe, 0xf1, 0x60, 0x7f,
0xee, 0xf3, 0x7f, 0xec, 0x70, 0x9b, 0x9e, 0x9a, 0xdb, 0x40, 0xa9, 0x1d,
0x44, 0xad, 0x52, 0x67, 0x9e, 0xdd, 0x0a, 0x28, 0x57, 0x57, 0x53, 0x8f,
0xac, 0xd3, 0xb4, 0x70, 0x54, 0xf6, 0xd4, 0xe3, 0xa2, 0xfb, 0xcd, 0x76,
0x3c, 0x3a, 0x0d, 0x32, 0xb2, 0x38, 0xbe, 0xfb, 0xf5, 0x68, 0x71, 0xec,
0x7e, 0xe6, 0xc5, 0xc7, 0xe6, 0x7f, 0x89, 0xff, 0x1f, 0x01, 0x02, 0x3f,
0xe6, 0x2a, 0x1f, 0x00, 0x00
};
unsigned int http_html_backup_len = 2393;
#!/bin/sh
# Uses zopfli for better gzip compression
# sudo apt-get install zopfli
zopfli --gzip ./enduser_setup.html
xxd -i ./enduser_setup.html.gz | sed 's/unsigned char/static const char/; s/__enduser_setup_html_gz/http_html_backup/' > http_html_backup.def
...@@ -2,15 +2,24 @@ ...@@ -2,15 +2,24 @@
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lmem.h"
#include "platform.h" #include "platform.h"
#include "c_types.h" #include "c_types.h"
#include "vfs.h" #include "vfs.h"
#include "c_string.h" #include "c_string.h"
#include <alloca.h>
#define FILE_READ_CHUNK 1024
static int file_fd = 0; static int file_fd = 0;
static int file_fd_ref = LUA_NOREF;
static int rtc_cb_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF;
typedef struct _file_fd_ud {
int fd;
} file_fd_ud;
static void table2tm( lua_State *L, vfs_time *tm ) static void table2tm( lua_State *L, vfs_time *tm )
{ {
...@@ -91,13 +100,48 @@ static int file_on(lua_State *L) ...@@ -91,13 +100,48 @@ static int file_on(lua_State *L)
// Lua: close() // Lua: close()
static int file_close( lua_State* L ) static int file_close( lua_State* L )
{ {
if(file_fd){ int need_pop = FALSE;
vfs_close(file_fd); file_fd_ud *ud;
file_fd = 0;
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
// fall back to last opened file
if (file_fd_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
// top of stack is now default file descriptor
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
lua_pop( L, 1 );
} else {
// no default file currently opened
return 0;
}
} else {
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
}
// unref default file descriptor
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
if(ud->fd){
vfs_close(ud->fd);
// mark as closed
ud->fd = 0;
} }
return 0; return 0;
} }
static int file_obj_free( lua_State *L )
{
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
if (ud->fd) {
// close file if it's still open
vfs_close(ud->fd);
ud->fd = 0;
}
return 0;
}
// Lua: format() // Lua: format()
static int file_format( lua_State* L ) static int file_format( lua_State* L )
{ {
...@@ -130,10 +174,10 @@ static int file_fscfg (lua_State *L) ...@@ -130,10 +174,10 @@ static int file_fscfg (lua_State *L)
static int file_open( lua_State* L ) static int file_open( lua_State* L )
{ {
size_t len; size_t len;
if(file_fd){
vfs_close(file_fd); // unref last file descriptor to allow gc'ing if not kept by user script
file_fd = 0; luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
} file_fd_ref = LUA_NOREF;
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname ); const char *basename = vfs_basename( fname );
...@@ -146,7 +190,14 @@ static int file_open( lua_State* L ) ...@@ -146,7 +190,14 @@ static int file_open( lua_State* L )
if(!file_fd){ if(!file_fd){
lua_pushnil(L); lua_pushnil(L);
} else { } else {
lua_pushboolean(L, 1); file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
ud->fd = file_fd;
luaL_getmetatable( L, "file.obj" );
lua_setmetatable( L, -2 );
// store reference to opened file
lua_pushvalue( L, -1 );
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
} }
return 1; return 1;
} }
...@@ -170,19 +221,36 @@ static int file_list( lua_State* L ) ...@@ -170,19 +221,36 @@ static int file_list( lua_State* L )
return 0; return 0;
} }
static int file_seek (lua_State *L) static int get_file_obj( lua_State *L, int *argpos )
{
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
*argpos = 2;
return ud->fd;
} else {
*argpos = 1;
return file_fd;
}
}
#define GET_FILE_OBJ int argpos; \
int fd = get_file_obj( L, &argpos );
static int file_seek (lua_State *L)
{ {
GET_FILE_OBJ;
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END}; static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL}; static const char *const modenames[] = {"set", "cur", "end", NULL};
if(!file_fd) if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
int op = luaL_checkoption(L, 1, "cur", modenames); int op = luaL_checkoption(L, argpos, "cur", modenames);
long offset = luaL_optlong(L, 2, 0); long offset = luaL_optlong(L, ++argpos, 0);
op = vfs_lseek(file_fd, offset, mode[op]); op = vfs_lseek(fd, offset, mode[op]);
if (op < 0) if (op < 0)
lua_pushnil(L); /* error */ lua_pushnil(L); /* error */
else else
lua_pushinteger(L, vfs_tell(file_fd)); lua_pushinteger(L, vfs_tell(fd));
return 1; return 1;
} }
...@@ -210,7 +278,6 @@ static int file_remove( lua_State* L ) ...@@ -210,7 +278,6 @@ static int file_remove( lua_State* L )
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname ); const char *basename = vfs_basename( fname );
luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid"); luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
file_close(L);
vfs_remove((char *)fname); vfs_remove((char *)fname);
return 0; return 0;
} }
...@@ -218,9 +285,11 @@ static int file_remove( lua_State* L ) ...@@ -218,9 +285,11 @@ static int file_remove( lua_State* L )
// Lua: flush() // Lua: flush()
static int file_flush( lua_State* L ) static int file_flush( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
if(vfs_flush(file_fd) == 0) if(vfs_flush(fd) == 0)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
lua_pushnil(L); lua_pushnil(L);
...@@ -231,10 +300,6 @@ static int file_flush( lua_State* L ) ...@@ -231,10 +300,6 @@ static int file_flush( lua_State* L )
static int file_rename( lua_State* L ) static int file_rename( lua_State* L )
{ {
size_t len; size_t len;
if(file_fd){
vfs_close(file_fd);
file_fd = 0;
}
const char *oldname = luaL_checklstring( L, 1, &len ); const char *oldname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( oldname ); const char *basename = vfs_basename( oldname );
...@@ -253,38 +318,64 @@ static int file_rename( lua_State* L ) ...@@ -253,38 +318,64 @@ static int file_rename( lua_State* L )
} }
// g_read() // g_read()
static int file_g_read( lua_State* L, int n, int16_t end_char ) static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
{ {
if(n <= 0 || n > LUAL_BUFFERSIZE) static char *heap_mem = NULL;
n = LUAL_BUFFERSIZE; // free leftover memory
if (heap_mem) {
luaM_free(L, heap_mem);
heap_mem = NULL;
}
if(n <= 0)
n = FILE_READ_CHUNK;
if(end_char < 0 || end_char >255) if(end_char < 0 || end_char >255)
end_char = EOF; end_char = EOF;
luaL_Buffer b;
if(!file_fd) if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
luaL_buffinit(L, &b); char *p;
char *p = luaL_prepbuffer(&b);
int i; int i;
n = vfs_read(file_fd, p, n); if (n > LUAL_BUFFERSIZE) {
for (i = 0; i < n; ++i) // get buffer from heap
if (p[i] == end_char) p = heap_mem = luaM_malloc(L, n);
{ } else {
++i; // small chunks go onto the stack
break; p = alloca(n);
} }
if(i==0){ n = vfs_read(fd, p, n);
luaL_pushresult(&b); /* close buffer */ // bypass search if no end character provided
return (lua_objlen(L, -1) > 0); /* check whether read something */ if (n > 0 && end_char != EOF) {
for (i = 0; i < n; ++i)
if (p[i] == end_char)
{
++i;
break;
}
} else {
i = n;
} }
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR); if (i == 0 || n == VFS_RES_ERR) {
luaL_addsize(&b, i); if (heap_mem) {
luaL_pushresult(&b); /* close buffer */ luaM_free(L, heap_mem);
return 1; /* read at least an `eol' */ heap_mem = NULL;
}
return 0;
}
vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
lua_pushlstring(L, p, i);
if (heap_mem) {
luaM_free(L, heap_mem);
heap_mem = NULL;
}
return 1;
} }
// Lua: read() // Lua: read()
...@@ -293,42 +384,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char ) ...@@ -293,42 +384,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
// file.read('q') will read until 'q' or EOF is reached. // file.read('q') will read until 'q' or EOF is reached.
static int file_read( lua_State* L ) static int file_read( lua_State* L )
{ {
unsigned need_len = LUAL_BUFFERSIZE; unsigned need_len = FILE_READ_CHUNK;
int16_t end_char = EOF; int16_t end_char = EOF;
size_t el; size_t el;
if( lua_type( L, 1 ) == LUA_TNUMBER )
GET_FILE_OBJ;
if( lua_type( L, argpos ) == LUA_TNUMBER )
{ {
need_len = ( unsigned )luaL_checkinteger( L, 1 ); need_len = ( unsigned )luaL_checkinteger( L, argpos );
if( need_len > LUAL_BUFFERSIZE ){
need_len = LUAL_BUFFERSIZE;
}
} }
else if(lua_isstring(L, 1)) else if(lua_isstring(L, argpos))
{ {
const char *end = luaL_checklstring( L, 1, &el ); const char *end = luaL_checklstring( L, argpos, &el );
if(el!=1){ if(el!=1){
return luaL_error( L, "wrong arg range" ); return luaL_error( L, "wrong arg range" );
} }
end_char = (int16_t)end[0]; end_char = (int16_t)end[0];
} }
return file_g_read(L, need_len, end_char); return file_g_read(L, need_len, end_char, fd);
} }
// Lua: readline() // Lua: readline()
static int file_readline( lua_State* L ) static int file_readline( lua_State* L )
{ {
return file_g_read(L, LUAL_BUFFERSIZE, '\n'); GET_FILE_OBJ;
return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd);
} }
// Lua: write("string") // Lua: write("string")
static int file_write( lua_State* L ) static int file_write( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
size_t l, rl; size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(file_fd, s, l); rl = vfs_write(fd, s, l);
if(rl==l) if(rl==l)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
...@@ -339,13 +434,15 @@ static int file_write( lua_State* L ) ...@@ -339,13 +434,15 @@ static int file_write( lua_State* L )
// Lua: writeline("string") // Lua: writeline("string")
static int file_writeline( lua_State* L ) static int file_writeline( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
size_t l, rl; size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(file_fd, s, l); rl = vfs_write(fd, s, l);
if(rl==l){ if(rl==l){
rl = vfs_write(file_fd, "\n", 1); rl = vfs_write(fd, "\n", 1);
if(rl==1) if(rl==1)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
...@@ -420,6 +517,20 @@ static int file_vol_umount( lua_State *L ) ...@@ -420,6 +517,20 @@ static int file_vol_umount( lua_State *L )
} }
static const LUA_REG_TYPE file_obj_map[] =
{
{ LSTRKEY( "close" ), LFUNCVAL( file_close ) },
{ LSTRKEY( "read" ), LFUNCVAL( file_read ) },
{ LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
{ LSTRKEY( "write" ), LFUNCVAL( file_write ) },
{ LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
{ LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
{ LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE file_vol_map[] = static const LUA_REG_TYPE file_vol_map[] =
{ {
{ LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )}, { LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )},
...@@ -459,6 +570,7 @@ static const LUA_REG_TYPE file_map[] = { ...@@ -459,6 +570,7 @@ static const LUA_REG_TYPE file_map[] = {
int luaopen_file( lua_State *L ) { int luaopen_file( lua_State *L ) {
luaL_rometatable( L, "file.vol", (void *)file_vol_map ); luaL_rometatable( L, "file.vol", (void *)file_vol_map );
luaL_rometatable( L, "file.obj", (void *)file_obj_map );
return 0; return 0;
} }
......
...@@ -190,19 +190,20 @@ static void seroutasync_done (task_param_t arg) ...@@ -190,19 +190,20 @@ static void seroutasync_done (task_param_t arg)
{ {
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32); luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
if (serout.lua_done_ref != LUA_REFNIL) { // we're here so serout.lua_done_ref != LUA_NOREF serout.delay_table = NULL;
if (serout.lua_done_ref != LUA_NOREF) {
lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref); lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
serout.lua_done_ref = LUA_NOREF;
if (lua_pcall(L, 0, 0, 0)) { if (lua_pcall(L, 0, 0, 0)) {
// Uncaught Error. Print instead of sudden reset // Uncaught Error. Print instead of sudden reset
luaL_error(L, "error: %s", lua_tostring(L, -1)); luaL_error(L, "error: %s", lua_tostring(L, -1));
} }
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
} }
} }
static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) { static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) {
(void) p; (void) p;
NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timing is delayed for short timings when debug output is enabled
if (serout.index < serout.tablelen) { if (serout.index < serout.tablelen) {
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level); GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level);
serout.level = serout.level==LOW ? HIGH : LOW; serout.level = serout.level==LOW ? HIGH : LOW;
...@@ -220,17 +221,25 @@ static int lgpio_serout( lua_State* L ) ...@@ -220,17 +221,25 @@ static int lgpio_serout( lua_State* L )
serout.pin = luaL_checkinteger( L, 1 ); serout.pin = luaL_checkinteger( L, 1 );
serout.level = luaL_checkinteger( L, 2 ); serout.level = luaL_checkinteger( L, 2 );
serout.repeats = luaL_optint( L, 4, 1 )-1; serout.repeats = luaL_optint( L, 4, 1 )-1;
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
uint8_t is_async = FALSE;
if (!lua_isnoneornil(L, 5)) { if (!lua_isnoneornil(L, 5)) {
if (lua_isnumber(L, 5)) { if (lua_isnumber(L, 5)) {
serout.lua_done_ref = LUA_REFNIL; serout.lua_done_ref = LUA_NOREF;
} else { } else {
lua_pushvalue(L, 5); lua_pushvalue(L, 5);
serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX); serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} }
is_async = TRUE;
} else { } else {
serout.lua_done_ref = LUA_NOREF; serout.lua_done_ref = LUA_NOREF;
} }
if (serout.delay_table) {
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
serout.delay_table = NULL;
}
luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin"); luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin");
luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" ); luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" );
luaL_argcheck(L, lua_istable( L, 3 ) && luaL_argcheck(L, lua_istable( L, 3 ) &&
...@@ -244,7 +253,7 @@ static int lgpio_serout( lua_State* L ) ...@@ -244,7 +253,7 @@ static int lgpio_serout( lua_State* L )
lua_pop( L, 1 ); lua_pop( L, 1 );
} }
if (serout.lua_done_ref != LUA_NOREF) { // async version for duration above 15 mSec if (is_async) { // async version for duration above 15 mSec
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) { if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
// Failed to init the timer // Failed to init the timer
luaL_error(L, "Unable to initialize timer"); luaL_error(L, "Unable to initialize timer");
...@@ -296,6 +305,7 @@ int luaopen_gpio( lua_State *L ) { ...@@ -296,6 +305,7 @@ int luaopen_gpio( lua_State *L ) {
platform_gpio_init(task_get_id(gpio_intr_callback_task)); platform_gpio_init(task_get_id(gpio_intr_callback_task));
#endif #endif
serout.done_taskid = task_get_id((task_callback_t) seroutasync_done); serout.done_taskid = task_get_id((task_callback_t) seroutasync_done);
serout.lua_done_ref = LUA_NOREF;
return 0; return 0;
} }
......
...@@ -14,13 +14,13 @@ static int http_callback_registry = LUA_NOREF; ...@@ -14,13 +14,13 @@ static int http_callback_registry = LUA_NOREF;
static void http_callback( char * response, int http_status, char * full_response ) static void http_callback( char * response, int http_status, char * full_response )
{ {
#if defined(HTTPCLIENT_DEBUG_ON) #if defined(HTTPCLIENT_DEBUG_ON)
c_printf( "http_status=%d\n", http_status ); dbg_printf( "http_status=%d\n", http_status );
if ( http_status != HTTP_STATUS_GENERIC_ERROR ) if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{ {
if (full_response != NULL) { if (full_response != NULL) {
c_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
} }
c_printf( "response=%s<EOF>\n", response ); dbg_printf( "response=%s<EOF>\n", response );
} }
#endif #endif
if (http_callback_registry != LUA_NOREF) if (http_callback_registry != LUA_NOREF)
......
...@@ -72,6 +72,7 @@ typedef struct lmqtt_userdata ...@@ -72,6 +72,7 @@ typedef struct lmqtt_userdata
uint8_t secure; uint8_t secure;
#endif #endif
bool connected; // indicate socket connected, not mqtt prot connected. bool connected; // indicate socket connected, not mqtt prot connected.
bool keepalive_sent;
ETSTimer mqttTimer; ETSTimer mqttTimer;
tConnState connState; tConnState connState;
}lmqtt_userdata; }lmqtt_userdata;
...@@ -212,9 +213,9 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code) ...@@ -212,9 +213,9 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code)
{ {
return; return;
} }
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua
lua_pushinteger(L, reason_code); lua_pushinteger(L, reason_code);
...@@ -241,12 +242,12 @@ static sint8 mqtt_send_if_possible(struct espconn *pesp_conn) ...@@ -241,12 +242,12 @@ static sint8 mqtt_send_if_possible(struct espconn *pesp_conn)
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if( mud->secure ) if( mud->secure )
{ {
espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
} }
else else
#endif #endif
{ {
espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
} }
mud->keep_alive_tick = 0; mud->keep_alive_tick = 0;
} }
...@@ -275,7 +276,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) ...@@ -275,7 +276,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len)
READPACKET: READPACKET:
if(length > MQTT_BUF_SIZE || length <= 0) if(length > MQTT_BUF_SIZE || length <= 0)
return; return;
// c_memcpy(in_buffer, pdata, length); // c_memcpy(in_buffer, pdata, length);
uint8_t temp_buffer[MQTT_BUF_SIZE]; uint8_t temp_buffer[MQTT_BUF_SIZE];
...@@ -287,7 +288,7 @@ READPACKET: ...@@ -287,7 +288,7 @@ READPACKET:
case MQTT_CONNECT_SENDING: case MQTT_CONNECT_SENDING:
case MQTT_CONNECT_SENT: case MQTT_CONNECT_SENT:
mud->event_timeout = 0; mud->event_timeout = 0;
if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){
NODE_DBG("MQTT: Invalid packet\r\n"); NODE_DBG("MQTT: Invalid packet\r\n");
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
...@@ -301,16 +302,16 @@ READPACKET: ...@@ -301,16 +302,16 @@ READPACKET:
{ {
espconn_disconnect(pesp_conn); espconn_disconnect(pesp_conn);
} }
mqtt_connack_fail(mud, MQTT_CONN_FAIL_NOT_A_CONNACK_MSG); mqtt_connack_fail(mud, MQTT_CONN_FAIL_NOT_A_CONNACK_MSG);
break; break;
} else if (mqtt_get_connect_ret_code(in_buffer) != MQTT_CONNACK_ACCEPTED) { } else if (mqtt_get_connect_ret_code(in_buffer) != MQTT_CONNACK_ACCEPTED) {
NODE_DBG("MQTT: CONNACK REFUSED (CODE: %d)\n", mqtt_get_connect_ret_code(in_buffer)); NODE_DBG("MQTT: CONNACK REFUSED (CODE: %d)\n", mqtt_get_connect_ret_code(in_buffer));
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) if(mud->secure)
{ {
...@@ -320,12 +321,12 @@ READPACKET: ...@@ -320,12 +321,12 @@ READPACKET:
#endif #endif
{ {
espconn_disconnect(pesp_conn); espconn_disconnect(pesp_conn);
} }
mqtt_connack_fail(mud, mqtt_get_connect_ret_code(in_buffer)); mqtt_connack_fail(mud, mqtt_get_connect_ret_code(in_buffer));
break; break;
} else { } else {
mud->connState = MQTT_DATA; mud->connState = MQTT_DATA;
NODE_DBG("MQTT: Connected\r\n"); NODE_DBG("MQTT: Connected\r\n");
...@@ -454,6 +455,7 @@ READPACKET: ...@@ -454,6 +455,7 @@ READPACKET:
break; break;
case MQTT_MSG_TYPE_PINGRESP: case MQTT_MSG_TYPE_PINGRESP:
// Ignore // Ignore
mud->keepalive_sent = 0;
NODE_DBG("MQTT: PINGRESP received\r\n"); NODE_DBG("MQTT: PINGRESP received\r\n");
break; break;
} }
...@@ -585,7 +587,7 @@ void mqtt_socket_timer(void *arg) ...@@ -585,7 +587,7 @@ void mqtt_socket_timer(void *arg)
NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
if(mud->event_timeout > 0){ if(mud->event_timeout > 0){
NODE_DBG("event_timeout: %d.\n", mud->event_timeout); NODE_DBG("event_timeout: %d.\n", mud->event_timeout);
mud->event_timeout --; mud->event_timeout --;
if(mud->event_timeout > 0){ if(mud->event_timeout > 0){
return; return;
} else { } else {
...@@ -605,7 +607,7 @@ void mqtt_socket_timer(void *arg) ...@@ -605,7 +607,7 @@ void mqtt_socket_timer(void *arg)
NODE_DBG("sSend MQTT_CONNECT failed.\n"); NODE_DBG("sSend MQTT_CONNECT failed.\n");
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING); mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING);
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) if(mud->secure)
{ {
...@@ -620,7 +622,7 @@ void mqtt_socket_timer(void *arg) ...@@ -620,7 +622,7 @@ void mqtt_socket_timer(void *arg)
} else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out. } else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out.
NODE_DBG("MQTT_CONNECT timeout.\n"); NODE_DBG("MQTT_CONNECT timeout.\n");
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) if(mud->secure)
{ {
...@@ -630,7 +632,7 @@ void mqtt_socket_timer(void *arg) ...@@ -630,7 +632,7 @@ void mqtt_socket_timer(void *arg)
#endif #endif
{ {
espconn_disconnect(mud->pesp_conn); espconn_disconnect(mud->pesp_conn);
} }
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING); mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING);
} else if(mud->connState == MQTT_DATA){ } else if(mud->connState == MQTT_DATA){
msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q));
...@@ -640,13 +642,20 @@ void mqtt_socket_timer(void *arg) ...@@ -640,13 +642,20 @@ void mqtt_socket_timer(void *arg)
// no queued event. // no queued event.
mud->keep_alive_tick ++; mud->keep_alive_tick ++;
if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){ if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){
uint8_t temp_buffer[MQTT_BUF_SIZE]; if (mud->keepalive_sent) {
mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); // Oh dear -- keepalive timer expired and still no ack of previous message
NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); mqtt_socket_reconnected(mud->pesp_conn, 0);
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); } else {
msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, uint8_t temp_buffer[MQTT_BUF_SIZE];
0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE);
mqtt_send_if_possible(mud->pesp_conn); NODE_DBG("\r\nMQTT: Send keepalive packet\r\n");
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection);
msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg,
0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) );
mud->keepalive_sent = 1;
mud->keep_alive_tick = 0; // Need to reset to zero in case flow control stopped.
mqtt_send_if_possible(mud->pesp_conn);
}
} }
} }
} }
...@@ -675,6 +684,7 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -675,6 +684,7 @@ static int mqtt_socket_client( lua_State* L )
// create a object // create a object
mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata)); mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata));
c_memset(mud, 0, sizeof(*mud));
// pre-initialize it, in case of errors // pre-initialize it, in case of errors
mud->self_ref = LUA_NOREF; mud->self_ref = LUA_NOREF;
mud->cb_connect_ref = LUA_NOREF; mud->cb_connect_ref = LUA_NOREF;
...@@ -685,18 +695,8 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -685,18 +695,8 @@ static int mqtt_socket_client( lua_State* L )
mud->cb_suback_ref = LUA_NOREF; mud->cb_suback_ref = LUA_NOREF;
mud->cb_unsuback_ref = LUA_NOREF; mud->cb_unsuback_ref = LUA_NOREF;
mud->cb_puback_ref = LUA_NOREF; mud->cb_puback_ref = LUA_NOREF;
mud->pesp_conn = NULL;
#ifdef CLIENT_SSL_ENABLE
mud->secure = 0;
#endif
mud->keep_alive_tick = 0;
mud->event_timeout = 0;
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
mud->connected = false;
c_memset(&mud->mqttTimer, 0, sizeof(ETSTimer));
c_memset(&mud->mqtt_state, 0, sizeof(mqtt_state_t));
c_memset(&mud->connect_info, 0, sizeof(mqtt_connect_info_t));
// set its metatable // set its metatable
luaL_getmetatable(L, "mqtt.socket"); luaL_getmetatable(L, "mqtt.socket");
...@@ -761,7 +761,7 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -761,7 +761,7 @@ static int mqtt_socket_client( lua_State* L )
c_free(mud->connect_info.password); c_free(mud->connect_info.password);
mud->connect_info.password = NULL; mud->connect_info.password = NULL;
} }
return luaL_error(L, "not enough memory"); return luaL_error(L, "not enough memory");
} }
c_memcpy(mud->connect_info.client_id, clientId, idl); c_memcpy(mud->connect_info.client_id, clientId, idl);
...@@ -819,8 +819,8 @@ static int mqtt_delete( lua_State* L ) ...@@ -819,8 +819,8 @@ static int mqtt_delete( lua_State* L )
// ---- alloc-ed in mqtt_socket_lwt() // ---- alloc-ed in mqtt_socket_lwt()
if(mud->connect_info.will_topic){ if(mud->connect_info.will_topic){
c_free(mud->connect_info.will_topic); c_free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL; mud->connect_info.will_topic = NULL;
} }
if(mud->connect_info.will_message){ if(mud->connect_info.will_message){
...@@ -926,7 +926,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) ...@@ -926,7 +926,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
if( dns_reconn_count >= 5 ){ if( dns_reconn_count >= 5 ){
NODE_ERR( "DNS Fail!\n" ); NODE_ERR( "DNS Fail!\n" );
// Note: should delete the pesp_conn or unref self_ref here. // Note: should delete the pesp_conn or unref self_ref here.
struct espconn *pesp_conn = arg; struct espconn *pesp_conn = arg;
if(pesp_conn != NULL) { if(pesp_conn != NULL) {
lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse;
...@@ -934,7 +934,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) ...@@ -934,7 +934,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS); mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS);
} }
} }
mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing. mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing.
return -1; return -1;
} }
...@@ -1070,7 +1070,7 @@ static int mqtt_socket_connect( lua_State* L ) ...@@ -1070,7 +1070,7 @@ static int mqtt_socket_connect( lua_State* L )
} }
stack++; stack++;
// call back function when a connection fails // call back function when a connection fails
if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){ if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){
lua_pushvalue(L, stack); // copy argument (func) to the top of stack lua_pushvalue(L, stack); // copy argument (func) to the top of stack
...@@ -1135,7 +1135,7 @@ static int mqtt_socket_close( lua_State* L ) ...@@ -1135,7 +1135,7 @@ static int mqtt_socket_close( lua_State* L )
// Send disconnect message // Send disconnect message
mqtt_message_t* temp_msg = mqtt_msg_disconnect(&mud->mqtt_state.mqtt_connection); mqtt_message_t* temp_msg = mqtt_msg_disconnect(&mud->mqtt_state.mqtt_connection);
NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]); NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]);
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) { if(mud->secure) {
espconn_status = espconn_secure_send(mud->pesp_conn, temp_msg->data, temp_msg->length); espconn_status = espconn_secure_send(mud->pesp_conn, temp_msg->data, temp_msg->length);
...@@ -1150,7 +1150,7 @@ static int mqtt_socket_close( lua_State* L ) ...@@ -1150,7 +1150,7 @@ static int mqtt_socket_close( lua_State* L )
} }
} }
mud->connected = 0; mud->connected = 0;
while (mud->mqtt_state.pending_msg_q) { while (mud->mqtt_state.pending_msg_q) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} }
...@@ -1595,7 +1595,7 @@ static const LUA_REG_TYPE mqtt_socket_map[] = { ...@@ -1595,7 +1595,7 @@ static const LUA_REG_TYPE mqtt_socket_map[] = {
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
}; };
static const LUA_REG_TYPE mqtt_map[] = { static const LUA_REG_TYPE mqtt_map[] = {
{ LSTRKEY( "Client" ), LFUNCVAL( mqtt_socket_client ) }, { LSTRKEY( "Client" ), LFUNCVAL( mqtt_socket_client ) },
...@@ -1609,7 +1609,7 @@ static const LUA_REG_TYPE mqtt_map[] = { ...@@ -1609,7 +1609,7 @@ static const LUA_REG_TYPE mqtt_map[] = {
{ LSTRKEY( "CONNACK_REFUSED_ID_REJECTED" ), LNUMVAL( MQTT_CONNACK_REFUSED_ID_REJECTED ) }, { LSTRKEY( "CONNACK_REFUSED_ID_REJECTED" ), LNUMVAL( MQTT_CONNACK_REFUSED_ID_REJECTED ) },
{ LSTRKEY( "CONNACK_REFUSED_SERVER_UNAVAILABLE" ), LNUMVAL( MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE ) }, { LSTRKEY( "CONNACK_REFUSED_SERVER_UNAVAILABLE" ), LNUMVAL( MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE ) },
{ LSTRKEY( "CONNACK_REFUSED_BAD_USER_OR_PASS" ), LNUMVAL( MQTT_CONNACK_REFUSED_BAD_USER_OR_PASS ) }, { LSTRKEY( "CONNACK_REFUSED_BAD_USER_OR_PASS" ), LNUMVAL( MQTT_CONNACK_REFUSED_BAD_USER_OR_PASS ) },
{ LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) }, { LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) },
{ LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) }, { LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) },
{ LNILKEY, LNILVAL } { LNILKEY, LNILVAL }
......
...@@ -134,6 +134,8 @@ static int ow_read_bytes( lua_State *L ) ...@@ -134,6 +134,8 @@ static int ow_read_bytes( lua_State *L )
if( size == 0 ) if( size == 0 )
return 0; return 0;
luaL_argcheck(L, size <= LUAL_BUFFERSIZE, 2, "Attempt to read too many characters");
luaL_Buffer b; luaL_Buffer b;
luaL_buffinit( L, &b ); luaL_buffinit( L, &b );
char *p = luaL_prepbuffer(&b); char *p = luaL_prepbuffer(&b);
......
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
#define MAX_ATTEMPTS 5 #define MAX_ATTEMPTS 5
#if 0 #if 0
# define sntp_dbg(...) c_printf(__VA_ARGS__) # define sntp_dbg(...) dbg_printf(__VA_ARGS__)
#else #else
# define sntp_dbg(...) # define sntp_dbg(...)
#endif #endif
......
// ***************************************************************************
// Somfy module for ESP8266 with NodeMCU
//
// Written by Lukas Voborsky, @voborsky
// based on https://github.com/Nickduino/Somfy_Remote
// Somfy protocol description: https://pushstack.wordpress.com/somfy-rts-protocol/
// and discussion: https://forum.arduino.cc/index.php?topic=208346.0
//
// MIT license, http://opensource.org/licenses/MIT
// ***************************************************************************
//#define NODE_DEBUG
#include "os_type.h"
#include "osapi.h"
#include "sections.h"
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "hw_timer.h"
#include "user_interface.h"
#define SYMBOL 640 // symbol width in microseconds
#define SOMFY_UP 0x2
#define SOMFY_STOP 0x1
#define SOMFY_DOWN 0x4
#define SOMFY_PROG 0x8
#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf"
static task_handle_t done_taskid;
static uint8_t pin;
static uint8_t frame[7];
static uint8_t sync;
static uint8_t repeat;
//static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us
// the `delay` array of constants must be in RAM as it is accessed from the timer interrupt
static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate)
static uint8_t repeatindex;
static uint8_t signalindex;
static uint8_t subindex;
static uint8_t bitcondition;
int lua_done_ref; // callback when transmission is done
void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) {
// NODE_DBG("remote: %x\n", remote);
// NODE_DBG("button: %x\n", button);
// NODE_DBG("rolling code: %x\n", code);
frame[0] = 0xA7; // Encryption key. Doesn't matter much
frame[1] = button << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = code >> 8; // Rolling code (big endian)
frame[3] = code; // Rolling code
frame[4] = remote >> 16; // Remote address
frame[5] = remote >> 8; // Remote address
frame[6] = remote; // Remote address
// frame[7] = 0x80;
// frame[8] = 0x0;
// frame[9] = 0x0;
// NODE_DBG("Frame:\t\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
// Checksum calculation: a XOR of all the nibbles
uint8_t checksum = 0;
for(uint8_t i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
//Checksum integration
frame[1] |= checksum; // If a XOR of all the nibbles is equal to 0, the blinds will consider the checksum ok.
// NODE_DBG("With checksum:\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
// Obfuscation: a XOR of all the uint8_ts
for(uint8_t i = 1; i < 7; i++) {
frame[i] ^= frame[i-1];
}
// NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
}
static void somfy_transmissionDone (task_param_t arg)
{
lua_State *L = lua_getstate();
lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref);
lua_done_ref = LUA_NOREF;
lua_call (L, 0, 0);
}
static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
(void) p;
// NODE_DBG("%d\t%d\n", signalindex, subindex);
switch (signalindex) {
case 0:
subindex = 0;
if(sync == 2) { // Only with the first frame.
//Wake-up pulse & Silence
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(9415);
break;
} else {
signalindex++; signalindex++; //no break means: go directly to step 3
}
case 1:
//Wake-up pulse & Silence
DIRECT_WRITE_LOW(pin);
signalindex++;
// delayMicroseconds(89565);
break;
case 2:
signalindex++;
// no break means go directly to step 3
// a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse
case 3:
// Hardware sync: two sync for the first frame, seven for the following ones.
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(4*SYMBOL);
break;
case 4:
DIRECT_WRITE_LOW(pin);
subindex++;
if (subindex < sync) {signalindex--;} else {signalindex++;}
// delayMicroseconds(4*SYMBOL);
break;
case 5:
// Software sync
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(4550);
break;
case 6:
DIRECT_WRITE_LOW(pin);
signalindex++;
subindex=0;
// delayMicroseconds(SYMBOL);
break;
case 7:
//Data: bits are sent one by one, starting with the MSB.
bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1;
if(bitcondition) {
DIRECT_WRITE_LOW(pin);
}
else {
DIRECT_WRITE_HIGH(pin);
}
signalindex++;
// delayMicroseconds(SYMBOL);
break;
case 8:
//Data: bits are sent one by one, starting with the MSB.
if(bitcondition) {
DIRECT_WRITE_HIGH(pin);
}
else {
DIRECT_WRITE_LOW(pin);
}
if (subindex<56) {
subindex++;
signalindex--;
}
else {
signalindex++;
}
// delayMicroseconds(SYMBOL);
break;
case 9:
DIRECT_WRITE_LOW(pin);
signalindex++;
// delayMicroseconds(30415); // Inter-frame silence
break;
case 10:
repeatindex++;
if (repeatindex<repeat) {
DIRECT_WRITE_HIGH(pin); //start repeat from step 3, but don't wait as after step 1
signalindex=4; subindex=0; sync=7;
} else {
platform_hw_timer_close(TIMER_OWNER);
if (lua_done_ref != LUA_NOREF) {
task_post_low (done_taskid, (task_param_t)0);
}
}
break;
}
if (signalindex<10) {
platform_hw_timer_arm_ticks(TIMER_OWNER, delay[signalindex-1]);
}
}
static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rolling_code, num_repeat, callback
if (!lua_isnumber(L, 4)) {
return luaL_error(L, "wrong arg range");
}
pin = luaL_checkinteger(L, 1);
uint64_t remote = luaL_checkinteger(L, 2);
uint8_t cmd = luaL_checkinteger(L, 3);
uint16_t code = luaL_checkinteger(L, 4);
repeat=luaL_optint( L, 5, 2 );
luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin");
luaL_unref(L, LUA_REGISTRYINDEX, lua_done_ref);
if (!lua_isnoneornil(L, 6)) {
lua_pushvalue(L, 6);
lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
lua_done_ref = LUA_NOREF;
}
MOD_CHECK_ID(gpio, pin);
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
buildFrame(frame, remote, cmd, code);
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
// Failed to init the timer
luaL_error(L, "Unable to initialize timer");
}
platform_hw_timer_set_func(TIMER_OWNER, sendCommand, 0);
sync=2;
signalindex=0; repeatindex=0;
sendCommand(0);
return 0;
}
static const LUA_REG_TYPE somfy_map[] = {
{ LSTRKEY( "UP" ), LNUMVAL( SOMFY_UP ) },
{ LSTRKEY( "DOWN" ), LNUMVAL( SOMFY_DOWN ) },
{ LSTRKEY( "PROG" ), LNUMVAL( SOMFY_PROG ) },
{ LSTRKEY( "STOP" ), LNUMVAL( SOMFY_STOP ) },
{ LSTRKEY( "sendcommand" ), LFUNCVAL(somfy_lua_sendcommand)},
{ LNILKEY, LNILVAL}
};
int luaopen_somfy( lua_State *L ) {
done_taskid = task_get_id((task_callback_t) somfy_transmissionDone);
return 0;
}
NODEMCU_MODULE(SOMFY, "somfy", somfy_map, luaopen_somfy);
\ No newline at end of file
...@@ -2,24 +2,13 @@ ...@@ -2,24 +2,13 @@
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "platform.h" #include "lmem.h"
#include "c_stdlib.h"
#include "u8g.h" #include "u8g.h"
#include "u8g_glue.h"
#include "u8g_config.h" #include "u8g_config.h"
struct _lu8g_userdata_t
{
u8g_t u8g;
};
typedef struct _lu8g_userdata_t lu8g_userdata_t;
// shorthand macro for the u8g structure inside the userdata
#define LU8G (&(lud->u8g))
// helper function: retrieve and check userdata argument // helper function: retrieve and check userdata argument
static lu8g_userdata_t *get_lud( lua_State *L ) static lu8g_userdata_t *get_lud( lua_State *L )
...@@ -780,214 +769,6 @@ static int lu8g_setFontLineSpacingFactor( lua_State *L ) ...@@ -780,214 +769,6 @@ static int lu8g_setFontLineSpacingFactor( lua_State *L )
} }
// ------------------------------------------------------------
// comm functions
//
#define I2C_CMD_MODE 0x000
#define I2C_DATA_MODE 0x040
#define ESP_I2C_ID 0
static uint8_t do_i2c_start(uint8_t id, uint8_t sla)
{
platform_i2c_send_start( id );
// ignore return value -> tolerate missing ACK
platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER );
return 1;
}
static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g)
{
/* are we requested to set the a0 state? */
if ( u8g->pin_list[U8G_PI_SET_A0] == 0 )
return 1;
/* setup bus, might be a repeated start */
if ( do_i2c_start( ESP_I2C_ID, u8g->i2c_addr ) == 0 )
return 0;
if ( u8g->pin_list[U8G_PI_A0_STATE] == 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 )
; //return 0;
}
else
{
platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE );
}
u8g->pin_list[U8G_PI_SET_A0] = 0;
return 1;
}
static void lu8g_digital_write( u8g_t *u8g, uint8_t pin_index, uint8_t value )
{
uint8_t pin;
pin = u8g->pin_list[pin_index];
if ( pin != U8G_PIN_NONE )
platform_gpio_write( pin, value );
}
void u8g_Delay(u8g_t *u8g, uint16_t msec)
{
const uint16_t chunk = 50;
if (u8g->use_delay == 0)
return;
while (msec > chunk)
{
os_delay_us( chunk*1000 );
msec -= chunk;
}
if (msec > 0)
os_delay_us( msec*1000 );
}
void u8g_MicroDelay(void)
{
os_delay_us( 1 );
}
void u8g_10MicroDelay(void)
{
os_delay_us( 10 );
}
uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
switch(msg)
{
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_INIT:
// we assume that the SPI interface was already initialized
// just care for the /CS and D/C pins
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
platform_gpio_mode( u8g->pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
platform_gpio_mode( u8g->pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g_digital_write( u8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 0)
{
/* disable */
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
}
else
{
/* enable */
//u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW);
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_LOW );
}
break;
case U8G_COM_MSG_RESET:
if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
lu8g_digital_write( u8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_WRITE_BYTE:
platform_spi_send( 1, 8, arg_val );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
platform_spi_send( 1, 8, *ptr++ );
arg_val--;
}
}
break;
}
return 1;
}
uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
switch(msg)
{
case U8G_COM_MSG_INIT:
// we assume that the i2c bus was already initialized
//u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]);
break;
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_RESET:
/* Currently disabled, but it could be enable. Previous restrictions have been removed */
/* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */
break;
case U8G_COM_MSG_CHIP_SELECT:
u8g->pin_list[U8G_PI_A0_STATE] = 0;
u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */
if ( arg_val == 0 )
{
/* disable chip, send stop condition */
platform_i2c_send_stop( ESP_I2C_ID );
}
else
{
/* enable, do nothing: any byte writing will trigger the i2c start */
}
break;
case U8G_COM_MSG_WRITE_BYTE:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
arg_val--;
}
}
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
u8g->pin_list[U8G_PI_A0_STATE] = arg_val;
u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
break;
}
return 1;
}
// device destructor // device destructor
static int lu8g_close_display( lua_State *L ) static int lu8g_close_display( lua_State *L )
{ {
...@@ -996,6 +777,19 @@ static int lu8g_close_display( lua_State *L ) ...@@ -996,6 +777,19 @@ static int lu8g_close_display( lua_State *L )
if ((lud = get_lud( L )) == NULL) if ((lud = get_lud( L )) == NULL)
return 0; return 0;
if (lud->cb_ref != LUA_NOREF) {
// this is the fb_rle device
u8g_dev_t *fb_dev = LU8G->dev;
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)(fb_dev->dev_mem);
uint8_t *fb_dev_buf = fb_dev_pb->buf;
luaM_free( L, fb_dev_buf );
luaM_free( L, fb_dev_pb );
luaM_free( L, fb_dev );
luaL_unref( L, lud->cb_ref, LUA_REGISTRYINDEX );
}
return 0; return 0;
} }
...@@ -1016,9 +810,10 @@ static int lu8g_close_display( lua_State *L ) ...@@ -1016,9 +810,10 @@ static int lu8g_close_display( lua_State *L )
return luaL_error( L, "i2c address required" ); \ return luaL_error( L, "i2c address required" ); \
\ \
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
lud->cb_ref = LUA_NOREF; \
\ \
lud->u8g.i2c_addr = (uint8_t)addr; \ lud->i2c_addr = (uint8_t)addr; \
lud->u8g.use_delay = del > 0 ? 1 : 0; \ lud->use_delay = del > 0 ? 1 : 0; \
\ \
u8g_InitI2C( LU8G, &u8g_dev_ ## device, U8G_I2C_OPT_NONE); \ u8g_InitI2C( LU8G, &u8g_dev_ ## device, U8G_I2C_OPT_NONE); \
\ \
...@@ -1049,8 +844,9 @@ U8G_DISPLAY_TABLE_I2C ...@@ -1049,8 +844,9 @@ U8G_DISPLAY_TABLE_I2C
unsigned del = luaL_optinteger( L, 4, 0 ); \ unsigned del = luaL_optinteger( L, 4, 0 ); \
\ \
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
lud->cb_ref = LUA_NOREF; \
\ \
lud->u8g.use_delay = del > 0 ? 1 : 0; \ lud->use_delay = del > 0 ? 1 : 0; \
\ \
u8g_InitHWSPI( LU8G, &u8g_dev_ ## device, cs, dc, res ); \ u8g_InitHWSPI( LU8G, &u8g_dev_ ## device, cs, dc, res ); \
\ \
...@@ -1064,6 +860,53 @@ U8G_DISPLAY_TABLE_I2C ...@@ -1064,6 +860,53 @@ U8G_DISPLAY_TABLE_I2C
// Unroll the display table and insert binding functions for SPI based displays. // Unroll the display table and insert binding functions for SPI based displays.
U8G_DISPLAY_TABLE_SPI U8G_DISPLAY_TABLE_SPI
// //
//
//
// This display forwards the framebuffer contents as run-length encoded chunks to a Lua callback
static int lu8g_fb_rle( lua_State *L ) {
lu8g_userdata_t *lud;
if ((lua_type( L, 1 ) != LUA_TFUNCTION) &&
(lua_type( L, 1 ) != LUA_TLIGHTFUNCTION)) {
luaL_typerror( L, 1, "function" );
}
int width = luaL_checkint( L, 2 );
int height = luaL_checkint( L, 3 );
luaL_argcheck( L, (width > 0) && (width < 256) && ((width % 8) == 0), 2, "invalid width" );
luaL_argcheck( L, (height > 0) && (height < 256) && ((height % 8) == 0), 3, "invalid height" );
// construct display device structures manually because width and height are configurable
uint8_t *fb_dev_buf = (uint8_t *)luaM_malloc( L, width );
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)luaM_malloc( L, sizeof( u8g_pb_t ) );
fb_dev_pb->p.page_height = 8;
fb_dev_pb->p.total_height = height;
fb_dev_pb->p.page_y0 = 0;
fb_dev_pb->p.page_y1 = 0;
fb_dev_pb->p.page = 0;
fb_dev_pb->width = width;
fb_dev_pb->buf = fb_dev_buf;
u8g_dev_t *fb_dev = (u8g_dev_t *)luaM_malloc( L, sizeof( u8g_dev_t ) );
fb_dev->dev_fn = u8g_dev_gen_fb_fn;
fb_dev->dev_mem = fb_dev_pb;
fb_dev->com_fn = u8g_com_esp8266_fbrle_fn;
lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) );
lua_pushvalue( L, 1 ); // copy argument (func) to the top of stack
lud->cb_ref = luaL_ref( L, LUA_REGISTRYINDEX );
/* set metatable for userdata */
luaL_getmetatable(L, "u8g.display");
lua_setmetatable(L, -2);
u8g_Init8BitFixedPort( LU8G, fb_dev, width, height, 0, 0, 0);
return 1;
}
//
// *************************************************************************** // ***************************************************************************
...@@ -1137,6 +980,8 @@ static const LUA_REG_TYPE lu8g_map[] = { ...@@ -1137,6 +980,8 @@ static const LUA_REG_TYPE lu8g_map[] = {
#define U8G_FONT_TABLE_ENTRY(font) \ #define U8G_FONT_TABLE_ENTRY(font) \
{ LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) }, { LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) },
U8G_FONT_TABLE U8G_FONT_TABLE
//
{ LSTRKEY( "fb_rle" ), LFUNCVAL( lu8g_fb_rle ) },
// Options for circle/ ellipse drawing // Options for circle/ ellipse drawing
{ LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) }, { LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) },
{ LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) }, { LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) },
......
/*
Functions for integrating U8glib into the nodemcu platform.
*/
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "u8g.h"
#include "u8g_glue.h"
// ------------------------------------------------------------
// comm functions
//
#define I2C_CMD_MODE 0x000
#define I2C_DATA_MODE 0x040
#define ESP_I2C_ID 0
static uint8_t do_i2c_start(uint8_t id, uint8_t sla)
{
platform_i2c_send_start( id );
// ignore return value -> tolerate missing ACK
platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER );
return 1;
}
static uint8_t u8g_com_esp8266_ssd_start_sequence(struct _lu8g_userdata_t *lu8g)
{
/* are we requested to set the a0 state? */
if ( lu8g->u8g.pin_list[U8G_PI_SET_A0] == 0 )
return 1;
/* setup bus, might be a repeated start */
if ( do_i2c_start( ESP_I2C_ID, lu8g->i2c_addr ) == 0 )
return 0;
if ( lu8g->u8g.pin_list[U8G_PI_A0_STATE] == 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 )
; //return 0;
}
else
{
platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE );
}
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 0;
return 1;
}
static void lu8g_digital_write( struct _lu8g_userdata_t *lu8g, uint8_t pin_index, uint8_t value )
{
uint8_t pin;
pin = lu8g->u8g.pin_list[pin_index];
if ( pin != U8G_PIN_NONE )
platform_gpio_write( pin, value );
}
void u8g_Delay(u8g_t *u8g, uint16_t msec)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
const uint16_t chunk = 50;
if (lu8g->use_delay == 0)
return;
while (msec > chunk)
{
os_delay_us( chunk*1000 );
msec -= chunk;
}
if (msec > 0)
os_delay_us( msec*1000 );
}
void u8g_MicroDelay(void)
{
os_delay_us( 1 );
}
void u8g_10MicroDelay(void)
{
os_delay_us( 10 );
}
uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_INIT:
// we assume that the SPI interface was already initialized
// just care for the /CS and D/C pins
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g_digital_write( lu8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 0)
{
/* disable */
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
}
else
{
/* enable */
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_LOW );
}
break;
case U8G_COM_MSG_RESET:
if ( lu8g->u8g.pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
lu8g_digital_write( lu8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_WRITE_BYTE:
platform_spi_send( 1, 8, arg_val );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
platform_spi_send( 1, 8, *ptr++ );
arg_val--;
}
}
break;
}
return 1;
}
uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_INIT:
// we assume that the i2c bus was already initialized
//u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]);
break;
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_RESET:
/* Currently disabled, but it could be enable. Previous restrictions have been removed */
/* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */
break;
case U8G_COM_MSG_CHIP_SELECT:
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = 0;
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */
if ( arg_val == 0 )
{
/* disable chip, send stop condition */
platform_i2c_send_stop( ESP_I2C_ID );
}
else
{
/* enable, do nothing: any byte writing will trigger the i2c start */
}
break;
case U8G_COM_MSG_WRITE_BYTE:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
arg_val--;
}
}
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = arg_val;
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
break;
}
return 1;
}
// ***************************************************************************
// Generic framebuffer device and RLE comm driver
//
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg)
{
switch(msg)
{
case U8G_DEV_MSG_PAGE_FIRST:
// tell comm driver to start new framebuffer
u8g_SetChipSelect(u8g, dev, 1);
break;
case U8G_DEV_MSG_PAGE_NEXT:
{
u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 )
return 0;
}
break;
}
return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
}
static int bit_at( uint8_t *buf, int line, int x )
{
uint8_t byte = buf[x];
return buf[x] & (1 << line) ? 1 : 0;
}
struct _lu8g_fbrle_item
{
uint8_t start_x;
uint8_t len;
};
struct _lu8g_fbrle_line
{
uint8_t num_valid;
struct _lu8g_fbrle_item items[0];
};
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lud = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_INIT:
// allocate memory -> done
// init buffer
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 1) {
// new frame starts
if (lud->cb_ref != LUA_NOREF) {
// fire callback with nil argument
lua_State *L = lua_getstate();
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
lua_pushnil( L );
lua_call( L, 1, 0 );
}
}
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
uint8_t xwidth = u8g->pin_list[U8G_PI_EN];
size_t fbrle_line_size = sizeof( struct _lu8g_fbrle_line ) + sizeof( struct _lu8g_fbrle_item ) * (xwidth/2);
int num_lines = arg_val / (xwidth/8);
uint8_t *buf = (uint8_t *)arg_ptr;
struct _lu8g_fbrle_line *fbrle_line;
if (!(fbrle_line = (struct _lu8g_fbrle_line *)c_malloc( fbrle_line_size ))) {
break;
}
for (int line = 0; line < num_lines; line++) {
int start_run = -1;
fbrle_line->num_valid = 0;
for (int x = 0; x < xwidth; x++) {
if (bit_at( buf, line, x ) == 0) {
if (start_run >= 0) {
// inside run, end it and enter result
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
fbrle_line->items[fbrle_line->num_valid++].len = x - start_run;
//NODE_ERR( " line: %d x: %d len: %d\n", line, start_run, x - start_run );
start_run = -1;
}
} else {
if (start_run < 0) {
// outside run, start it
start_run = x;
}
}
if (fbrle_line->num_valid >= xwidth/2) break;
}
// active run?
if (start_run >= 0 && fbrle_line->num_valid < xwidth/2) {
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
fbrle_line->items[fbrle_line->num_valid++].len = xwidth - start_run;
}
// line done, trigger callback
if (lud->cb_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size );
lua_call( L, 1, 0 );
}
}
c_free( fbrle_line );
}
break;
}
return 1;
}
#ifndef _U8G_GLUE_H_
#define _U8G_GLUE_H_
#include "u8g.h"
struct _lu8g_userdata_t
{
u8g_t u8g;
uint8_t i2c_addr;
uint8_t use_delay;
int cb_ref;
};
typedef struct _lu8g_userdata_t lu8g_userdata_t;
// shorthand macro for the u8g structure inside the userdata
#define LU8G (&(lud->u8g))
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
#endif
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "c_types.h" #include "c_types.h"
#include "c_string.h" #include "c_string.h"
#include "c_stdlib.h"
#include "websocketclient.h" #include "websocketclient.h"
...@@ -45,7 +46,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) { ...@@ -45,7 +46,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) {
} }
} }
static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int opCode) { static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *message, int opCode) {
NODE_DBG("websocketclient_onReceiveCallback\n"); NODE_DBG("websocketclient_onReceiveCallback\n");
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
...@@ -59,7 +60,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int op ...@@ -59,7 +60,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int op
if (data->onReceive != LUA_NOREF) { if (data->onReceive != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, data->onReceive); // load the callback function lua_rawgeti(L, LUA_REGISTRYINDEX, data->onReceive); // load the callback function
lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref); // pass itself, #1 callback argument lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref); // pass itself, #1 callback argument
lua_pushstring(L, message); // #2 callback argument lua_pushlstring(L, message, len); // #2 callback argument
lua_pushnumber(L, opCode); // #3 callback argument lua_pushnumber(L, opCode); // #3 callback argument
lua_call(L, 3, 0); lua_call(L, 3, 0);
} }
...@@ -102,6 +103,7 @@ static int websocket_createClient(lua_State *L) { ...@@ -102,6 +103,7 @@ static int websocket_createClient(lua_State *L) {
ws_info *ws = (ws_info *) lua_newuserdata(L, sizeof(ws_info)); ws_info *ws = (ws_info *) lua_newuserdata(L, sizeof(ws_info));
ws->connectionState = 0; ws->connectionState = 0;
ws->extraHeaders = NULL;
ws->onConnection = &websocketclient_onConnectionCallback; ws->onConnection = &websocketclient_onConnectionCallback;
ws->onReceive = &websocketclient_onReceiveCallback; ws->onReceive = &websocketclient_onReceiveCallback;
ws->onFailure = &websocketclient_onCloseCallback; ws->onFailure = &websocketclient_onCloseCallback;
...@@ -118,7 +120,6 @@ static int websocketclient_on(lua_State *L) { ...@@ -118,7 +120,6 @@ static int websocketclient_on(lua_State *L) {
NODE_DBG("websocketclient_on\n"); NODE_DBG("websocketclient_on\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData; ws_data *data = (ws_data *) ws->reservedData;
...@@ -170,7 +171,6 @@ static int websocketclient_connect(lua_State *L) { ...@@ -170,7 +171,6 @@ static int websocketclient_connect(lua_State *L) {
NODE_DBG("websocketclient_connect is called.\n"); NODE_DBG("websocketclient_connect is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData; ws_data *data = (ws_data *) ws->reservedData;
...@@ -188,11 +188,61 @@ static int websocketclient_connect(lua_State *L) { ...@@ -188,11 +188,61 @@ static int websocketclient_connect(lua_State *L) {
return 0; return 0;
} }
static header_t *realloc_headers(header_t *headers, int new_size) {
if(headers) {
for(header_t *header = headers; header->key; header++) {
c_free(header->value);
c_free(header->key);
}
c_free(headers);
}
if(!new_size)
return NULL;
return (header_t *)c_malloc(sizeof(header_t) * (new_size + 1));
}
static int websocketclient_config(lua_State *L) {
NODE_DBG("websocketclient_config is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
ws_data *data = (ws_data *) ws->reservedData;
luaL_checktype(L, 2, LUA_TTABLE);
lua_getfield(L, 2, "headers");
if(lua_istable(L, -1)) {
lua_pushnil(L);
int size = 0;
while(lua_next(L, -2)) {
size++;
lua_pop(L, 1);
}
ws->extraHeaders = realloc_headers(ws->extraHeaders, size);
if(ws->extraHeaders) {
header_t *header = ws->extraHeaders;
lua_pushnil(L);
while(lua_next(L, -2)) {
header->key = c_strdup(lua_tostring(L, -2));
header->value = c_strdup(lua_tostring(L, -1));
header++;
lua_pop(L, 1);
}
header->key = header->value = NULL;
}
}
lua_pop(L, 1); // pop headers
return 0;
}
static int websocketclient_send(lua_State *L) { static int websocketclient_send(lua_State *L) {
NODE_DBG("websocketclient_send is called.\n"); NODE_DBG("websocketclient_send is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData; ws_data *data = (ws_data *) ws->reservedData;
...@@ -225,7 +275,8 @@ static int websocketclient_gc(lua_State *L) { ...@@ -225,7 +275,8 @@ static int websocketclient_gc(lua_State *L) {
NODE_DBG("websocketclient_gc\n"); NODE_DBG("websocketclient_gc\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws->extraHeaders = realloc_headers(ws->extraHeaders, 0);
ws_data *data = (ws_data *) ws->reservedData; ws_data *data = (ws_data *) ws->reservedData;
...@@ -265,6 +316,7 @@ static const LUA_REG_TYPE websocket_map[] = ...@@ -265,6 +316,7 @@ static const LUA_REG_TYPE websocket_map[] =
static const LUA_REG_TYPE websocketclient_map[] = static const LUA_REG_TYPE websocketclient_map[] =
{ {
{ LSTRKEY("on"), LFUNCVAL(websocketclient_on) }, { LSTRKEY("on"), LFUNCVAL(websocketclient_on) },
{ LSTRKEY("config"), LFUNCVAL(websocketclient_config) },
{ LSTRKEY("connect"), LFUNCVAL(websocketclient_connect) }, { LSTRKEY("connect"), LFUNCVAL(websocketclient_connect) },
{ LSTRKEY("send"), LFUNCVAL(websocketclient_send) }, { LSTRKEY("send"), LFUNCVAL(websocketclient_send) },
{ LSTRKEY("close"), LFUNCVAL(websocketclient_close) }, { LSTRKEY("close"), LFUNCVAL(websocketclient_close) },
......
This diff is collapsed.
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
#include "c_stdio.h" #include "c_stdio.h"
#include "task/task.h" #include "task/task.h"
//#define WIFI_DEBUG
//#define EVENT_DEBUG
void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...); void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...);
void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer); void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer);
...@@ -37,7 +40,13 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref) ...@@ -37,7 +40,13 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref)
void wifi_change_default_host_name(void); void wifi_change_default_host_name(void);
#ifdef NODE_DEBUG #if defined(WIFI_DEBUG) || defined(NODE_DEBUG)
#define WIFI_DBG(...) c_printf(__VA_ARGS__)
#else
#define WIFI_DBG(...) //c_printf(__VA_ARGS__)
#endif
#if defined(EVENT_DEBUG) || defined(NODE_DEBUG)
#define EVENT_DBG(...) c_printf(__VA_ARGS__) #define EVENT_DBG(...) c_printf(__VA_ARGS__)
#else #else
#define EVENT_DBG(...) //c_printf(__VA_ARGS__) #define EVENT_DBG(...) //c_printf(__VA_ARGS__)
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
typedef struct { typedef struct {
int canary;
int size; int size;
uint8_t colorsPerLed; uint8_t colorsPerLed;
uint8_t values[0]; uint8_t values[0];
...@@ -132,8 +131,7 @@ static int ws2812_write(lua_State* L) { ...@@ -132,8 +131,7 @@ static int ws2812_write(lua_State* L) {
} }
else if (type == LUA_TUSERDATA) else if (type == LUA_TUSERDATA)
{ {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
buffer1 = buffer->values; buffer1 = buffer->values;
length1 = buffer->colorsPerLed*buffer->size; length1 = buffer->colorsPerLed*buffer->size;
...@@ -156,8 +154,7 @@ static int ws2812_write(lua_State* L) { ...@@ -156,8 +154,7 @@ static int ws2812_write(lua_State* L) {
} }
else if (type == LUA_TUSERDATA) else if (type == LUA_TUSERDATA)
{ {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
buffer2 = buffer->values; buffer2 = buffer->values;
length2 = buffer->colorsPerLed*buffer->size; length2 = buffer->colorsPerLed*buffer->size;
...@@ -173,14 +170,13 @@ static int ws2812_write(lua_State* L) { ...@@ -173,14 +170,13 @@ static int ws2812_write(lua_State* L) {
return 0; return 0;
} }
// Handle a buffer where we can store led values static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
static int ws2812_new_buffer(lua_State *L) { /* relative string position: negative means back from end */
const int leds = luaL_checkint(L, 1); if (pos < 0) pos += (ptrdiff_t)len + 1;
const int colorsPerLed = luaL_checkint(L, 2); return (pos >= 0) ? pos : 0;
}
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) {
// Allocate memory // Allocate memory
size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t); size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t);
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size); ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
...@@ -193,16 +189,26 @@ static int ws2812_new_buffer(lua_State *L) { ...@@ -193,16 +189,26 @@ static int ws2812_new_buffer(lua_State *L) {
buffer->size = leds; buffer->size = leds;
buffer->colorsPerLed = colorsPerLed; buffer->colorsPerLed = colorsPerLed;
// Store canary for future type checks return buffer;
buffer->canary = CANARY_VALUE; }
// Handle a buffer where we can store led values
static int ws2812_new_buffer(lua_State *L) {
const int leds = luaL_checkint(L, 1);
const int colorsPerLed = luaL_checkint(L, 2);
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
c_memset(buffer->values, 0, colorsPerLed * leds);
return 1; return 1;
} }
static int ws2812_buffer_fill(lua_State* L) { static int ws2812_buffer_fill(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
// Grab colors // Grab colors
int i, j; int i, j;
...@@ -230,11 +236,10 @@ static int ws2812_buffer_fill(lua_State* L) { ...@@ -230,11 +236,10 @@ static int ws2812_buffer_fill(lua_State* L) {
} }
static int ws2812_buffer_fade(lua_State* L) { static int ws2812_buffer_fade(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int fade = luaL_checkinteger(L, 2); const int fade = luaL_checkinteger(L, 2);
unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); unsigned direction = luaL_optinteger( L, 3, FADE_OUT );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number");
uint8_t * p = &buffer->values[0]; uint8_t * p = &buffer->values[0];
...@@ -260,17 +265,25 @@ static int ws2812_buffer_fade(lua_State* L) { ...@@ -260,17 +265,25 @@ static int ws2812_buffer_fade(lua_State* L) {
static int ws2812_buffer_shift(lua_State* L) { static int ws2812_buffer_shift(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int shiftValue = luaL_checkinteger(L, 2); const int shiftValue = luaL_checkinteger(L, 2);
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL ); const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); ptrdiff_t start = posrelat(luaL_optinteger(L, 4, 1), buffer->size);
luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size"); ptrdiff_t end = posrelat(luaL_optinteger(L, 5, -1), buffer->size);
if (start < 1) start = 1;
if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size;
start--;
int size = end - start;
size_t offset = start * buffer->colorsPerLed;
luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size");
int shift = shiftValue >= 0 ? shiftValue : -shiftValue; int shift = shiftValue >= 0 ? shiftValue : -shiftValue;
// check if we want to shift at all // check if we want to shift at all
if (shift == 0) if (shift == 0 || size <= 0)
{ {
return 0; return 0;
} }
...@@ -280,38 +293,38 @@ static int ws2812_buffer_shift(lua_State* L) { ...@@ -280,38 +293,38 @@ static int ws2812_buffer_shift(lua_State* L) {
size_t shift_len, remaining_len; size_t shift_len, remaining_len;
// calculate length of shift section and remaining section // calculate length of shift section and remaining section
shift_len = shift*buffer->colorsPerLed; shift_len = shift*buffer->colorsPerLed;
remaining_len = (buffer->size-shift)*buffer->colorsPerLed; remaining_len = (size-shift)*buffer->colorsPerLed;
if (shiftValue > 0) if (shiftValue > 0)
{ {
// Store the values which are moved out of the array (last n pixels) // Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len); c_memcpy(tmp_pixels, &buffer->values[offset + (size-shift)*buffer->colorsPerLed], shift_len);
// Move pixels to end // Move pixels to end
os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len); os_memmove(&buffer->values[offset + shift*buffer->colorsPerLed], &buffer->values[offset], remaining_len);
// Fill beginning with temp data // Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL) if (shift_type == SHIFT_LOGICAL)
{ {
c_memset(&buffer->values[0], 0, shift_len); c_memset(&buffer->values[offset], 0, shift_len);
} }
else else
{ {
c_memcpy(&buffer->values[0], tmp_pixels, shift_len); c_memcpy(&buffer->values[offset], tmp_pixels, shift_len);
} }
} }
else else
{ {
// Store the values which are moved out of the array (last n pixels) // Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[0], shift_len); c_memcpy(tmp_pixels, &buffer->values[offset], shift_len);
// Move pixels to end // Move pixels to end
os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len); os_memmove(&buffer->values[offset], &buffer->values[offset + shift*buffer->colorsPerLed], remaining_len);
// Fill beginning with temp data // Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL) if (shift_type == SHIFT_LOGICAL)
{ {
c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len); c_memset(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], 0, shift_len);
} }
else else
{ {
c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); c_memcpy(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len);
} }
} }
// Free memory // Free memory
...@@ -320,13 +333,107 @@ static int ws2812_buffer_shift(lua_State* L) { ...@@ -320,13 +333,107 @@ static int ws2812_buffer_shift(lua_State* L) {
return 0; return 0;
} }
static int ws2812_buffer_dump(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
lua_pushlstring(L, buffer->values, buffer->size * buffer->colorsPerLed);
return 1;
}
static int ws2812_buffer_replace(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t l = buffer->size;
ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), l);
uint8_t *src;
size_t srcLen;
if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
src = (uint8_t *) lua_tolstring(L, 2, &length);
srcLen = length / buffer->colorsPerLed;
} else {
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
src = rhs->values;
srcLen = rhs->size;
luaL_argcheck(L, rhs->colorsPerLed == buffer->colorsPerLed, 2, "Buffers have different colors");
}
luaL_argcheck(L, srcLen + start - 1 <= buffer->size, 2, "Does not fit into destination");
c_memcpy(buffer->values + (start - 1) * buffer->colorsPerLed, src, srcLen * buffer->colorsPerLed);
return 0;
}
// buffer:mix(factor1, buffer1, ..)
// factor is 256 for 100%
// uses saturating arithmetic (one buffer at a time)
static int ws2812_buffer_mix(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
int pos = 2;
size_t cells = buffer->size * buffer->colorsPerLed;
int n_sources = (lua_gettop(L) - 1) / 2;
struct {
int factor;
const uint8_t *values;
} source[n_sources];
int src;
for (src = 0; src < n_sources; src++, pos += 2) {
int factor = luaL_checkinteger(L, pos);
ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer");
luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape");
source[src].factor = factor;
source[src].values = src_buffer->values;
}
size_t i;
for (i = 0; i < cells; i++) {
int val = 0;
for (src = 0; src < n_sources; src++) {
val += ((int)(source[src].values[i] * source[src].factor) >> 8);
}
if (val < 0) {
val = 0;
} else if (val > 255) {
val = 255;
}
buffer->values[i] = val;
}
return 0;
}
// Returns the total of all channels
static int ws2812_buffer_power(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t cells = buffer->size * buffer->colorsPerLed;
size_t i;
int total = 0;
for (i = 0; i < cells; i++) {
total += buffer->values[i];
}
lua_pushnumber(L, total);
return 1;
}
static int ws2812_buffer_get(lua_State* L) { static int ws2812_buffer_get(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int led = luaL_checkinteger(L, 2) - 1; const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
int i; int i;
...@@ -339,10 +446,9 @@ static int ws2812_buffer_get(lua_State* L) { ...@@ -339,10 +446,9 @@ static int ws2812_buffer_get(lua_State* L) {
} }
static int ws2812_buffer_set(lua_State* L) { static int ws2812_buffer_set(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int led = luaL_checkinteger(L, 2) - 1; const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
int type = lua_type(L, 3); int type = lua_type(L, 3);
...@@ -387,24 +493,94 @@ static int ws2812_buffer_set(lua_State* L) { ...@@ -387,24 +493,94 @@ static int ws2812_buffer_set(lua_State* L) {
} }
static int ws2812_buffer_size(lua_State* L) { static int ws2812_buffer_size(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
lua_pushnumber(L, buffer->size); lua_pushnumber(L, buffer->size);
return 1; return 1;
} }
static int ws2812_buffer_sub(lua_State* L) {
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t l = lhs->size;
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
if (start < 1) start = 1;
if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
if (start <= end) {
ws2812_buffer *result = allocate_buffer(L, end - start + 1, lhs->colorsPerLed);
c_memcpy(result->values, lhs->values + lhs->colorsPerLed * (start - 1), lhs->colorsPerLed * (end - start + 1));
} else {
ws2812_buffer *result = allocate_buffer(L, 0, lhs->colorsPerLed);
}
return 1;
}
static int ws2812_buffer_concat(lua_State* L) {
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
luaL_argcheck(L, lhs->colorsPerLed == rhs->colorsPerLed, 1, "Can only concatenate buffers with same colors");
int colorsPerLed = lhs->colorsPerLed;
int leds = lhs->size + rhs->size;
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
c_memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size);
c_memcpy(buffer->values + lhs->colorsPerLed * lhs->size, rhs->values, rhs->colorsPerLed * rhs->size);
return 1;
}
static int ws2812_buffer_tostring(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_Buffer result;
luaL_buffinit(L, &result);
luaL_addchar(&result, '[');
int i;
int p = 0;
for (i = 0; i < buffer->size; i++) {
int j;
if (i > 0) {
luaL_addchar(&result, ',');
}
luaL_addchar(&result, '(');
for (j = 0; j < buffer->colorsPerLed; j++, p++) {
if (j > 0) {
luaL_addchar(&result, ',');
}
char numbuf[5];
c_sprintf(numbuf, "%d", buffer->values[p]);
luaL_addstring(&result, numbuf);
}
luaL_addchar(&result, ')');
}
luaL_addchar(&result, ']');
luaL_pushresult(&result);
return 1;
}
static const LUA_REG_TYPE ws2812_buffer_map[] = static const LUA_REG_TYPE ws2812_buffer_map[] =
{ {
{ LSTRKEY( "dump" ), LFUNCVAL( ws2812_buffer_dump )},
{ LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )},
{ LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )},
{ LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )},
{ LSTRKEY( "replace" ), LFUNCVAL( ws2812_buffer_replace )},
{ LSTRKEY( "mix" ), LFUNCVAL( ws2812_buffer_mix )},
{ LSTRKEY( "power" ), LFUNCVAL( ws2812_buffer_power )},
{ LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )}, { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "sub" ), LFUNCVAL( ws2812_buffer_sub )},
{ LSTRKEY( "__concat" ),LFUNCVAL( ws2812_buffer_concat )},
{ LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )}, { LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )},
{ LSTRKEY( "__tostring" ), LFUNCVAL( ws2812_buffer_tostring )},
{ LNILKEY, LNILVAL} { LNILKEY, LNILVAL}
}; };
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
#include "nodemcu_mdns.h" #include "nodemcu_mdns.h"
#if 0 #if 0
#define MDNS_DBG(...) os_printf(...) #define MDNS_DBG(...) dbg_printf(...)
#else #else
#define MDNS_DBG(...) do {} while (0) #define MDNS_DBG(...) do {} while (0)
#endif #endif
......
...@@ -355,11 +355,12 @@ sint32_t vfs_chdir( const char *path ) ...@@ -355,11 +355,12 @@ sint32_t vfs_chdir( const char *path )
sint32_t vfs_errno( const char *name ) sint32_t vfs_errno( const char *name )
{ {
vfs_fs_fns *fs_fns; vfs_fs_fns *fs_fns;
const char *normname = normalize_path( name );
char *outname; char *outname;
if (!name) name = ""; // current drive if (!name) name = ""; // current drive
const char *normname = normalize_path( name );
#ifdef BUILD_SPIFFS #ifdef BUILD_SPIFFS
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) { if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
return fs_fns->ferrno( ); return fs_fns->ferrno( );
......
...@@ -85,7 +85,7 @@ inline sint32_t vfs_flush( int fd ) { ...@@ -85,7 +85,7 @@ inline sint32_t vfs_flush( int fd ) {
// Returns: File size // Returns: File size
inline uint32_t vfs_size( int fd ) { inline uint32_t vfs_size( int fd ) {
vfs_file *f = (vfs_file *)fd; vfs_file *f = (vfs_file *)fd;
return f && f->fns->size ? f->fns->size( f ) : 0; return f ? f->fns->size( f ) : 0;
} }
// vfs_ferrno - get file system specific errno // vfs_ferrno - get file system specific errno
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#include "platform.h" #include "platform.h"
#include "spiffs.h" #include "spiffs.h"
#include "spiffs_nucleus.h"
spiffs fs; spiffs fs;
#define LOG_PAGE_SIZE 256 #define LOG_PAGE_SIZE 256
...@@ -10,9 +12,9 @@ spiffs fs; ...@@ -10,9 +12,9 @@ spiffs fs;
#define MIN_BLOCKS_FS 4 #define MIN_BLOCKS_FS 4
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[32*4]; static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
#if SPIFFS_CACHE #if SPIFFS_CACHE
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*2]; static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2];
#endif #endif
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
...@@ -168,8 +170,8 @@ static bool myspiffs_mount_internal(bool force_mount) { ...@@ -168,8 +170,8 @@ static bool myspiffs_mount_internal(bool force_mount) {
spiffs_fds, spiffs_fds,
sizeof(spiffs_fds), sizeof(spiffs_fds),
#if SPIFFS_CACHE #if SPIFFS_CACHE
spiffs_cache, myspiffs_cache,
sizeof(spiffs_cache), sizeof(myspiffs_cache),
#else #else
0, 0, 0, 0,
#endif #endif
...@@ -243,6 +245,7 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int ...@@ -243,6 +245,7 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd );
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ); static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd );
...@@ -295,7 +298,7 @@ static vfs_file_fns myspiffs_file_fns = { ...@@ -295,7 +298,7 @@ static vfs_file_fns myspiffs_file_fns = {
.eof = myspiffs_vfs_eof, .eof = myspiffs_vfs_eof,
.tell = myspiffs_vfs_tell, .tell = myspiffs_vfs_tell,
.flush = myspiffs_vfs_flush, .flush = myspiffs_vfs_flush,
.size = NULL, .size = myspiffs_vfs_size,
.ferrno = myspiffs_vfs_ferrno .ferrno = myspiffs_vfs_ferrno
}; };
...@@ -430,13 +433,17 @@ static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) { ...@@ -430,13 +433,17 @@ static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) {
static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) { static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) {
GET_FILE_FH(fd); GET_FILE_FH(fd);
return SPIFFS_read( &fs, fh, ptr, len ); sint32_t n = SPIFFS_read( &fs, fh, ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
} }
static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) { static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) {
GET_FILE_FH(fd); GET_FILE_FH(fd);
return SPIFFS_write( &fs, fh, (void *)ptr, len ); sint32_t n = SPIFFS_write( &fs, fh, (void *)ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
} }
static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) { static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) {
...@@ -456,7 +463,8 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int ...@@ -456,7 +463,8 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int
break; break;
} }
return SPIFFS_lseek( &fs, fh, off, spiffs_whence ); sint32_t res = SPIFFS_lseek( &fs, fh, off, spiffs_whence );
return res >= 0 ? res : VFS_RES_ERR;
} }
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) { static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) {
...@@ -477,6 +485,16 @@ static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) { ...@@ -477,6 +485,16 @@ static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) {
return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR; return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR;
} }
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
int32_t curpos = SPIFFS_tell( &fs, fh );
int32_t size = SPIFFS_lseek( &fs, fh, 0, SPIFFS_SEEK_END );
(void) SPIFFS_lseek( &fs, fh, curpos, SPIFFS_SEEK_SET );
return size;
}
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) { static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) {
return SPIFFS_errno( &fs ); return SPIFFS_errno( &fs );
} }
......
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