def test_safe(self): b= Battery() b.battery_raw = MagicMock(side_effect=[3400,3800]) self.assertEqual(b.safe(), False) self.assertEqual(b.safe(), True)
def test_value(self): b= Battery() b.battery_raw = MagicMock(side_effect=[3500,3800,5000]) self.assertEqual(b.value(), 0) self.assertEqual(b.value(), 100)
class Display(object): IMG_DIR = '/flash/imgs' def __init__(self, debug=False): self.cfg = None self.rtc = RTC() self.debug = debug self.battery = Battery() # use this pin for debug self.wifi_pin = Pin("GP24", mode=Pin.IN, pull=Pin.PULL_UP) # Empty WDT object self.wdt = None if not debug: if not self.wifi_pin(): self.wdt = WDT(timeout=20000) self.sd = None else: from machine import SD try: self.sd = SD() mount(self.sd, '/sd') self.logfile = open("/sd/display.log", "a") except OSError: self.sd = None self.logfile = None self.epd = EPD() self.log("Time left on the alarm: %dms" % self.rtc.alarm_left()) # Don't flash when we're awake outside of debug heartbeat(self.debug) def log(self, msg, end='\n'): time = "%d, %d, %d, %d, %d, %d" % self.rtc.now()[:-2] msg = time + ", " + msg if self.logfile: self.logfile.write(msg + end) print(msg, end=end) def feed_wdt(self): if self.wdt: self.wdt.feed() def connect_wifi(self): from network import WLAN if not self.cfg: raise ValueError("Can't initialise wifi, no config") self.log('Starting WLAN, attempting to connect to ' + ','.join(self.cfg.wifi.keys())) wlan = WLAN(0, WLAN.STA) wlan.ifconfig(config='dhcp') while not wlan.isconnected(): nets = wlan.scan() for network in nets: if network.ssid in self.cfg.wifi.keys(): self.log('Connecting to ' + network.ssid) self.feed_wdt() # just in case wlan.connect(ssid=network.ssid, auth=(network.sec, self.cfg.wifi[network.ssid])) while not wlan.isconnected(): idle() break self.feed_wdt() # just in case sleep_ms(2000) self.log('Connected as %s' % wlan.ifconfig()[0]) @staticmethod def reset_cause(): import machine val = machine.reset_cause() if val == machine.POWER_ON: return "power" elif val == machine.HARD_RESET: return "hard" elif val == machine.WDT_RESET: return "wdt" elif val == machine.DEEPSLEEP_RESET: return "sleep" elif val == machine.SOFT_RESET: return "soft" def set_alarm(self, now, json_metadata): import json json_dict = json.loads(json_metadata) # Now we know the time too self.rtc = RTC(datetime=now) list_int = json_dict["wakeup"][:6] time_str = ",".join([str(x) for x in list_int]) self.log("Setting alarm for " + time_str) self.rtc.alarm(time=tuple(list_int)) if self.rtc.alarm_left() == 0: self.log("Alarm failed, setting for +1 hour") self.rtc.alarm(time=3600000) del json def display_file_image(self, file_obj): towrite = 15016 max_chunk = 250 while towrite > 0: c = max_chunk if towrite > max_chunk else towrite buff = file_obj.read(c) self.epd.upload_image_data(buff, delay_us=2000) self.feed_wdt() towrite -= c self.epd.display_update() def display_no_config(self): self.log("Displaying no config msg") with open(Display.IMG_DIR + '/no_config.bin', 'rb') as pic: self.display_file_image(pic) def display_low_battery(self): self.log("Displaying low battery msg") with open(Display.IMG_DIR + '/low_battery.bin', 'rb') as pic: self.display_file_image(pic) def display_cannot_connect(self): self.log("Displaying no server comms msg") with open(Display.IMG_DIR + '/no_server.bin', 'rb') as pic: self.display_file_image(pic) def display_no_wifi(self): self.log("Displaying no wifi msg") with open(Display.IMG_DIR + '/no_wifi.bin', 'rb') as pic: self.display_file_image(pic) def check_battery_level(self): now_batt = 200 last_batt = self.battery.battery_raw() while now_batt > last_batt: sleep_ms(50) last_batt = now_batt self.feed_wdt() now_batt = self.battery.battery_raw() self.log("Battery value: %d (%d)" % (self.battery.value(), self.battery.battery_raw())) if not self.battery.safe(): self.log("Battery voltage (%d) low! Turning off" % self.battery.battery_raw()) self.feed_wdt() self.display_low_battery() return False else: self.log("Battery value: %d (%d)" % (self.battery.value(), self.battery.battery_raw())) return True def run_deepsleep(self): if not self.run(): # RTC wasn't set, try to sleep forever self.rtc.alarm(time=2000000000) # Set the wakeup (why do it earlier?) rtc_i = self.rtc.irq(trigger=RTC.ALARM0, wake=DEEPSLEEP) self.log("Going to sleep, waking in %dms" % self.rtc.alarm_left()) # Close files on the SD card if self.sd: self.logfile.close() self.logfile = None unmount('/sd') self.sd.deinit() # Turn the screen off self.epd.disable() if not self.wifi_pin(): # Basically turn off deepsleep() else: self.log("DEBUG MODE: Staying awake") pass # Do nothing, allow network connections in def run(self): woken = self.wifi_pin() self.epd.enable() if not self.check_battery_level(): return False try: self.epd.get_sensor_data() except ValueError: self.log("Can't communicate with display, flashing light and giving up") heartbeat(True) sleep_ms(15000) return True if self.rtc.alarm_left() > 0: self.log("Woken up but the timer is still running, refreshing screen only") self.epd.display_update() self.feed_wdt() return True try: self.cfg = Config.load(sd=self.sd) self.log("Loaded config") except (OSError, ValueError) as e: self.log("Failed to load config: " + str(e)) self.display_no_config() try: self.connect_wifi() except: pass # everything while True: sleep_ms(10) self.feed_wdt() self.feed_wdt() self.connect_wifi() content = b'' try: self.log("Connecting to server %s:%d" % (self.cfg.host, self.cfg.port)) c = Connect(self.cfg.host, self.cfg.port, debug=self.debug) self.feed_wdt() cause = Display.reset_cause() if woken: cause = "user" self.log("Reset cause: " + cause) if len(self.cfg.upload_path) > 0: temp = self.epd.get_sensor_data() # we read this already c.post(self.cfg.upload_path, battery=self.battery.value(), reset=cause, screen=temp) self.log("Fetching metadata from " + self.cfg.metadata_path) metadata = c.get_quick(self.cfg.metadata_path, max_length=1024, path_type='json') # This will set the time to GMT, not localtime self.set_alarm(c.last_fetch_time, metadata) self.feed_wdt() del metadata del self.battery self.log("Fetching image from " + self.cfg.image_path) self.epd.image_erase_frame_buffer() self.feed_wdt() length, socket = c.get_object(self.cfg.image_path) if length != 15016: raise ValueError("Wrong data size for image: %d" % length) self.feed_wdt() except (RuntimeError, ValueError, OSError) as e: self.log("Failed to get remote info: " + str(e)) self.display_cannot_connect() self.rtc.alarm(time=3600000) return True sleep_ms(1000) # How do we make the write to display more reliable? self.feed_wdt() self.log("Uploading to display") self.display_file_image(socket) c.get_object_done() # close off socket if self.cfg.src == "sd": # If we've got a working config from SD instead of flash self.log("Transferring working config") Config.transfer() self.log("SUCCESS") self.log("Finished. Mem free: %d" % gc.mem_free()) return True