def __init__(self): self.endCheckValue = -1 self.widthScaleParam = 1 #Lチカ #self.ledRed = machine.PWM(21) #self.ledGreen = machine.PWM(22) #self.ledBlue = machine.PWM(3) #self.ledRed.freq(440) #self.ledGreen.freq(440) #self.ledBlue.freq(440) #Neopixel led_pin = 15 self.led_num = 10 self.leds = Neopixel(led_pin, self.led_num)
def init(self): """ Init logic; connect to wifi, connect to MQTT and setup RTC/NTP """ self._log('Connecting to wifi ({0})... '.format(self._wifi_credentials[0]), tft=True) self._wlan = network.WLAN(network.STA_IF) self._wlan.active(True) self._wlan.connect(*self._wifi_credentials) safety = 10 while not self._wlan.isconnected() and safety > 0: # Wait for the wifi to connect, max 10s time.sleep(1) safety -= 1 self._log('Connecting to wifi ({0})... {1}'.format(self._wifi_credentials[0], 'Done' if safety else 'Fail')) mac_address = ubinascii.hexlify(self._wlan.config('mac'), ':').decode() self._log('Connecting to MQTT...', tft=True) if self._mqtt is not None: self._mqtt.unsubscribe('emon/#') self._mqtt = network.mqtt('emon', self._mqtt_broker, user='******', password='******', clientid=mac_address, data_cb=self._process_data) self._mqtt.start() safety = 5 while self._mqtt.status()[0] != 2 and safety > 0: # Wait for MQTT connection, max 5s time.sleep(1) safety -= 1 self._mqtt.subscribe('emon/#') self._log('Connecting to MQTT... {0}'.format('Done' if safety else 'Fail')) self._log('Sync NTP...', tft=True) self._rtc.ntp_sync(server='be.pool.ntp.org', tz='CET-1CEST-2') safety = 5 while not self._rtc.synced() and safety > 0: # Wait for NTP time sync, max 5s time.sleep(1) safety -= 1 self._last_update = self._rtc.now() self._log('Sync NTP... {0}'.format('Done' if safety else 'Fail')) self._log('Initializing Neopixels...', tft=True) try: self._neopixel = Neopixel(Pin(15), 10, Neopixel.TYPE_RGB) self._neopixel.clear() except Exception: self._neopixel = None self._log('Initializing Neopixels... {0}'.format('Available' if self._neopixel is not None else 'Unavailable')) self._tft.text(0, 14, ' ' * 50, self._tft.DARKGREY) # Clear the line
class RGB(): # 0 IN 1 OUT def __init__(self, port=PORTB, nums=1): from machine import Neopixel self.nums = nums * 3 self.np = Neopixel(Pin(port[0]), self.nums) # self.np = Neopixel(Pin(port[0]), self.nums, Neopixel.TYPE_RGBW) def setColor(self, color, pos=None): if pos == None: self.np.set(1, color, num=self.nums) else: self.np.set(pos, color) def setHSB(self, hue, saturation, brightness, pos=None): if pos == None: self.np.setHSB(1, hue, saturation, brightness, num=self.nums) else: self.np.setHSB(pos, hue, saturation, brightness) def deinit(self): self.np.deinit()
def __init__(self, port=PORTB, nums=1): from machine import Neopixel self.nums = nums * 3 self.np = Neopixel(Pin(port[0]), self.nums)
class RetroPCCG: PAINT_BUFFER = 250 DATA_END = -999 def __init__(self): self.endCheckValue = -1 self.widthScaleParam = 1 #Lチカ #self.ledRed = machine.PWM(21) #self.ledGreen = machine.PWM(22) #self.ledBlue = machine.PWM(3) #self.ledRed.freq(440) #self.ledGreen.freq(440) #self.ledBlue.freq(440) #Neopixel led_pin = 15 self.led_num = 10 self.leds = Neopixel(led_pin, self.led_num) def setParam(self, widthScale, endCheck): self.widthScaleParam = widthScale self.endCheckValue = endCheck def executePaint(self, fileName): print(fileName) #PaintBuffer self.screenBuffer = barray.BARRAY(76800) self.dataFile = open(fileName, "r") self.currentFileLine = "" sizeX = self.readData() #データの横ピクセル数 sizeY = self.readData() #データの縦ピクセル数 rateX = 320 / sizeX rateY = 240 / sizeY #入りきる側の倍率に合わせる if rateX < rateY: rateY = rateX / self.widthScaleParam else: rateX = rateY bgColor = self.readData() frameColor = self.readData() #X, Y, backGroundColor,FrameColor lcd.clear(bgColor) self.offsetX = int((320 - sizeX * rateX) / 2) self.offsetY = int((240 - sizeY * rateY) / 2) #画面サイズより小さい場合、枠を描く if self.offsetX > 0: self.drawLine(self.offsetX, 0 + self.offsetY , self.offsetX,sizeY + self.offsetY, frameColor) self.drawLine(sizeX + self.offsetX, 0 + self.offsetY, sizeX + self.offsetX , sizeY + self.offsetY, frameColor) if self.offsetY > 0: self.drawLine(0 + self.offsetX, self.offsetY, sizeX + self.offsetX, self.offsetY, frameColor) self.drawLine(0 + self.offsetX, sizeY + self.offsetY, sizeX + self.offsetX , sizeY + self.offsetY , frameColor) startX = 0 endX = 0 startY = 0 endY = 0 color = 0 fileEnd = False status = Status.NONE while fileEnd == False: nextData = self.readData() if nextData == self.DATA_END: break; if status == Status.NONE: if nextData == -10: status = Status.LINE elif nextData == -20: status = Status.PAINT elif nextData == -30: status = Status.LAST_PAINT else: fileEnd = True elif status == Status.LINE: if nextData == -1: status = Status.NONE else: color = nextData startX = self.readData() if startX == -1: status = Status.LINE # 0,-1 で来るパターンがある else: startY = self.readData() #TODO:1ドット打つ status = Status.LINE_NEXT elif status == Status.LINE_NEXT: if nextData <= self.endCheckValue: status = Status.LINE else: endX = nextData endY = self.readData() self.drawLine(int(startX * rateX) + self.offsetX, int(startY * rateY) + self.offsetY, int(endX * rateX) + self.offsetX,int(endY * rateY) + self.offsetY, color) startX = endX startY = endY elif status == Status.PAINT: if nextData <= self.endCheckValue: status = Status.NONE else: color = nextData paintX = self.readData() paintY = self.readData() self.paint(int(paintX * rateX) + self.offsetX,int(paintY * rateY) + self.offsetY ,color) status = Status.PAINT_NEXT elif status == Status.PAINT_NEXT: if nextData <= self.endCheckValue: status = Status.PAINT else: paintX = nextData paintY = self.readData() self.paint(int(paintX * rateX) + self.offsetX,int(paintY * rateY) + self.offsetY ,color) elif status == Status.LAST_PAINT: if nextData <= self.endCheckValue: status = Status.NONE else: color = nextData startX = self.readData() startY = self.readData() endX = self.readData() endY = self.readData() self.lastPaint(int(startX * rateX) + self.offsetX,int(startY * rateY) + self.offsetY, \ int(endX * rateX) + self.offsetX,int(endY * rateY) + self.offsetY ,color) gc.collect() #self.testBuffer() self.dataFile.close() if self.offsetX > 0: self.paint(0, 120, frameColor) self.paint(319, 120, frameColor) if self.offsetY > 0: self.paint(160, 0, frameColor) self.paint(160, 239, frameColor) del self.currentFileLine del self.screenBuffer del self.dataFile gc.collect() #self.ledRed.duty(0) #self.ledGreen.duty(0) #self.ledBlue.duty(0) for i in range(self.led_num): leds.set(i + 1, 0) def readData(self): while True: #値設定While while True: #ファイル1行読みこみWhile if len(self.currentFileLine) == 0: self.currentFileLine = self.dataFile.readline().strip() if self.currentFileLine : # "#"以降はコメント commentIndex = self.currentFileLine.find("#") if commentIndex != -1: self.currentFileLine = self.currentFileLine[:commentIndex].strip() if len(self.currentFileLine) > 1: break else: return self.DATA_END else: break #次のカンマの位置までを切り出す index = self.currentFileLine.find(",") if index == -1: #カンマ見つからず result = self.currentFileLine.strip() del self.currentFileLine gc.collect() self.currentFileLine = "" if len(result) > 0: return int(result,0) else: result = self.currentFileLine[:index].strip() self.currentFileLine = self.currentFileLine[index+1:].strip() if len(result) > 0: return int(result,0) def drawLine(self,startX,startY,endX,endY,color): realStartX = startX realStartY = startY realEndX = endX realEndY = endY dx = abs(realEndX - realStartX) sx = 1 if realStartX < realEndX else -1 dy = abs(realEndY - realStartY) sy = 1 if realStartY < realEndY else -1 err = int((dx if dx > dy else -dy) / 2) while True: if realStartX >= 0 and realStartX < 320 and realStartY >=0 and realStartY < 240: lcd.pixel(realStartX,realStartY,color) self.screenBuffer.put(realStartX + realStartY * 320, True) if realStartX == realEndX and realStartY == realEndY: break e2 = err if e2 > -dx: err -= dy realStartX += sx if e2 < dy: err += dx realStartY += sy def paint(self,pixelX, pixelY, color): #バッファにある限りループ #int lx; /* 領域右端のX座標 */ #int rx; /* 領域右端のX座標 */ #int y; /* 領域のY座標 */ #int oy; /* 親ラインのY座標 */ #Lチカ Colorの赤要素が、他の要素の2倍ある時だけLED ON #colorRed = (color & 0xFF0000) / 0x100 / 0x100 #colorGreen = (color & 0x00FF00) / 0x100 #colorBlue = (color & 0x0000FF) #print("R:" + str(colorRed) + ",G:" + str(colorGreen) + ",B:" + str(colorBlue) + "\n") #if colorRed > 30 and colorRed > (colorGreen + colorBlue): # self.ledRed.duty(colorRed * 100 / 255) #self.ledGreen.duty(colorGreen * 100 / 255) #self.ledBlue.duty(colorBlue * 100 / 255) for i in range(self.led_num): self.leds.set(i + 1, color) gPoint = {"lx":pixelX, "rx":pixelX ,"y":pixelY, "oy":pixelY} self.bufferList = [gPoint] while len(self.bufferList) > 0: checkPoint = self.bufferList.pop(0) lx = checkPoint["lx"] rx = checkPoint["rx"] ly = checkPoint["y"] oy = checkPoint["oy"] lxsav = lx - 1 rxsav = rx + 1 ##処理済みなら飛ばす if self.screenBuffer.get(lx + ly * 320) == True: continue ##右の境界を探す while rx < 319: if self.screenBuffer.get(rx + 1 + ly * 320) == True: break rx = rx + 1 ##左の境界を探す while lx > 0: if self.screenBuffer.get(lx - 1 + ly * 320) == True: break lx = lx - 1 ##lx から rx まで線を引く self.drawLine(lx, ly, rx, ly, color) gc.collect() ##真上のスキャンライン走査 if ly - 1 >= 0: if ly - 1 == oy: self.scanLine( lx, lxsav, ly -1 ,ly ) self.scanLine( rxsav, rx, ly -1 ,ly ) else: self.scanLine( lx, rx, ly -1 ,ly ) ##真下のスキャンライン走査 if ly + 1 <= 239: if ly + 1 == oy: self.scanLine( lx, lxsav, ly + 1, ly) self.scanLine( rxsav, rx, ly + 1, ly) else: self.scanLine( lx, rx, ly +1, ly) #Lチカ # self.pinout.value(0) def scanLine(self, lx, rx, y, oy): templx = 0 while lx <= rx: while lx < rx: if self.screenBuffer.get(lx + y * 320) == False: break lx = lx + 1 if self.screenBuffer.get(lx + y * 320) == True: break templx = lx while lx <= rx: if self.screenBuffer.get(lx + y * 320) == True: break lx = lx + 1 gPoint = {"lx":templx, "rx":lx - 1 ,"y":y, "oy":oy} if len(self.bufferList) < self.PAINT_BUFFER: self.bufferList.append(gPoint) #指定された矩形で、まだ塗っていない部分だけ塗る def lastPaint(self, startX, startY, endX, endY, color): print("lastPaint:" + str(startX) + ":"+ str(startY) +":"+ str(endX) +":"+ str(endY) ) y = startY while y <= endY: x = startX while x <= endX: if self.screenBuffer.get(x + y * 320) == False: lcd.pixel(x,y,color) x = x + 1 y = y + 1 def testBuffer(self): for x in range(0,320): for y in range(0,240): if self.screenBuffer.get(x + y * 320) == True: lcd.pixel(x,y,lcd.WHITE) else: lcd.pixel(x,y,lcd.BLACK)
class Monitor(object): def __init__( self, solar_topic, grid_topic, mqtt_broker, wifi_credentials, graph_interval_s=60, update_interval_ms=1000 ): self._solar_topic = solar_topic self._grid_topic = grid_topic self._mqtt_broker = mqtt_broker self._wifi_credentials = wifi_credentials self._graph_interval = graph_interval_s self._update_interval = update_interval_ms self._graph_window = Monitor._shorten(self._graph_interval * 320) self._tft = None self._wlan = None self._mqtt = None self._neopixel = None self._battery = IP5306(I2C(scl=Pin(22), sda=Pin(21))) self._timer = Timer(0) self._rtc = RTC() self._button_a = ButtonA(callback=self._button_a_pressed) self._button_b = ButtonB(callback=self._button_b_pressed) self._button_c = ButtonC(callback=self._button_c_pressed) self._reboot = False self._backup = False self._solar = None self._usage = None self._grid = None self._importing = None self._prev_importing = None self._solar_avg_buffer = [] self._grid_avg_buffer = [] self._usage_buffer = [] self._usage_buffer_max = 0 self._usage_buffer_min = 0 self._usage_buffer_avg = 0 self._usage_buffer_stddev = 0 self._usage_max_coords = [0, 0] self._calculate_buffer_stats('usage', 0) self._solar_buffer = [] self._solar_buffer_max = 0 self._solar_buffer_min = 0 self._solar_buffer_avg = 0 self._solar_buffer_stddev = 0 self._solar_max_coords = [0, 0] self._calculate_buffer_stats('solar', 0) self._last_update = (0, 0, 0, 0, 0, 0) self._data_received = [False, False] self._buffer_updated = False self._realtime_updated = False self._last_value_added = None self._graph_max = 0 self._solar_max = 0 self._usage_max = 0 self._menu_horizontal_pointer = 0 self._menu_tick = 0 self._menu_tick_divider = 0 self._blank_menu = False self._save = False self._show_markers = True self._color = None self._ticks = {'M': 0, # MQTT message 'D': 0, # Data sample (solar + grid) 'R': 0, # Remaining time for next graph update 'G': 0, # Graph datapoint added 'B': 0, # Button press 'E': 0} # Exceptions self._tick_keys = ['M', 'D', 'G', 'B', 'R', 'E'] self._last_exception = 'None' self._runtime_config_parameters = ['show_markers'] self._last_logline = '' self._log('Initializing TFT...') self._tft = display.TFT() self._tft.init(self._tft.M5STACK, width=240, height=320, rst_pin=33, backl_pin=32, miso=19, mosi=23, clk=18, cs=14, dc=27, bgr=True, backl_on=1) self._tft.tft_writecmd(0x21) # Invert colors self._tft.clear() self._tft.font(self._tft.FONT_Default, transparent=False) self._tft.text(0, 0, 'USAGE', self._tft.DARKGREY) self._tft.text(self._tft.CENTER, 0, 'IMPORTING', self._tft.DARKGREY) self._tft.text(self._tft.RIGHT, 0, 'SOLAR', self._tft.DARKGREY) self._tft.text(0, 14, 'Loading...', self._tft.DARKGREY) self._log('Initializing TFT... Done') def init(self): """ Init logic; connect to wifi, connect to MQTT and setup RTC/NTP """ self._log('Connecting to wifi ({0})... '.format(self._wifi_credentials[0]), tft=True) self._wlan = network.WLAN(network.STA_IF) self._wlan.active(True) self._wlan.connect(*self._wifi_credentials) safety = 10 while not self._wlan.isconnected() and safety > 0: # Wait for the wifi to connect, max 10s time.sleep(1) safety -= 1 self._log('Connecting to wifi ({0})... {1}'.format(self._wifi_credentials[0], 'Done' if safety else 'Fail')) mac_address = ubinascii.hexlify(self._wlan.config('mac'), ':').decode() self._log('Connecting to MQTT...', tft=True) if self._mqtt is not None: self._mqtt.unsubscribe('emon/#') self._mqtt = network.mqtt('emon', self._mqtt_broker, user='******', password='******', clientid=mac_address, data_cb=self._process_data) self._mqtt.start() safety = 5 while self._mqtt.status()[0] != 2 and safety > 0: # Wait for MQTT connection, max 5s time.sleep(1) safety -= 1 self._mqtt.subscribe('emon/#') self._log('Connecting to MQTT... {0}'.format('Done' if safety else 'Fail')) self._log('Sync NTP...', tft=True) self._rtc.ntp_sync(server='be.pool.ntp.org', tz='CET-1CEST-2') safety = 5 while not self._rtc.synced() and safety > 0: # Wait for NTP time sync, max 5s time.sleep(1) safety -= 1 self._last_update = self._rtc.now() self._log('Sync NTP... {0}'.format('Done' if safety else 'Fail')) self._log('Initializing Neopixels...', tft=True) try: self._neopixel = Neopixel(Pin(15), 10, Neopixel.TYPE_RGB) self._neopixel.clear() except Exception: self._neopixel = None self._log('Initializing Neopixels... {0}'.format('Available' if self._neopixel is not None else 'Unavailable')) self._tft.text(0, 14, ' ' * 50, self._tft.DARKGREY) # Clear the line def _process_data(self, message): """ Process MQTT message """ try: topic = message[1] data = float(message[2]) self._ticks['M'] += 1 # Collect data samples from solar & grid if topic == self._solar_topic: self._solar = max(0.0, data) self._data_received[0] = True elif topic == self._grid_topic: self._grid = data self._data_received[1] = True if self._data_received[0] and self._data_received[1]: self._ticks['D'] += 1 # Once the data has been received, calculate realtime usage self._usage = self._solar + self._grid self._last_update = self._rtc.now() self._realtime_updated = True # Redraw realtime values self._data_received = [False, False] # Process data for the graph; collect solar & grids, and every x-pixel # average the data out and draw them on that pixel. now = time.time() rounded_now = int(now - now % self._graph_interval) if self._last_value_added is None: self._last_value_added = rounded_now self._ticks['R'] = int(rounded_now + self._graph_interval - now) self._solar_avg_buffer.append(int(self._solar)) self._grid_avg_buffer.append(int(self._grid)) if self._last_value_added != rounded_now: self._ticks['G'] += 1 solar, usage = self._read_avg_buffer(reset=True) self._solar_buffer.append(solar) self._solar_buffer = self._solar_buffer[-319:] # Keep one pixel for moving avg self._calculate_buffer_stats('solar', solar) self._usage_buffer.append(usage) self._usage_buffer = self._usage_buffer[-319:] # Keep one pixel for moving avg self._calculate_buffer_stats('usage', usage) self._last_value_added = rounded_now self._buffer_updated = True # Redraw the complete graph except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in process data: {0}'.format(ex)) def _calculate_buffer_stats(self, buffer_type, single_value): buffer = getattr(self, '_{0}_buffer'.format(buffer_type)) if len(buffer) == 0: return setattr(self, '_{0}_buffer_max'.format(buffer_type), single_value if len(buffer) == 1 else max(*buffer)) setattr(self, '_{0}_buffer_min'.format(buffer_type), single_value if len(buffer) == 1 else min(*buffer)) setattr(self, '_{0}_buffer_avg'.format(buffer_type), sum(buffer) / len(buffer)) setattr(self, '_{0}_buffer_stddev'.format(buffer_type), Monitor._stddev(buffer)) def _read_avg_buffer(self, reset): solar_avg_buffer_length = len(self._solar_avg_buffer) grid_avg_buffer_length = len(self._grid_avg_buffer) if solar_avg_buffer_length == 0 or grid_avg_buffer_length == 0: return 0, 0 solar = int(sum(self._solar_avg_buffer) / solar_avg_buffer_length) grid = int(sum(self._grid_avg_buffer) / grid_avg_buffer_length) usage = solar + grid if reset: self._solar_avg_buffer = [] self._grid_avg_buffer = [] return solar, usage def load(self): self._log('Loading runtime configuration...', tft=True) if 'runtime_config.json' in os.listdir('/flash'): with open('/flash/runtime_config.json', 'r') as f: runtime_config = ujson.load(f) for key in self._runtime_config_parameters: if key in runtime_config: setattr(self, '_{0}'.format(key), runtime_config[key]) self._log('Loading runtime configuration... Done', tft=True) self._log('Restoring backup...', tft=True) if 'backup.json' in os.listdir('/flash'): with open('/flash/backup.json', 'r') as f: backup = ujson.load(f) self._usage_buffer = backup.get('usage_buffer', []) self._calculate_buffer_stats('usage', 0) self._solar_buffer = backup.get('solar_buffer', []) self._calculate_buffer_stats('solar', 0) os.remove('/flash/backup.json') self._log('Restoring backup... Done', tft=True) def run(self): """ Set timer """ self._timer.init(period=self._update_interval, mode=Timer.PERIODIC, callback=self._tick) def _tick(self, timer): """ Do stuff at a regular interval """ _ = timer self._draw() try: # At every tick, make sure wifi is still connected if not self._wlan.isconnected(): self.init() except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in watchdog: {0}'.format(ex)) if self._reboot: self._take_backup() self._save_runtime_config() machine.reset() if self._backup: self._take_backup() self._save_runtime_config() self._backup = False if self._save: self._save_runtime_config() self._save = False def _save_runtime_config(self): data = {} for key in self._runtime_config_parameters: data[key] = getattr(self, '_{0}'.format(key)) with open('/flash/runtime_config.json', 'w') as runtime_config_file: runtime_config_file.write(ujson.dumps(data)) def _take_backup(self): with open('/flash/backup.json', 'w') as backup_file: backup_file.write(ujson.dumps({'usage_buffer': self._usage_buffer, 'solar_buffer': self._solar_buffer})) def _draw(self): """ Update display """ try: self._draw_realtime() except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in draw realtime: {0}'.format(ex)) try: self._draw_graph() except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in draw graph: {0}'.format(ex)) try: self._draw_menu() except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in draw menu: {0}'.format(ex)) try: self._draw_rgb() except Exception as ex: self._last_exception = str(ex) self._ticks['E'] += 1 self._log('Exception in draw rgb: {0}'.format(ex)) def _draw_rgb(self): """ Uses the neopixel leds (if available) to indicate how "good" our power consumption is. """ if self._neopixel is None: return if len(self._solar_buffer) == 0 or self._usage is None: self._neopixel.clear() return high_usage = self._usage > self._usage_buffer_avg + (self._usage_buffer_stddev * 2) if self._grid < 0: # Feeding back to the grid score = 0 if self._grid < -500: score += 1 if self._grid < -1000: score += 1 if high_usage: score -= 1 colors = [Neopixel.GREEN, Neopixel.LIME, Neopixel.YELLOW] color = colors[max(0, score)] else: score = 0 if high_usage: score += 1 if self._solar == 0: score += 1 colors = [Neopixel.BLUE, Neopixel.PURPLE, Neopixel.RED] color = colors[max(0, score)] if self._color != color: self._neopixel.set(0, color, num=10) self._color = color def _draw_realtime(self): """ Realtime part; current usage, importing/exporting and solar """ if not self._realtime_updated: return self._tft.text(self._tft.RIGHT, 14, ' {0:.2f}W'.format(self._solar), self._tft.YELLOW) self._tft.text(0, 14, '{0:.2f}W '.format(self._usage), self._tft.BLUE) self._importing = self._grid > 0 if self._prev_importing != self._importing: if self._importing: self._tft.text(self._tft.CENTER, 0, ' IMPORTING ', self._tft.DARKGREY) else: self._tft.text(self._tft.CENTER, 0, ' EXPORTING ', self._tft.DARKGREY) if self._importing: self._tft.text(self._tft.CENTER, 14, ' {0:.2f}W '.format(abs(self._grid)), self._tft.RED) else: self._tft.text(self._tft.CENTER, 14, ' {0:.2f}W '.format(abs(self._grid)), self._tft.GREEN) self._prev_importing = self._importing self._realtime_updated = False def _draw_graph(self): """ Draw the graph part """ solar_moving_avg, usage_moving_avg = self._read_avg_buffer(reset=False) solar_max = max(self._solar_buffer_max, solar_moving_avg) usage_max = max(self._usage_buffer_max, usage_moving_avg) max_value = float(max(solar_max, usage_max)) if max_value != self._graph_max: self._graph_max = max_value self._buffer_updated = True if solar_max != self._solar_max: self._solar_max = solar_max self._buffer_updated = True if usage_max != self._usage_max: self._usage_max = usage_max self._buffer_updated = True ratio = 1 if max_value == 0 else (180.0 / max_value) show_markers = self._show_markers and max_value > 0 buffer_size = len(self._usage_buffer) avg_marker = False usage_max_coords = self._usage_max_coords solar_max_coords = self._solar_max_coords if self._buffer_updated: for index, usage in enumerate(self._usage_buffer): solar = self._solar_buffer[index] usage_y, solar_y = self._draw_graph_line(index, solar, usage, ratio) if usage == usage_max: usage_max_coords = [index, usage_y] if solar == solar_max: solar_max_coords = [index, solar_y] usage_y, solar_y = self._draw_graph_line(buffer_size, solar_moving_avg, usage_moving_avg, ratio) if usage_moving_avg == usage_max: avg_marker = True usage_max_coords = [buffer_size, usage_y] if solar_moving_avg == solar_max: avg_marker = True solar_max_coords = [buffer_size, solar_y] max_coords_changed = self._usage_max_coords != usage_max_coords or self._solar_max_coords != solar_max_coords if self._buffer_updated and max_coords_changed: self._tft.rect(buffer_size + 1, 40, 320, 220, self._tft.BLACK, self._tft.BLACK) if show_markers: self._draw_marker('{0:.0f}W'.format(solar_max), solar_max_coords, not avg_marker) self._draw_marker('{0:.0f}W'.format(usage_max), usage_max_coords, not avg_marker) self._usage_max_coords = usage_max_coords self._solar_max_coords = solar_max_coords self._buffer_updated = False def _draw_marker(self, text, coords, transparent): x, y = coords if x > 160: text_x = x - self._tft.textWidth(text) - 10 line_start_x = x - 2 line_end_x = x - 8 else: text_x = x + 10 line_start_x = x + 2 line_end_x = text_x - 2 if y > 120: text_y = y - 22 else: text_y = y + 10 self._tft.font(self._tft.FONT_Default, transparent=transparent) self._tft.text(text_x, text_y, text, self._tft.DARKGREY) self._tft.line(line_start_x, y, line_end_x, text_y + 6, self._tft.DARKGREY) self._tft.font(self._tft.FONT_Default, transparent=False) def _draw_graph_line(self, index, solar, usage, ratio): usage_height = int(usage * ratio) solar_height = int(solar * ratio) usage_y = 220 - usage_height solar_y = 220 - solar_height max_height = max(usage_height, solar_height) self._tft.line(index, 40, index, 220 - max_height, self._tft.BLACK) if usage_height > solar_height: self._tft.line(index, usage_y, index, solar_y, self._tft.BLUE) if solar_height > 0: self._tft.line(index, solar_y, index, 220, self._tft.DARKCYAN) else: self._tft.line(index, solar_y, index, usage_y, self._tft.YELLOW) if usage_height > 0: self._tft.line(index, usage_y, index, 220, self._tft.DARKCYAN) return usage_y, solar_y def _draw_menu(self): if self._blank_menu: self._tft.rect(0, 221, 320, 240, self._tft.BLACK, self._tft.BLACK) self._blank_menu = False if self._menu_horizontal_pointer == 0: data = 'Updated: {0:04d}/{1:02d}/{2:02d} {3:02d}:{4:02d}:{5:02d}'.format(*self._last_update[:6]) elif self._menu_horizontal_pointer == 1: data = 'Battery: {0}%'.format(self._battery.level) elif self._menu_horizontal_pointer == 2: data = 'Graph: {0} {1}, max {2:.2f}W'.format(len(self._usage_buffer), self._graph_window, self._graph_max) elif self._menu_horizontal_pointer in [3, 4]: data_type = 'solar' if self._menu_horizontal_pointer == 3 else 'usage' solar, usage = self._read_avg_buffer(reset=False) if self._menu_tick == 0: value = min( getattr(self, '_{0}_buffer_min'.format(data_type)), solar if data_type == 'solar' else usage, self._solar if data_type == 'solar' else self._usage ) info = 'min' elif self._menu_tick == 1: value = getattr(self, '_{0}_buffer_avg'.format(data_type)) info = 'avg' elif self._menu_tick == 2: value = getattr(self, '_{0}_buffer_avg'.format(data_type)) + (getattr(self, '_{0}_buffer_stddev'.format(data_type)) * 2) info = 'high' else: value = max( getattr(self, '_{0}_buffer_max'.format(data_type)), solar if data_type == 'solar' else usage, self._solar if data_type == 'solar' else self._usage ) info = 'max' data = '{0}{1} stats: {2:.2f}W {3}'.format(data_type[0].upper(), data_type[1:], value, info) elif self._menu_horizontal_pointer == 5: data = 'Time: {0}'.format(time.time()) elif self._menu_horizontal_pointer == 6: data = 'Exception: {0}'.format(self._last_exception[:20]) elif self._menu_horizontal_pointer == 7: data = 'Press B to reboot' elif self._menu_horizontal_pointer == 8: data = 'Press B to take a backup' elif self._menu_horizontal_pointer == 9: data = 'Press B to {0} markers'.format('hide' if self._show_markers else 'show') elif self._menu_horizontal_pointer == 10: log_entry = self._last_logline[:26] if len(log_entry) < 26: log_entry += ' ' * (26 - len(log_entry)) data = 'Log: {0}'.format(log_entry) else: data = 'Ticks: {0}'.format(', '.join('{0}'.format(self._ticks[key]) for key in self._tick_keys)) self._tft.text(0, self._tft.BOTTOM, '<', self._tft.DARKGREY) self._tft.text(self._tft.RIGHT, self._tft.BOTTOM, '>', self._tft.DARKGREY) if len(data) < 32: padding = int(float(32 - len(data) + 1) / 2) data = '{0}{1}{2}'.format(' ' * padding, data, ' ' * padding) self._tft.text(self._tft.CENTER, self._tft.BOTTOM, data, self._tft.DARKGREY) self._menu_tick_divider += 1 if self._menu_tick_divider == 3: # Increase menu tick every X seconds self._menu_tick += 1 if self._menu_tick == 4: self._menu_tick = 0 self._menu_tick_divider = 0 def _button_a_pressed(self, pin, pressed): _ = pin if pressed: self._ticks['B'] += 1 self._menu_horizontal_pointer -= 1 if self._menu_horizontal_pointer < 0: self._menu_horizontal_pointer = 11 self._blank_menu = True def _button_b_pressed(self, pin, pressed): _ = pin if pressed: if self._menu_horizontal_pointer == 7: self._reboot = True elif self._menu_horizontal_pointer == 8: self._backup = True elif self._menu_horizontal_pointer == 9: self._show_markers = not self._show_markers self._save = True def _button_c_pressed(self, pin, pressed): _ = pin if pressed: self._ticks['B'] += 1 self._menu_horizontal_pointer += 1 if self._menu_horizontal_pointer > 11: self._menu_horizontal_pointer = 0 self._blank_menu = True @staticmethod def _stddev(entries): """ returns the standard deviation of lst """ avg = sum(entries) / len(entries) variance = sum([(e - avg) ** 2 for e in entries]) / len(entries) return sqrt(variance) @staticmethod def _shorten(seconds): """ Converts seconds to a `xh ym ys` notation """ parts = [] seconds_hour = 60 * 60 seconds_minute = 60 if seconds >= seconds_hour: hours = int((seconds - seconds % seconds_hour) / seconds_hour) seconds = seconds - (hours * seconds_hour) parts.append('{0}h'.format(hours)) if seconds >= seconds_minute: minutes = int((seconds - seconds % seconds_minute) / seconds_minute) seconds = seconds - (minutes * seconds_minute) parts.append('{0}m'.format(minutes)) if seconds > 0: parts.append('{0}s'.format(seconds)) return ' '.join(parts) def _log(self, message, tft=False): """ Logs a message to the console and (optionally) to the display """ print(message) self._last_logline = message if tft: self._tft.text(0, 14, '{0}{1}'.format(message, ' ' * 50), self._tft.DARKGREY)
from machine import I2C, Pin, Neopixel import robot time.sleep_ms(500) np = Neopixel(Pin(27), 1) np.set(0,0x001000) #green np.show() neopixel_pin=27 pinsVbatt=[26,33,14,5,13] pins3v=[4,12,2,15,0] neopixel_pin=27 uart_pins=[16,17] #first 4 pin connecter i2c_pins=[21,22] #2nd and 3rd 4 pin connector i2c = I2C(sda=Pin(21), scl=Pin(22), freq=100000) i2c.scan() # i2c adressen: #48 (=0x30): motor driver (im gehaeuse) #104 (=0x68): MPU 6050 (accel/gyro/temp) #41 (=0x29): vl53l0x np.set(0,0x0000FF) #blue? from machine import PWM servo = PWM(Pin(26),freq=50) servo.duty(8) time.sleep(1) for i in range(5): np.set(0,0xFF0000) #red np.show()
R = Neopixel.RED G = Neopixel.GREEN B = Neopixel.BLUE P = Neopixel.PURPLE Y = Neopixel.YELLOW ###################################### # 2x BLUE: Script started # blinking PUPRLE: Connecting to WiFi # 1st GREEN: WiFi connected # blinking YELLOW: syncing time # 4x GREEN: boot process done ###################################### # Set up neopixel and turn off np = Neopixel(Pin(26), 1, 0) np.set(0,0,0,0) for _ in range(2): np.set(0,B,0,0) time.sleep(0.1) np.set(0,0,0,0) # Activate Wifi connection wlan = WLAN(STA_IF) wlan.active(True) wlan.connect(<WIFI_SSID>, <WIFI_PASSWORD>, 5000) while not wlan.isconnected():
R = Neopixel.RED G = Neopixel.GREEN B = Neopixel.BLUE P = Neopixel.PURPLE Y = Neopixel.YELLOW ###################################### # 2x BLUE: Script started # blinking PUPRLE: Connecting to WiFi # 1st GREEN: WiFi connected # blinking YELLOW: syncing time # 4x GREEN: boot process done ###################################### # Set up neopixel and turn off np = Neopixel(Pin(26), 1, 0) np.set(0,0,0,0) for _ in range(2): np.set(0,B,0,0) time.sleep(0.1) np.set(0,0,0,0) # Set up ADC for ACS712 sensor acs712 = ADC(Pin(ADC5)) acs712.atten(ADC.ATTN_11DB) # Capture start time startTime = round(time.time()) # Check network connection
'black': 0x000000, 'red': 0xFF0000, 'maroon': 0x800000, 'yellow': 0xFFFF00, 'olive': 0x808000, 'lime': 0x00FF00, 'green': 0x008000, 'aqua': 0x00FFFF, 'teal': 0x008080, 'blue': 0x0000FF, 'navy': 0x000080, 'fuchsia': 0xFF00FF, 'purple': 0x800080 } #html colors np = Neopixel(Pin(27), 1) #one neopixel at pin 27 for c in colors: print('set neopixel 0 to {}'.format(c)) np.set(0, colors[c]) np.show() time.sleep(.5) #sensors #init i2c bus i2c = I2C(sda=Pin(21), scl=Pin(22), freq=100000) #distance sensor from vl53l0x import VL53L0X dist = VL53L0X(i2c) dist.start() #better performance (faster response, allows higher frequency) # works up to ~1300 mm # returns something >8000 if above.