-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboot.py
455 lines (351 loc) · 14.4 KB
/
boot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
from utime import ticks_ms
start_time = ticks_ms() # note t=time() is in seconds. to measure execution time
print('\n\nboot starting\n\n')
# vs code will infer tab size from from file type
# custo File → Preferences → User Settings tab size and detect
import gc
from machine import deepsleep, idle, RTC, reset_cause, DEEPSLEEP_RESET, DEEPSLEEP
from utime import sleep_ms, sleep, sleep_us, localtime, mktime
from machine import Pin
import framebuf
# check if the device woke from a deep sleep
if reset_cause() == DEEPSLEEP_RESET:
print('woke from a deep sleep')
else:
print('fresh boot')
r = RTC()
mem = r.memory() # content survives deep sleep
print('RTC memory contents (bytes): ', mem) # bytes
# initialize and increment RTC memory. digit as string
if mem == b'':
print('RTC memory empty. initializing with "0" ..')
mem = "0"
r.memory(mem)
mem_int = int(mem)
# avoid gosts
if mem_int == 9:
avoid_ghost = True # could be used to do some housekeeping on the display every x deep sleep
mem_int = 1
print('will fill buffer with 1 = white')
else:
avoid_ghost = False
mem_int = mem_int + 1
print('no need to fill with white yet')
mem = str(mem_int)
r.memory(mem)
print('writing to RTC memory for next boot: str, int ', mem, mem_int)
# power led removed with cuter to save power during deep sleep
# on board red led is GPIO 2
# to signal we are running. will be set to off just before going to deep sleep
led = Pin(2, Pin.OUT, value=0)
led.on()
"""
framebuffer:
This module provides a general frame buffer which can be used to create bitmap
images, which can then be sent to a display.
http://docs.micropython.org/en/latest/library/framebuf.html
buffer is an object with a buffer protocol which must be large enough to contain every pixel
defined by the width, height and format of the FrameBuffer.
"""
################################################
# create framebuf from local PBM file in ESP32 flash memory
################################################
def frame_local():
print('get PBM from flash memory')
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("mem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
pbm_file = 'nyt_today.pbm'
print('\nread portable bitmap file')
fp = open(pbm_file, 'rb')
print('type: ', fp.readline()) # P4
dim = fp.readline()
w = int(dim.split()[0])
h = int(dim.split()[1])
print('portable bitmap file: w %d, h %d' %(w,h))
size = w*h//8
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("mem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
#The bytearray type is a mutable sequence of integers in the range 0 <= x < 256
buf = bytearray(size) # [0] class int
print('buf bytearray allocated. object with a buffer protocol ' , len(buf), type(buf))
for i in range(size):
x = fp.read(1)
x = int.from_bytes(x,'little')
buf[i] = x
fp.close()
gc.collect()
print('delta free: ', free - gc.mem_free())
# buf need to be bytearray, not bytes TypeError: object with buffer protocol required
fb = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB)
print('framebuffer created' , type(fb))
return (fb, buf)
#########################################################
# create framebuf from web server's PBM file
#########################################################
def frame_remote():
print('create frame buffer from webserver pbm file')
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("mem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
# use lighttpd to serve static files
s = "http://192.168.1.206:81/epaper/nyt_today.pbm"
print('\ngetting pbm file from my web server', s)
# use urequest from https://github.com/pfalcon/pycopy-lib. else redirect not implemented error , at least on nginx/OMV5
import urequests
response = urequests.get(s) # response is object
"""
requests status code: 200
request reason: b'OK'
"""
print('requests status code: ', response.status_code)
print('request reason: ', response.reason)
if response.status_code != 200:
print('request get FAILED, sleeping for 1 hour')
sleeptime_msec = 1*60*60*1000 # one hour
deepsleep(sleeptime_msec)
#print(response.text) # actual content of http request if text, json
#parsed = response.json()) # dictionary
buf = response.content # response in bytes
print('request content: ', type(buf), len(buf)) #content <class 'bytes'> 48000
response.close()
size = len(buf) # 800 * 480 // 8
# buf is bytes, and framebuff needs bytearray , otherwize TypeError: object with buffer protocol required
# not enough ram to have both
# workaround: save buf as temp file, delete bytes buf to reclaim memory
# create bytearray and read temp file one by one to avoid creating another big buffer
print('create temp.pbm file with buf bytes content')
with open('temp.pbm', 'wb') as fp:
fp.write(buf)
# reclaim buf bytes memory
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("mem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
del buf
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("mem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
buf = bytearray(size) # elements are of class int
print('bytearray allocated. ie an object with a buffer protocol ', len(buf), type(buf))
print('read temp.pbm file')
with open('temp.pbm', 'rb') as fp:
for i in range(size):
x = fp.read(1)
x = int.from_bytes(x, 'little')
buf[i] = x
# buf need to be bytearray, not bytes. TypeError: object with buffer protocol required
fb = framebuf.FrameBuffer(buf, 800, 640, framebuf.MONO_HLSB)
print('framebuffer created', type(fb)) # TypeError: object of type 'FrameBuffer' has no len()
return(fb, buf)
############################################################
# boot
############################################################
import os
import sys
from esp import flash_size
from esp32 import raw_temperature
import ntptime
from machine import Pin, SPI, freq
from machine import deepsleep, idle, RTC, reset_cause, DEEPSLEEP_RESET, DEEPSLEEP
from utime import sleep_ms, sleep, sleep_us
from micropython import mem_info, const, stack_use
"""
Pins 1 and 3 are REPL UART TX and RX respectively
Pins 6, 7, 8, 11, 16, and 17 are used for connecting the embedded flash, and are not recommended for other uses
Pins 34-39 are input only, and also do not have internal pull-up resistors
The pull value of some pins can be set to Pin.PULL_HOLD to reduce power consumption during deepsleep.
"""
print(os.uname(), '\n')
print ("implementation: ",sys.implementation) # no ()
print ("platform: ", sys.platform)
print ("version: ",sys.version)
print ("sys.path: ", sys.path) # list
print ("modules imported: ", sys.modules) # dict
print('cpu frequency: %d Mhz' %(freq()/1000000))
print('flash size in Mbytes: ', flash_size()/(1024.0*1024.0))
print ('ESP32 internal temp %d' %(int(raw_temperature()-32)*5.0/9.0))
#esp.osdebug(None)
#to display flash size
#import port_diag
import uos
# do not include for 512k port, no file system
# free file system
i= uos.statvfs('/')
fs = i[1]*i[2]/(1024.0*1024.0)
free= i[0]*i[4]/(1024.0*1024.0)
per = (float(free)/float(fs))
print('file system size %0.1f, free %0.1f, free in percent %0.1f' %(fs, free, per))
#uos.dupterm(None, 1) # disable REPL on UART(0)
def start_repl():
# need to import once webrepl_setup from a usb/ttl connection to set password
# creates webrepl_cfg.py (not visible in uPyCraft, visible w: os.listdir()
# cannot just browse to IP, need client http://micropython.org/webrepl/
import webrepl
print('import webrepl_setup once to set password')
print('start webrepl: use http://micropython.org/webrepl/ to access or use local webrepl.html')
print('ws://192.168.1.5:8266/')
webrepl.start()
def wifi_connect(ssid,psk):
import network
from time import sleep_ms
i=0
ok = True
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
print('set static IP')
sta_if.ifconfig(('192.168.1.5', '255.255.255.0','192.168.1.1', '8.8.8.8'))
sta_if.connect(ssid, psk)
while not sta_if.isconnected():
sleep_ms(300)
i = i + 1
if i >=10:
ok=False
break
if ok == True:
sleep_ms(10)
print('\n\nconnected. network config:', sta_if.ifconfig())
print ('status: ', sta_if.status())
print('ssid: ', ssid)
return (sta_if)
else:
print('cannot connect to %s' %(ssid))
return(None)
# return None or sta_id
"""
credential for wifi stored in mynet.py
net = [
['ssid1', 'pass1'] , \
['ssid2', 'pass2'] , \
['ssd3', 'pass3'], \
['ssid4', 'pass4'] \
]
"""
########################################################
# update epaper with content. either pdf or all white
# buf already allocated and updated
# use modified library
########################################################
def refresh_epaper(buf):
miso = Pin(12) # not used
sck = Pin(13)
mosi = Pin(14) # DIN
cs = Pin(15)
busy = Pin(25)
dc = Pin(27)
rst = Pin(26)
s = ticks_ms()
spi = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=sck, miso=miso, mosi=mosi)
print('SPI started')
import epaper4in2_mod # modified W and H
e = epaper4in2_mod.EPD(spi, cs, dc, rst, busy)
e.init()
print('epaper initialized, display frame')
e.display_frame(buf)
print('displayed. put to sleep')
e.sleep_75()
print('epaper execution time (ms): ', ticks_ms()-s)
###############################################
# start wifi
# set local time with ntp
###############################################
import mynet
print(mynet.net)
wifi_ok = False
for i in range(len(mynet.net)): # try all networks
print("\ntrying to connect to wifi %s ...\n" %(mynet.net[i][0]))
wifi = wifi_connect(mynet.net[i][0], mynet.net[i][1])
if wifi != None:
print('\n************** wifi connected **************\n')
wifi_ok = True
break
if wifi_ok == False:
print('could not connect to any wifi')
sleeptime_msec = 24*60*60*1000 # one day
print('deep sleep hr', sleeptime_msec/(3600*1000))
led.off()
deepsleep(sleeptime_msec)
else: # wifi OK
# set local time from ntp server. not used yet. but for a future version maybe
try: # protect from timeout in ntp
print('local time before ntp: ', localtime())
print('set time with ntp')
ntptime.settime()
print('UTC time after ntp: ', localtime())
except Exception as e:
print('exception in ntp ', str(e))
"""
# micropython does not handle local time
localtime(mktime(localtime() + 1 * 3600))
print('local time: ', localtime())
"""
########################################################
# memory analysis
########################################################
# ESP32 4MB, heap is 111168 bytes. note: stack is fixed 15360 / 1024 = 15Kio
gc.collect()
free = gc.mem_free(); alloc = gc.mem_alloc()
print("\nmem free %d, alloc %d, sum %d, percent free %0.2f" %(free, alloc, free+alloc, free/(free+alloc)))
# garbage collection can be executed manually, if alloc fails, or if 25% of currently free heap becomes occupied
#gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())
print("stack used: ", stack_use())
print("mem info (1 for memory map): ", mem_info())
################################################################
# every so often, based on RTC memory, fill screen with white, than go to sleep for a while
################################################################
if avoid_ghost:
print('write entiere screen with white to avoid ghosts')
buf = bytearray(800 * 480 // 8)
fb = framebuf.FrameBuffer(buf, 800, 480, framebuf.MONO_HLSB)
white = 1
# frame buffer is all white
fb.fill(white)
refresh_epaper(buf)
sleeptime_msec = 10*60*1000 # 10 mn
print('deep sleep after un-gosthing the screen', sleeptime_msec/(3600*1000))
deepsleep(sleeptime_msec)
##############################################
# build framebuffer with pbm content
##############################################
# if running big.py on windows, the pbm file is already in ESP32 flash memory (synched with vscode and pymaker)
#(fb,buf) = frame_local()
# get pbm file from raspberry PI web server
(fb,buf) = frame_remote()
print('toggle led to signal epaper refresh')
for i in range(5):
led.on()
sleep_ms(200) # blynk led to signal we are about to refresh
led.off()
sleep_ms(200) #
led.off()
refresh_epaper(buf)
for i in range(5):
led.on()
sleep_ms(200) # blynk led to signal refresh done
led.off()
sleep_ms(200)
led.off()
sleeptime_msec = 24*60*60*1000 # one day
print('deep sleep hr', sleeptime_msec/(3600*1000))
print ('script execution time(ms): ', ticks_ms()-start_time)
deepsleep(sleeptime_msec)
print('boot ends')
"""
41 sec execution, 31 sec in epaper
"""
"""
https://docs.micropython.org/en/latest/reference/constrained.html
The information that is printed is implementation dependent,
but currently includes the amount of stack and heap used.
In verbose mode (1) it prints out the entire heap indicating
which blocks are used and which are free. . free block
Each letter represents a single block of memory, a block being 16 bytes.
So each line of the heap dump represents 0x400 bytes or 1KiB of RAM.
00000: h=hhhhBM.LDSShDhB=.B=Bhh=SB=h====h===hh==h======================
00400: ================================================================
03800: ...........h========h============...............................
(22 lines all free)
09400: ....
15 + 22 lines + 4x16 bytes. 37x1024 + 64 = 37952 . GC: total: 37952, used: 8000, free: 29952
stack: 2128 out of 8192
"""