Skip to content

Commit 46dc159

Browse files
authored
add Shelly S1G3 support
1 parent f08ffbb commit 46dc159

File tree

5 files changed

+857
-0
lines changed

5 files changed

+857
-0
lines changed
34.8 KB
Binary file not shown.
Binary file not shown.

raw/esp32c3/Shelly_S1G3/bootloader.be

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#
2+
# Flash bootloader from URL or filesystem
3+
#
4+
5+
class bootloader
6+
static var _addr = [0x1000, 0x0000] # possible addresses for bootloader
7+
static var _sign = bytes('E9') # signature of the bootloader
8+
static var _addr_high = 0x8000 # address of next partition after bootloader
9+
10+
# get the bootloader address, 0x1000 for Xtensa based, 0x0000 for RISC-V based (but might have some exception)
11+
# we prefer to probed what's already in place rather than manage a hardcoded list of architectures
12+
# (there is a low risk of collision if the address is 0x0000 and offset 0x1000 is actually E9)
13+
def get_bootloader_address()
14+
import flash
15+
# let's see where we find 0xE9, trying first 0x1000 then 0x0000
16+
for addr : self._addr
17+
if flash.read(addr, size(self._sign)) == self._sign
18+
return addr
19+
end
20+
end
21+
return nil
22+
end
23+
24+
#
25+
# download from URL and store to `bootloader.bin`
26+
#
27+
def download(url)
28+
# address to flash the bootloader
29+
var addr = self.get_bootloader_address()
30+
if addr == nil raise "internal_error", "can't find address for bootloader" end
31+
32+
var cl = webclient()
33+
cl.begin(url)
34+
var r = cl.GET()
35+
if r != 200 raise "network_error", "GET returned "+str(r) end
36+
var bl_size = cl.get_size()
37+
if bl_size <= 8291 raise "internal_error", "wrong bootloader size "+str(bl_size) end
38+
if bl_size > (0x8000 - addr) raise "internal_error", "bootloader is too large "+str(bl_size / 1024)+"kB" end
39+
40+
cl.write_file("bootloader.bin")
41+
cl.close()
42+
end
43+
44+
# returns true if ok
45+
def flash(url)
46+
var fname = "bootloader.bin" # default local name
47+
if url != nil
48+
if url[0..3] == "http" # if starts with 'http' download
49+
self.download(url)
50+
else
51+
fname = url # else get from file system
52+
end
53+
end
54+
# address to flash the bootloader
55+
var addr = self.get_bootloader_address()
56+
if addr == nil tasmota.log("OTA: can't find address for bootloader", 2) return false end
57+
58+
var bl = open(fname, "r")
59+
if bl.readbytes(size(self._sign)) != self._sign
60+
tasmota.log("OTA: file does not contain a bootloader signature", 2)
61+
return false
62+
end
63+
bl.seek(0) # reset to start of file
64+
65+
var bl_size = bl.size()
66+
if bl_size <= 8291 tasmota.log("OTA: wrong bootloader size "+str(bl_size), 2) return false end
67+
if bl_size > (0x8000 - addr) tasmota.log("OTA: bootloader is too large "+str(bl_size / 1024)+"kB", 2) return false end
68+
69+
tasmota.log("OTA: Flashing bootloader", 2)
70+
# from now on there is no turning back, any failure means a bricked device
71+
import flash
72+
# read current value for bytes 2/3
73+
var cur_config = flash.read(addr, 4)
74+
75+
flash.erase(addr, self._addr_high - addr) # erase the bootloader
76+
var buf = bl.readbytes(0x1000) # read by chunks of 4kb
77+
# put back signature
78+
buf[2] = cur_config[2]
79+
buf[3] = cur_config[3]
80+
while size(buf) > 0
81+
flash.write(addr, buf, true) # set flag to no-erase since we already erased it
82+
addr += size(buf)
83+
buf = bl.readbytes(0x1000) # read next chunk
84+
end
85+
bl.close()
86+
tasmota.log("OTA: Booloader flashed, please restart", 2)
87+
return true
88+
end
89+
end
90+
91+
return bootloader
92+
93+
#-
94+
95+
### FLASH
96+
import bootloader
97+
bootloader().flash('https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/sdk/esp32/bin/bootloader_dio_40m.bin')
98+
99+
#bootloader().flash('https://raw.githubusercontent.com/espressif/arduino-esp32/master/tools/sdk/esp32/bin/bootloader_dout_40m.bin')
100+
101+
### FLASH from local file
102+
bootloader().flash("bootloader-tasmota-c3.bin")
103+
104+
#### debug only
105+
bl = bootloader()
106+
print(format("0x%04X", bl.get_bootloader_address()))
107+
108+
-#
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# migration script for Shelly
2+
3+
# simple function to copy from autoconfig archive to filesystem
4+
# return true if ok
5+
def cp(from, to)
6+
import path
7+
if to == nil to = from end # to is optional
8+
if !path.exists(to)
9+
try
10+
# tasmota.log("f_in="+tasmota.wd + from)
11+
var f_in = open(tasmota.wd + from)
12+
var f_out = open(to, "w")
13+
var f_content = f_in.readbytes(0x2000) # read by chunks of 8kb
14+
while size(f_content) > 0
15+
f_out.write(f_content)
16+
f_content = f_in.readbytes(0x2000) # read next chunk
17+
end
18+
f_in.close()
19+
f_out.close()
20+
except .. as e,m
21+
tasmota.log("OTA: Couldn't copy "+to+" "+e+" "+m,2)
22+
return false
23+
end
24+
return true
25+
end
26+
return true
27+
end
28+
29+
def copy_ota(from_addr, to_addr, sz)
30+
import flash
31+
import string
32+
var size_left = sz
33+
var offset = 0
34+
35+
tasmota.log(string.format("UPL: Copy flash from 0x%06X to 0x%06X (size: %ikB)", from_addr, to_addr, sz / 1024), 2)
36+
while size_left > 0
37+
var b = flash.read(from_addr + offset, 4096)
38+
flash.erase(to_addr + offset, 4096)
39+
flash.write(to_addr + offset, b, true)
40+
size_left -= 4096
41+
offset += 4096
42+
if ((offset-4096) / 102400) < (offset / 102400)
43+
tasmota.log(string.format("UPL: Progress %ikB", offset/1024), 3)
44+
end
45+
end
46+
tasmota.log("UPL: done", 2)
47+
end
48+
49+
# make some room if there are some leftovers from shelly
50+
import path
51+
path.remove("index.html.gz")
52+
53+
# copy some files from autoconf to filesystem
54+
var ok
55+
ok = cp("bootloader-tasmota-c3.bin")
56+
ok = cp("Partition_Wizard.tapp")
57+
58+
# use an alternative to partition_core that can read Shelly's otadata
59+
tasmota.log("OTA: loading "+tasmota.wd + "partition_core_shelly.be", 2)
60+
load(tasmota.wd + "partition_core_shelly.be")
61+
62+
# load bootloader flasher
63+
tasmota.log("OTA: loading "+tasmota.wd + "bootloader.be", 2)
64+
load(tasmota.wd + "bootloader.be")
65+
66+
67+
# all good
68+
if ok
69+
# do some basic check that the bootloader is not already in place
70+
import flash
71+
if flash.read(0x1000, 4) == bytes('CD3F6395')
72+
tasmota.log("OTA: bootloader already in place, not flashing it")
73+
else
74+
ok = global.bootloader().flash("bootloader-tasmota-c3.bin")
75+
end
76+
if ok
77+
var p = global.partition_core_shelly.Partition()
78+
var app0 = p.get_ota_slot(0)
79+
var app1 = p.get_ota_slot(1)
80+
var app0_size = app0.get_image_size()
81+
var app1_size = app1.get_image_size()
82+
# check if we get some Tasmota signature in slot 1
83+
if (flash.read(p.get_ota_slot(1).start + 16, 4) == bytes("00FFFF00"))
84+
copy_ota(app1.start, app0.start, app1_size)
85+
elif (flash.read(p.get_ota_slot(0).start + 16, 4) == bytes("00FFFF00"))
86+
copy_ota(app0.start, app1.start, app0_size)
87+
end
88+
var otadata_offset = p.otadata.offset
89+
flash.erase(otadata_offset, 0x2000)
90+
tasmota.log("OTA: Shelly migration successful", 2)
91+
end
92+
end
93+
94+
# dump logs to file
95+
var lr = tasmota_log_reader()
96+
var f_logs = open("migration_logs.txt", "w")
97+
var logs = lr.get_log(2)
98+
while logs != nil
99+
f_logs.write(logs)
100+
logs = lr.get_log(2)
101+
end
102+
f_logs.close()
103+
104+
# Done

0 commit comments

Comments
 (0)