diff --git a/main.lua b/main.lua index 86d63ff..060e438 100644 --- a/main.lua +++ b/main.lua @@ -57,8 +57,24 @@ function displayTime() print("Local time : " .. time.year .. "-" .. time.month .. "-" .. time.day .. " " .. time.hour .. ":" .. time.minute .. ":" .. time.second .. " in " .. charactersOfTime .. " chars " .. wordsOfTime .. " words") - -- Write the buffer to the LEDs - ws2812.write(ledBuf) + --if lines 4 to 6 are inverted due to hardware-fuckup, unfuck it here + if ((inv46 ~= nil) and (inv46 == "on")) then + tempstring = ledBuf:sub(1,99) -- first 33 leds + rowend = {44,55,66} + for _, startled in ipairs(rowend) do + for i = 0,10 do + tempstring = tempstring .. ledBuf:sub((startled-i)*3-2,(startled-i)*3) + end + end + tempstring = tempstring .. ledBuf:sub((67*3)-2,ledBuf:len()) + ws2812.write(tempstring) + tempstring=nil + else + ws2812.write(ledBuf) + ledBuf=nil + end + + -- Used for debugging if (clockdebug ~= nil) then @@ -69,7 +85,7 @@ function displayTime() end end -- cleanup - ledBuf=nil + words=nil time=nil collectgarbage() @@ -116,7 +132,6 @@ function normalOperation() mydofile("webserver") startWebServer() end) - displayTime() -- Start the time Thread tmr.alarm(1, 20000, 1 ,function() diff --git a/os/esptool.py b/os/esptool.py index 93ab071..38ffb72 100755 --- a/os/esptool.py +++ b/os/esptool.py @@ -1,15 +1,16 @@ #!/usr/bin/env python +# NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/themadinventor/esptool/issues/21 # # ESP8266 ROM Bootloader Utility # https://github.com/themadinventor/esptool # -# Copyright (C) 2014 Fredrik Ahlberg +# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, other contributors as noted. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT +# +# This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # @@ -17,17 +18,23 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin # Street, Fifth Floor, Boston, MA 02110-1301 USA. -import sys -import struct -import serial -import math -import time import argparse +import hashlib +import inspect +import json import os +import serial +import struct import subprocess +import sys +import tempfile +import time -class ESPROM: +__version__ = "1.2-dev" + + +class ESPROM(object): # These are the currently known commands supported by the ROM ESP_FLASH_BEGIN = 0x02 ESP_FLASH_DATA = 0x03 @@ -55,72 +62,64 @@ class ESPROM: # OTP ROM addresses ESP_OTP_MAC0 = 0x3ff00050 ESP_OTP_MAC1 = 0x3ff00054 + ESP_OTP_MAC3 = 0x3ff0005c - # Sflash stub: an assembly routine to read from spi flash and send to host - SFLASH_STUB = "\x80\x3c\x00\x40\x1c\x4b\x00\x40\x21\x11\x00\x40\x00\x80" \ - "\xfe\x3f\xc1\xfb\xff\xd1\xf8\xff\x2d\x0d\x31\xfd\xff\x41\xf7\xff\x4a" \ - "\xdd\x51\xf9\xff\xc0\x05\x00\x21\xf9\xff\x31\xf3\xff\x41\xf5\xff\xc0" \ - "\x04\x00\x0b\xcc\x56\xec\xfd\x06\xff\xff\x00\x00" + # Flash sector size, minimum unit of erase. + ESP_FLASH_SECTOR = 0x1000 - def __init__(self, port = 0, baud = ESP_ROM_BAUD): - self._port = serial.Serial(port, baud) + def __init__(self, port=0, baud=ESP_ROM_BAUD): + self._port = serial.Serial(port) + self._slip_reader = slip_reader(port) + # setting baud rate in a separate step is a workaround for + # CH341 driver on some Linux versions (this opens at 9600 then + # sets), shouldn't matter for other platforms/drivers. See + # https://github.com/themadinventor/esptool/issues/44#issuecomment-107094446 + self._port.baudrate = baud - """ Read bytes from the serial port while performing SLIP unescaping """ - def read(self, length = 1): - b = '' - while len(b) < length: - c = self._port.read(1) - if c == '\xdb': - c = self._port.read(1) - if c == '\xdc': - b = b + '\xc0' - elif c == '\xdd': - b = b + '\xdb' - else: - raise Exception('Invalid SLIP escape') - else: - b = b + c - return b + """ Read a SLIP packet from the serial port """ + def read(self): + return self._slip_reader.next() """ Write bytes to the serial port while performing SLIP escaping """ def write(self, packet): - buf = '\xc0'+(packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc'))+'\xc0' + buf = '\xc0' \ + + (packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc')) \ + + '\xc0' self._port.write(buf) """ Calculate checksum of a blob, as it is defined by the ROM """ @staticmethod - def checksum(data, state = ESP_CHECKSUM_MAGIC): + def checksum(data, state=ESP_CHECKSUM_MAGIC): for b in data: state ^= ord(b) return state """ Send a request and read the response """ - def command(self, op = None, data = None, chk = 0): - if op: - # Construct and send request + def command(self, op=None, data=None, chk=0): + if op is not None: pkt = struct.pack('> 16) & 0xff) == 0: + mac0 = self.read_reg(self.ESP_OTP_MAC0) + mac1 = self.read_reg(self.ESP_OTP_MAC1) + mac3 = self.read_reg(self.ESP_OTP_MAC3) + if (mac3 != 0): + oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) + elif ((mac1 >> 16) & 0xff) == 0: oui = (0x18, 0xfe, 0x34) elif ((mac1 >> 16) & 0xff) == 1: oui = (0xac, 0xd0, 0x74) else: - raise Exception("Unknown OUI") + raise FatalError("Unknown OUI") return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ + def chip_id(self): + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & 0xffffff) << 8) + """ Read SPI flash manufacturer and device id """ def flash_id(self): self.flash_begin(0, 0) self.write_reg(0x60000240, 0x0, 0xffffffff) self.write_reg(0x60000200, 0x10000000, 0xffffffff) - flash_id = esp.read_reg(0x60000240) + flash_id = self.read_reg(0x60000240) self.flash_finish(False) return flash_id - """ Read SPI flash """ - def flash_read(self, offset, size, count = 1): - # Create a custom stub - stub = struct.pack(' 16: - raise Exception('Invalid firmware image') - - for i in xrange(segments): - (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: - raise Exception('Suspicious segment %x,%d' % (offset, size)) - self.segments.append((offset, size, f.read(size))) - - # Skip the padding. The checksum is stored in the last byte so that the - # file is a multiple of 16 bytes. - align = 15-(f.tell() % 16) - f.seek(align, 1) - - self.checksum = ord(f.read(1)) - - def add_segment(self, addr, data): + def add_segment(self, addr, data, pad_to=4): + """ Add a segment to the image, with specified address & data + (padded to a boundary of pad_to size) """ # Data should be aligned on word boundary l = len(data) - if l % 4: - data += b"\x00" * (4 - l % 4) - self.segments.append((addr, len(data), data)) + if l % pad_to: + data += b"\x00" * (pad_to - l % pad_to) + if l > 0: + self.segments.append((addr, len(data), data)) - def save(self, filename): - f = file(filename, 'wb') - f.write(struct.pack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: + raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size)) + segment_data = f.read(size) + if len(segment_data) < size: + raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data))) + segment = (offset, size, segment_data) + self.segments.append(segment) + return segment - checksum = ESPROM.ESP_CHECKSUM_MAGIC - for (offset, size, data) in self.segments: - f.write(struct.pack(' 16: + raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) + + for i in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def save(self, filename): + with open(filename, 'wb') as f: + self.write_v1_header(f, self.segments) + checksum = ESPROM.ESP_CHECKSUM_MAGIC + for segment in self.segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +class OTAFirmwareImage(BaseFirmwareImage): + """ 'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + def __init__(self, load_file=None): + super(OTAFirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack(' 16: + raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments)) + + # load all the usual segments + for _ in xrange(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + def save(self, filename): + with open(filename, 'wb') as f: + # Save first header for irom0 segment + f.write(struct.pack(' 0: + esp._port.baudrate = baud_rate + # Read the greeting. + p = esp.read() + if p != 'OHAI': + raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p)) + + def flash_write(self, addr, data, show_progress=False): + assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned' + assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned' + sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr)) + sys.stdout.flush() + self._esp.write(struct.pack(' length: + raise FatalError('Read more than expected') + p = self._esp.read() + if len(p) != 16: + raise FatalError('Expected digest, got: %s' % hexify(p)) + expected_digest = hexify(p).upper() + digest = hashlib.md5(data).hexdigest().upper() + print + if digest != expected_digest: + raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) + p = self._esp.read() + if len(p) != 1: + raise FatalError('Expected status, got: %s' % hexify(p)) + status_code = struct.unpack(', ) or a single +# argument. + +def load_ram(esp, args): + image = LoadFirmwareImage(args.filename) + + print 'RAM boot...' + for (offset, size, data) in image.segments: + print 'Downloading %d bytes at %08x...' % (size, offset), + sys.stdout.flush() + esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) + + seq = 0 + while len(data) > 0: + esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) + data = data[esp.ESP_RAM_BLOCK:] + seq += 1 + print 'done!' + + print 'All segments done, executing at %08x' % image.entrypoint + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) + + +def dump_mem(esp, args): + f = file(args.filename, 'wb') + for i in xrange(args.size / 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack('> 8) & 0xff, (flash_id >> 16) & 0xff) + + +def read_flash(esp, args): + flasher = CesantaFlasher(esp, args.baud) + t = time.time() + data = flasher.flash_read(args.address, args.size, not args.no_progress) + t = time.time() - t + print ('\rRead %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' + % (len(data), args.address, t, len(data) / t * 8 / 1000)) + file(args.filename, 'wb').write(data) + + +def _verify_flash(flasher, args, flash_params=None): + differences = False + for address, argfile in args.addr_filename: + image = argfile.read() + argfile.seek(0) # rewind in case we need it again + if address == 0 and image[0] == '\xe9' and flash_params is not None: + image = image[0:2] + flash_params + image[4:] + image_size = len(image) + print 'Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name) + # Try digest first, only read if there are differences. + digest, _ = flasher.flash_digest(address, image_size) + digest = hexify(digest).upper() + expected_digest = hashlib.md5(image).hexdigest().upper() + if digest == expected_digest: + print '-- verify OK (digest matched)' + continue + else: + differences = True + if getattr(args, 'diff', 'no') != 'yes': + print '-- verify FAILED (digest mismatch)' + continue + + flash = flasher.flash_read(address, image_size) + assert flash != image + diff = [i for i in xrange(image_size) if flash[i] != image[i]] + print '-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0]) + for d in diff: + print ' %08x %02x %02x' % (address + d, ord(flash[d]), ord(image[d])) + if differences: + raise FatalError("Verify failed.") + + +def verify_flash(esp, args, flash_params=None): + flasher = CesantaFlasher(esp) + _verify_flash(flasher, args, flash_params) + + +def version(args): + print __version__ + +# +# End of operations functions +# + + +def main(): + parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') parser.add_argument( - '--port', '-p', - help = 'Serial port device', - default = '/dev/ttyUSB0') + '--port', '-p', + help='Serial port device', + default=os.environ.get('ESPTOOL_PORT', '/dev/ttyUSB0')) parser.add_argument( - '--baud', '-b', - help = 'Serial port baud rate', - type = arg_auto_int, - default = ESPROM.ESP_ROM_BAUD) + '--baud', '-b', + help='Serial port baud rate used when flashing/reading', + type=arg_auto_int, + default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD)) subparsers = parser.add_subparsers( - dest = 'operation', - help = 'Run esptool {command} -h for additional help') + dest='operation', + help='Run esptool {command} -h for additional help') parser_load_ram = subparsers.add_parser( - 'load_ram', - help = 'Download an image to RAM and execute') - parser_load_ram.add_argument('filename', help = 'Firmware image') + 'load_ram', + help='Download an image to RAM and execute') + parser_load_ram.add_argument('filename', help='Firmware image') parser_dump_mem = subparsers.add_parser( - 'dump_mem', - help = 'Dump arbitrary memory to disk') - parser_dump_mem.add_argument('address', help = 'Base address', type = arg_auto_int) - parser_dump_mem.add_argument('size', help = 'Size of region to dump', type = arg_auto_int) - parser_dump_mem.add_argument('filename', help = 'Name of binary dump') + 'dump_mem', + help='Dump arbitrary memory to disk') + parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) + parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_dump_mem.add_argument('filename', help='Name of binary dump') parser_read_mem = subparsers.add_parser( - 'read_mem', - help = 'Read arbitrary memory location') - parser_read_mem.add_argument('address', help = 'Address to read', type = arg_auto_int) + 'read_mem', + help='Read arbitrary memory location') + parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) parser_write_mem = subparsers.add_parser( - 'write_mem', - help = 'Read-modify-write to arbitrary memory location') - parser_write_mem.add_argument('address', help = 'Address to write', type = arg_auto_int) - parser_write_mem.add_argument('value', help = 'Value', type = arg_auto_int) - parser_write_mem.add_argument('mask', help = 'Mask of bits to write', type = arg_auto_int) + 'write_mem', + help='Read-modify-write to arbitrary memory location') + parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) + parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) + parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int) + + def add_spi_flash_subparsers(parent): + """ Add common parser arguments for SPI flash properties """ + parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', + choices=['40m', '26m', '20m', '80m'], + default=os.environ.get('ESPTOOL_FF', '40m')) + parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', + choices=['qio', 'qout', 'dio', 'dout'], + default=os.environ.get('ESPTOOL_FM', 'qio')) + parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=str.lower, + choices=['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2', '64m', '128m'], + default=os.environ.get('ESPTOOL_FS', '4m')) parser_write_flash = subparsers.add_parser( - 'write_flash', - help = 'Write a binary blob to flash') - parser_write_flash.add_argument('addr_filename', nargs = '+', help = 'Address and binary file to write there, separated by space') - parser_write_flash.add_argument('--flash_freq', '-ff', help = 'SPI Flash frequency', - choices = ['40m', '26m', '20m', '80m'], default = '40m') - parser_write_flash.add_argument('--flash_mode', '-fm', help = 'SPI Flash mode', - choices = ['qio', 'qout', 'dio', 'dout'], default = 'qio') - parser_write_flash.add_argument('--flash_size', '-fs', help = 'SPI Flash size in Mbit', - choices = ['4m', '2m', '8m', '16m', '32m'], default = '4m') + 'write_flash', + help='Write a binary blob to flash') + parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', + action=AddrFilenamePairAction) + add_spi_flash_subparsers(parser_write_flash) + parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") + parser_write_flash.add_argument('--verify', help='Verify just-written data (only necessary if very cautious, data is already CRCed', action='store_true') - parser_run = subparsers.add_parser( - 'run', - help = 'Run application code in flash') + subparsers.add_parser( + 'run', + help='Run application code in flash') parser_image_info = subparsers.add_parser( - 'image_info', - help = 'Dump headers from an application image') - parser_image_info.add_argument('filename', help = 'Image file to parse') + 'image_info', + help='Dump headers from an application image') + parser_image_info.add_argument('filename', help='Image file to parse') parser_make_image = subparsers.add_parser( - 'make_image', - help = 'Create an application image from binary files') - parser_make_image.add_argument('output', help = 'Output image file') - parser_make_image.add_argument('--segfile', '-f', action = 'append', help = 'Segment input file') - parser_make_image.add_argument('--segaddr', '-a', action = 'append', help = 'Segment base address', type = arg_auto_int) - parser_make_image.add_argument('--entrypoint', '-e', help = 'Address of entry point', type = arg_auto_int, default = 0) + 'make_image', + help='Create an application image from binary files') + parser_make_image.add_argument('output', help='Output image file') + parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') + parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) + parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) parser_elf2image = subparsers.add_parser( - 'elf2image', - help = 'Create an application image from ELF file') - parser_elf2image.add_argument('input', help = 'Input ELF file') - parser_elf2image.add_argument('--output', '-o', help = 'Output filename prefix', type = str) - parser_elf2image.add_argument('--flash_freq', '-ff', help = 'SPI Flash frequency', - choices = ['40m', '26m', '20m', '80m'], default = '40m') - parser_elf2image.add_argument('--flash_mode', '-fm', help = 'SPI Flash mode', - choices = ['qio', 'qout', 'dio', 'dout'], default = 'qio') - parser_elf2image.add_argument('--flash_size', '-fs', help = 'SPI Flash size in Mbit', - choices = ['4m', '2m', '8m', '16m', '32m'], default = '4m') + 'elf2image', + help='Create an application image from ELF file') + parser_elf2image.add_argument('input', help='Input ELF file') + parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) + parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1','2'], default='1') + add_spi_flash_subparsers(parser_elf2image) - parser_read_mac = subparsers.add_parser( - 'read_mac', - help = 'Read MAC address from OTP ROM') + subparsers.add_parser( + 'read_mac', + help='Read MAC address from OTP ROM') - parser_flash_id = subparsers.add_parser( - 'flash_id', - help = 'Read SPI flash manufacturer and device ID') + subparsers.add_parser( + 'chip_id', + help='Read Chip ID from OTP ROM') + + subparsers.add_parser( + 'flash_id', + help='Read SPI flash manufacturer and device ID') parser_read_flash = subparsers.add_parser( - 'read_flash', - help = 'Read SPI flash content') - parser_read_flash.add_argument('address', help = 'Start address', type = arg_auto_int) - parser_read_flash.add_argument('size', help = 'Size of region to dump', type = arg_auto_int) - parser_read_flash.add_argument('filename', help = 'Name of binary dump') + 'read_flash', + help='Read SPI flash content') + parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) + parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) + parser_read_flash.add_argument('filename', help='Name of binary dump') + parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") - parser_erase_flash = subparsers.add_parser( - 'erase_flash', - help = 'Perform Chip Erase on SPI flash') + parser_verify_flash = subparsers.add_parser( + 'verify_flash', + help='Verify a binary blob against flash') + parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', + action=AddrFilenamePairAction) + parser_verify_flash.add_argument('--diff', '-d', help='Show differences', + choices=['no', 'yes'], default='no') + + subparsers.add_parser( + 'erase_flash', + help='Perform Chip Erase on SPI flash') + + subparsers.add_parser( + 'version', help='Print esptool version') + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation args = parser.parse_args() - # Create the ESPROM connection object, if needed - esp = None - if args.operation not in ('image_info','make_image','elf2image'): - esp = ESPROM(args.port, args.baud) + print 'esptool.py v%s' % __version__ + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPROM class. + + operation_func = globals()[args.operation] + operation_args,_,_,_ = inspect.getargspec(operation_func) + if operation_args[0] == 'esp': # operation function takes an ESPROM connection object + initial_baud = min(ESPROM.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate + esp = ESPROM(args.port, initial_baud) esp.connect() + operation_func(esp, args) + else: + operation_func(args) - # Do the actual work. Should probably be split into separate functions. - if args.operation == 'load_ram': - image = ESPFirmwareImage(args.filename) - print 'RAM boot...' - for (offset, size, data) in image.segments: - print 'Downloading %d bytes at %08x...' % (size, offset), - sys.stdout.flush() - esp.mem_begin(size, math.ceil(size / float(esp.ESP_RAM_BLOCK)), esp.ESP_RAM_BLOCK, offset) +class AddrFilenamePairAction(argparse.Action): + """ Custom parser class for the address/filename pairs passed as arguments """ + def __init__(self, option_strings, dest, nargs='+', **kwargs): + super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) - seq = 0 - while len(data) > 0: - esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) - data = data[esp.ESP_RAM_BLOCK:] - seq += 1 - print 'done!' + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0,len(values),2): + try: + address = int(values[i],0) + except ValueError as e: + raise argparse.ArgumentError(self,'Address "%s" must be a number' % values[i]) + try: + argfile = open(values[i + 1], 'rb') + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError(self,'Must be pairs of an address and the binary filename to write there') + pairs.append((address, argfile)) + setattr(namespace, self.dest, pairs) - print 'All segments done, executing at %08x' % image.entrypoint - esp.mem_finish(image.entrypoint) +# This is "wrapped" stub_flasher.c, to be loaded using run_stub. +_CESANTA_FLASHER_STUB = """\ +{"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\ +81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\ +1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\ +0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\ +C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\ +0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\ +2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\ +C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\ +0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\ +00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\ +6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\ +B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\ +D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\ +C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\ +030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\ +C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\ +01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\ +2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\ +042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\ +019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\ +71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\ +0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\ +21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\ +C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\ +3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\ +FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\ +853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\ +1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\ +5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\ +0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\ +B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\ +10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\ +22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\ +D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\ +FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\ +3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\ +C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\ +C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\ +1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\ +32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\ +10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\ +10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\ +01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\ +011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\ +000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\ +C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\ +21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\ +32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\ +31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\ +01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\ +FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\ +01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\ +C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\ +entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\ +401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\ +43520} +""" - elif args.operation == 'read_mem': - print '0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address)) - - elif args.operation == 'write_mem': - esp.write_reg(args.address, args.value, args.mask, 0) - print 'Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address) - - elif args.operation == 'dump_mem': - f = file(args.filename, 'wb') - for i in xrange(args.size/4): - d = esp.read_reg(args.address+(i*4)) - f.write(struct.pack(' 0: - print '\rWriting at 0x%08x... (%d %%)' % (address + seq*esp.ESP_FLASH_BLOCK, 100*(seq+1)/blocks), - sys.stdout.flush() - block = image[0:esp.ESP_FLASH_BLOCK] - # Fix sflash config data - if address == 0 and seq == 0 and block[0] == '\xe9': - block = block[0:2] + flash_info + block[4:] - # Pad the last block - block = block + '\xff' * (esp.ESP_FLASH_BLOCK-len(block)) - esp.flash_block(block, seq) - image = image[esp.ESP_FLASH_BLOCK:] - seq += 1 - print - print '\nLeaving...' - esp.flash_finish(False) - - elif args.operation == 'run': - esp.run() - - elif args.operation == 'image_info': - image = ESPFirmwareImage(args.filename) - print ('Entry point: %08x' % image.entrypoint) if image.entrypoint != 0 else 'Entry point not set' - print '%d segments' % len(image.segments) - print - checksum = ESPROM.ESP_CHECKSUM_MAGIC - for (idx, (offset, size, data)) in enumerate(image.segments): - print 'Segment %d: %5d bytes at %08x' % (idx+1, size, offset) - checksum = ESPROM.checksum(data, checksum) - print - print 'Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!') - - elif args.operation == 'make_image': - image = ESPFirmwareImage() - if len(args.segfile) == 0: - raise Exception('No segments specified') - if len(args.segfile) != len(args.segaddr): - raise Exception('Number of specified files does not match number of specified addresses') - for (seg, addr) in zip(args.segfile, args.segaddr): - data = file(seg, 'rb').read() - image.add_segment(addr, data) - image.entrypoint = args.entrypoint - image.save(args.output) - - elif args.operation == 'elf2image': - if args.output is None: - args.output = args.input + '-' - e = ELFFile(args.input) - image = ESPFirmwareImage() - image.entrypoint = e.get_symbol_addr("call_user_start") - for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): - data = e.load_section(section) - image.add_segment(e.get_symbol_addr(start), data) - - image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] - image.flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40}[args.flash_size] - image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] - - image.save(args.output + "0x00000.bin") - data = e.load_section(".irom0.text") - off = e.get_symbol_addr("_irom0_text_start") - 0x40200000 - assert off >= 0 - f = open(args.output + "0x%05x.bin" % off, "wb") - f.write(data) - f.close() - - elif args.operation == 'read_mac': - mac = esp.read_mac() - print 'MAC: %s' % ':'.join(map(lambda x: '%02x'%x, mac)) - - elif args.operation == 'flash_id': - flash_id = esp.flash_id() - print 'Manufacturer: %02x' % (flash_id & 0xff) - print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff) - - elif args.operation == 'read_flash': - print 'Please wait...' - file(args.filename, 'wb').write(esp.flash_read(args.address, 1024, int(math.ceil(args.size / 1024.)))[:args.size]) - - elif args.operation == 'erase_flash': - esp.flash_erase() +if __name__ == '__main__': + try: + main() + except FatalError as e: + print '\nA fatal error occurred: %s' % e + sys.exit(2) diff --git a/os/flash.sh b/os/flash.sh index c303c23..b79f680 100755 --- a/os/flash.sh +++ b/os/flash.sh @@ -32,5 +32,6 @@ if [ $? -ne 0 ]; then exit 1 fi -#./esptool.py --port /dev/$DEVICE $BAUD write_flash 0x00000 nodemcu-master-enduser_setup,file,gpio,net,node,rtcfifo,rtcmem,rtctime,sntp,spi,tmr,uart,wifi,ws2812-integer.bin -./esptool.py --port /dev/ttyUSB0 $BAUD write_flash 0x00000 0x00000.bin 0x10000 0x10000.bin +./esptool.py --port /dev/$DEVICE $BAUD write_flash -fm dio 0x00000 nodemcu2.bin +# 0x3fc000 esp_init_data_default_v08.bin 0x07e000 blank.bin 0x3fe000 blank.bin +#./esptool.py --port /dev/$DEVICE $BAUD write_flash 0x00000 0x00000.bin 0x10000 0x10000.bin diff --git a/os/nodemcu-master-bit,enduser_setup,file,gpio,node,rtcfifo,rtcmem,rtctime,sntp,spi,tmr,uart,wifi,ws2812-integer.bin b/os/nodemcu-master-bit,enduser_setup,file,gpio,node,rtcfifo,rtcmem,rtctime,sntp,spi,tmr,uart,wifi,ws2812-integer.bin deleted file mode 100644 index c2d7814..0000000 Binary files a/os/nodemcu-master-bit,enduser_setup,file,gpio,node,rtcfifo,rtcmem,rtctime,sntp,spi,tmr,uart,wifi,ws2812-integer.bin and /dev/null differ diff --git a/os/nodemcu2.bin b/os/nodemcu2.bin new file mode 100644 index 0000000..1644bdf Binary files /dev/null and b/os/nodemcu2.bin differ diff --git a/tools/initialFlash.sh b/tools/initialFlash.sh index ebcf25c..b54d309 100755 --- a/tools/initialFlash.sh +++ b/tools/initialFlash.sh @@ -18,10 +18,11 @@ if [ $# -ne 1 ]; then exit 1 fi -FILES="displayword.lua main.lua timecore.lua webpage.lua webserver.lua wordclock.lua init.lua" +FILES="displayword.lua main.lua timecore.lua webpage.html webserver.lua wordclock.lua init.lua" + # Format filesystem first echo "Format the complete ESP" -$LUATOOL -p $DEVICE -w +$LUATOOL -p $DEVICE -w -b 115200 if [ $? -ne 0 ]; then echo "STOOOOP" exit 1 @@ -36,7 +37,7 @@ for f in $FILES; do exit 1 fi echo "------------- $f ------------" - $LUATOOL -p $DEVICE -f $f -t $f + $LUATOOL -p $DEVICE -f $f -b 115200 -t $f if [ $? -ne 0 ]; then echo "STOOOOP" exit 1 @@ -44,6 +45,6 @@ for f in $FILES; do done echo "Reboot the ESP" -$LUATOOL -p $DEVICE -r +$LUATOOL -p $DEVICE -r -b 115200 exit 0 diff --git a/webpage.html b/webpage.html index 7e1dbdd..fad40f7 100644 --- a/webpage.html +++ b/webpage.html @@ -55,6 +55,8 @@ Please note that all settings are mandatory

4. Minute ColorFourth minute after Three quaterDreiviertel Joa/nei +Invert lines 4-6invert +
diff --git a/webserver.lua b/webserver.lua index c61ea10..8568412 100644 --- a/webserver.lua +++ b/webserver.lua @@ -165,7 +165,9 @@ function startWebServer() file.remove(configFile .. ".new") sec, _ = rtctime.get() file.open(configFile.. ".new", "w+") - file.write("-- Config\n" .. "wifi.sta.config(\"" .. _POST.ssid .. "\",[[" .. _POST.password .. "]])\n" .. "sntpserverhostname=\"" .. _POST.sntpserver .. "\"\n" .. "timezoneoffset=\"" .. _POST.timezoneoffset .. "\"\n") + file.write("-- Config\n" .. "station_cfg={}\nstation_cfg.ssid=\"" .. _POST.ssid .. "\"\nstation_cfg.pwd=\"" .. _POST.password .. "\"\nstation_cfg.save=false\nwifi.sta.config(station_cfg)\n") + file.write("sntpserverhostname=\"" .. _POST.sntpserver .. "\"\n" .. "timezoneoffset=\"" .. _POST.timezoneoffset .. "\"\n".. "inv46=\"" .. tostring(_POST.inv46) .. "\"\n") + if ( _POST.fcolor ~= nil) then -- color=string.char(_POST.green, _POST.red, _POST.blue) print ("Got fcolor: " .. _POST.fcolor)