Skip to content

Commit

Permalink
add Shelly S1G3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason2866 authored Nov 9, 2024
1 parent f08ffbb commit 46dc159
Show file tree
Hide file tree
Showing 5 changed files with 857 additions and 0 deletions.
Binary file added raw/esp32c3/Shelly_S1G3/Partition_Wizard.tapp
Binary file not shown.
Binary file added raw/esp32c3/Shelly_S1G3/bootloader-tasmota-c3.bin
Binary file not shown.
108 changes: 108 additions & 0 deletions raw/esp32c3/Shelly_S1G3/bootloader.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#
# Flash bootloader from URL or filesystem
#

class bootloader
static var _addr = [0x1000, 0x0000] # possible addresses for bootloader
static var _sign = bytes('E9') # signature of the bootloader
static var _addr_high = 0x8000 # address of next partition after bootloader

# get the bootloader address, 0x1000 for Xtensa based, 0x0000 for RISC-V based (but might have some exception)
# we prefer to probed what's already in place rather than manage a hardcoded list of architectures
# (there is a low risk of collision if the address is 0x0000 and offset 0x1000 is actually E9)
def get_bootloader_address()
import flash
# let's see where we find 0xE9, trying first 0x1000 then 0x0000
for addr : self._addr
if flash.read(addr, size(self._sign)) == self._sign
return addr
end
end
return nil
end

#
# download from URL and store to `bootloader.bin`
#
def download(url)
# address to flash the bootloader
var addr = self.get_bootloader_address()
if addr == nil raise "internal_error", "can't find address for bootloader" end

var cl = webclient()
cl.begin(url)
var r = cl.GET()
if r != 200 raise "network_error", "GET returned "+str(r) end
var bl_size = cl.get_size()
if bl_size <= 8291 raise "internal_error", "wrong bootloader size "+str(bl_size) end
if bl_size > (0x8000 - addr) raise "internal_error", "bootloader is too large "+str(bl_size / 1024)+"kB" end

cl.write_file("bootloader.bin")
cl.close()
end

# returns true if ok
def flash(url)
var fname = "bootloader.bin" # default local name
if url != nil
if url[0..3] == "http" # if starts with 'http' download
self.download(url)
else
fname = url # else get from file system
end
end
# address to flash the bootloader
var addr = self.get_bootloader_address()
if addr == nil tasmota.log("OTA: can't find address for bootloader", 2) return false end

var bl = open(fname, "r")
if bl.readbytes(size(self._sign)) != self._sign
tasmota.log("OTA: file does not contain a bootloader signature", 2)
return false
end
bl.seek(0) # reset to start of file

var bl_size = bl.size()
if bl_size <= 8291 tasmota.log("OTA: wrong bootloader size "+str(bl_size), 2) return false end
if bl_size > (0x8000 - addr) tasmota.log("OTA: bootloader is too large "+str(bl_size / 1024)+"kB", 2) return false end

tasmota.log("OTA: Flashing bootloader", 2)
# from now on there is no turning back, any failure means a bricked device
import flash
# read current value for bytes 2/3
var cur_config = flash.read(addr, 4)

flash.erase(addr, self._addr_high - addr) # erase the bootloader
var buf = bl.readbytes(0x1000) # read by chunks of 4kb
# put back signature
buf[2] = cur_config[2]
buf[3] = cur_config[3]
while size(buf) > 0
flash.write(addr, buf, true) # set flag to no-erase since we already erased it
addr += size(buf)
buf = bl.readbytes(0x1000) # read next chunk
end
bl.close()
tasmota.log("OTA: Booloader flashed, please restart", 2)
return true
end
end

return bootloader

#-
### FLASH
import bootloader
bootloader().flash('https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/sdk/esp32/bin/bootloader_dio_40m.bin')
#bootloader().flash('https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/sdk/esp32/bin/bootloader_dout_40m.bin')
### FLASH from local file
bootloader().flash("bootloader-tasmota-c3.bin")
#### debug only
bl = bootloader()
print(format("0x%04X", bl.get_bootloader_address()))
-#
104 changes: 104 additions & 0 deletions raw/esp32c3/Shelly_S1G3/migrate_shelly.be
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# migration script for Shelly

# simple function to copy from autoconfig archive to filesystem
# return true if ok
def cp(from, to)
import path
if to == nil to = from end # to is optional
if !path.exists(to)
try
# tasmota.log("f_in="+tasmota.wd + from)
var f_in = open(tasmota.wd + from)
var f_out = open(to, "w")
var f_content = f_in.readbytes(0x2000) # read by chunks of 8kb
while size(f_content) > 0
f_out.write(f_content)
f_content = f_in.readbytes(0x2000) # read next chunk
end
f_in.close()
f_out.close()
except .. as e,m
tasmota.log("OTA: Couldn't copy "+to+" "+e+" "+m,2)
return false
end
return true
end
return true
end

def copy_ota(from_addr, to_addr, sz)
import flash
import string
var size_left = sz
var offset = 0

tasmota.log(string.format("UPL: Copy flash from 0x%06X to 0x%06X (size: %ikB)", from_addr, to_addr, sz / 1024), 2)
while size_left > 0
var b = flash.read(from_addr + offset, 4096)
flash.erase(to_addr + offset, 4096)
flash.write(to_addr + offset, b, true)
size_left -= 4096
offset += 4096
if ((offset-4096) / 102400) < (offset / 102400)
tasmota.log(string.format("UPL: Progress %ikB", offset/1024), 3)
end
end
tasmota.log("UPL: done", 2)
end

# make some room if there are some leftovers from shelly
import path
path.remove("index.html.gz")

# copy some files from autoconf to filesystem
var ok
ok = cp("bootloader-tasmota-c3.bin")
ok = cp("Partition_Wizard.tapp")

# use an alternative to partition_core that can read Shelly's otadata
tasmota.log("OTA: loading "+tasmota.wd + "partition_core_shelly.be", 2)
load(tasmota.wd + "partition_core_shelly.be")

# load bootloader flasher
tasmota.log("OTA: loading "+tasmota.wd + "bootloader.be", 2)
load(tasmota.wd + "bootloader.be")


# all good
if ok
# do some basic check that the bootloader is not already in place
import flash
if flash.read(0x1000, 4) == bytes('CD3F6395')
tasmota.log("OTA: bootloader already in place, not flashing it")
else
ok = global.bootloader().flash("bootloader-tasmota-c3.bin")
end
if ok
var p = global.partition_core_shelly.Partition()
var app0 = p.get_ota_slot(0)
var app1 = p.get_ota_slot(1)
var app0_size = app0.get_image_size()
var app1_size = app1.get_image_size()
# check if we get some Tasmota signature in slot 1
if (flash.read(p.get_ota_slot(1).start + 16, 4) == bytes("00FFFF00"))
copy_ota(app1.start, app0.start, app1_size)
elif (flash.read(p.get_ota_slot(0).start + 16, 4) == bytes("00FFFF00"))
copy_ota(app0.start, app1.start, app0_size)
end
var otadata_offset = p.otadata.offset
flash.erase(otadata_offset, 0x2000)
tasmota.log("OTA: Shelly migration successful", 2)
end
end

# dump logs to file
var lr = tasmota_log_reader()
var f_logs = open("migration_logs.txt", "w")
var logs = lr.get_log(2)
while logs != nil
f_logs.write(logs)
logs = lr.get_log(2)
end
f_logs.close()

# Done
Loading

0 comments on commit 46dc159

Please sign in to comment.