diff --git a/esp8266/boot.py b/esp8266/boot.py new file mode 100644 index 0000000..b1017cb --- /dev/null +++ b/esp8266/boot.py @@ -0,0 +1,15 @@ +# This file is executed on every boot (including wake-boot from deepsleep) + +import esp +import gc +import webrepl + +esp.osdebug(None) + +webrepl.start() +gc.collect() + +from main import * + + + diff --git a/esp8266/main.py b/esp8266/main.py new file mode 100644 index 0000000..5a9cc8c --- /dev/null +++ b/esp8266/main.py @@ -0,0 +1,38 @@ + +import network + +def report( ) : + sta_if = network.WLAN(network.STA_IF) + if sta_if.isconnected() : + print('network ip: ', sta_if.ifconfig()[0]) + else: + print("no network connection.") + + ap_if = network.WLAN(network.AP_IF) + if ap_if.active() : + print('{} access point: {}'.format(ap_if.config('essid'), ap_if.ifconfig()[0])) + else: + print("Access point not active.") + +from mlx90614 import * +m = mlx(14, 12) + +from oled import * +from terminalfont import * +o = oled(4, 0) + +def display( ) : + pos1 = (0, 0) + pos2 = (0, terminalfont['Height'] + 2) + while True : + t1 = c2f(m.temp()) + t2 = c2f(m.objecttemp()) + o.clear() + s = "amb: {:.2f}".format(t1) + o.text(pos1, s, 1, terminalfont) + s = "obj: {:.2f}".format(t2) + o.text(pos2, s, 1, terminalfont) + o.display() + time.sleep_ms(100) + +display() diff --git a/esp8266/mlx90614.py b/esp8266/mlx90614.py new file mode 100644 index 0000000..5b3ce38 --- /dev/null +++ b/esp8266/mlx90614.py @@ -0,0 +1,70 @@ +'''IR temperature sensor using I2C interface.''' + +import machine, time + +def c2f( aValue ): + '''Celcius to Farenheit conversion.''' + return (aValue * 9.0 / 5.0) + 32.0 + +class mlx(object): + ''' ''' + + _ADDRESS = const(0x5A) + + #RAM +# _RAWIR1 = const(0x04) +# _RAWIR2 = const(0x05) + _TA = const(0x06) + _TOBJ1 = const(0x07) + _TOBJ2 = const(0x08) + + #EEPROM +# _TOMAX = const(0x20) +# _TOMIN = const(0x21) +# _PWMCTRL = const(0x22) +# _TARANGE = const(0x23) +# _EMISS = const(0x24) +# _CONFIG = const(0x25) +# _ADDR = const(0x0E) +# _ID1 = const(0x3C) +# _ID2 = const(0x3D) +# _ID3 = const(0x3E) +# _ID4 = const(0x3F) + + def __init__(self, aSDA, aSCL): + '''aLoc is either 'X', 1, 'Y' or 2.''' + super(mlx, self).__init__() + + self.i2c = machine.I2C(scl = machine.Pin(aSCL), sda = machine.Pin(aSDA)) + self._w1 = bytearray(2) + + def read( self, aLoc ) : + '''Read 16 bit value and return.''' + self.i2c.readfrom_mem_into(_ADDRESS, aLoc, self._w1) + return (self._w1[1] << 8) | self._w1[0] + +# def write( self, aVal, aLoc ) : +# """Write 16 bit value to given address. aVal may be an int buffer.""" +# self.i2c.mem_write(aVal, _ADDRESS, aLoc, addr_size = 16) + + def readtemp( self, aLoc ) : + ''' ''' + temp = self.read(aLoc) + return (temp * 0.02) - 273.15 + + def temp( self ) : + return self.readtemp(_TA) + + def objecttemp( self ) : + return self.readtemp(_TOBJ1) + + def object2temp( self ) : + return self.readtemp(_TOBJ2) + + def display( self ) : + while True : + t1 = c2f(self.temp()) + t2 = c2f(self.objecttemp()) + t3 = c2f(self.object2temp()) + print("1: {} 2: {} 3: {} \r".format(t1, t2, t3), end = '') + time.sleep_ms(500) diff --git a/esp8266/oled.py b/esp8266/oled.py new file mode 100644 index 0000000..361bd7e --- /dev/null +++ b/esp8266/oled.py @@ -0,0 +1,303 @@ +#driver for the diymall 9.6 oled display. + +import machine + +_I2C_ADDRESS = const(0x3C) # 011110+SA0+RW - 0x3C or 0x3D + +_DISPLAYOFF = const(0xAE) +_DISPLAYON = const(0xAF) + +_SETLOWCOLUMN = const(0x00) +_SETHIGHCOLUMN = const(0x10) + +_SETSTARTLINE = const(0x40) + +#_ACTIVATE_SCROLL = const(0x2F) +#_DEACTIVATE_SCROLL = const(0x2E) +#_SET_VERTICAL_SCROLL_AREA = const(0xA3) +#_RIGHT_HORIZONTAL_SCROLL = const(0x26) +#_LEFT_HORIZONTAL_SCROLL = const(0x27) +#_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = const(0x29) +#_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = const(0x2A) + +#Buffer layout in bits. 128 columns by 64 rows. +#Each byte represents 8 pixels in a row. +# Column +# R 0 8 10 ... 3F8 +# O 1 9 11 ... 3F9 +# W 2 A 12 ... 3FA +# 3 B 13 ... 3FB +# 4 C 14 ... 3FC +# 5 D 15 ... 3FD +# 6 E 16 ... 3FE +# 7 F 17 ... 3FF +# 400 408 +# 401 409 +# 402 40A +# 403 40B +# 404 40C +# 405 40D +# 406 40E +# 407 40F + +class oled(object) : + """diyMall OLED 9.6 128x64 pixel display driver.""" + + def __init__( self, aSDA, aSCL ) : + """aLoc I2C pin location is either 1 for 'X' or 2 for 'Y'.""" + self._size = (128, 64) + self._rotation = 0 +# self._inverted = False + self._on = False + self.i2c = machine.I2C(scl = machine.Pin(aSCL), sda = machine.Pin(aSDA), freq = 200000) + self.bytes = self._size[0] * self._size[1] // 8 + self.buffer = bytearray(self.bytes + 1) + self.buffer[0] = 0x40 #data write start command at very start of buffer. + + self.data = bytearray(2) + self.data[0] = 0 + + self.command(_DISPLAYOFF) + self.command(0xD5) #_SETDISPLAYCLOCKDIV + self.command(0x80) #suggested ratio. + self.command(0xA8) #multiplex + self.command(0x3F) + self.command(0xD3) #_SETDISPLAYOFFSET + self.command(0x0) + self.command(_SETSTARTLINE) #| 0x0 + self.command(0x8D) #_CHARGEPUMP + self.command(0x14) #No external power. + self.command(0x20) #_MEMORYMODE + self.command(0x00) #Act like ks0108 + self.command(0xA1) #_SEGREMAP + 0x01 + self.command(0xC8) #_COMSCANDEC + self.command(0xDA) #_SETCOMPINS + self.command(0x12) + self.command(0x81) #_CONTRAST + self.command(0xCF) + self.command(0xD9) #_SETPRECHARGE + self.command(0xF1) + self.command(0xDB) #_SETVCOMDETECT + self.command(0x40) + self.command(0xA4) #_DISPLAYALLON_RESUME + self.command(0xA6) #_NORMALDISPLAY + self.command(0XB0) + self.command(0x10) + self.command(0x01) #Set original position to 0,0. + + self.on(True) + + self.display() + +# @micropython.native + def write( self, aValue ) : + self.i2c.writeto(_I2C_ADDRESS, aValue) + +# @micropython.native + def command( self, aValue ) : + self.data[1] = aValue + self.write(self.data) + +# @micropython.native + def on( self, aTF ) : + if aTF != self._on : + self._on = aTF + '''Turn display on or off.''' + self.command(_DISPLAYON if aTF else _DISPLAYOFF) + +# @property +# def invert( self ) : return self._inverted +# +# @invert.setter +# def invert( self, aTF ) : +# if aTF != self._inverted : +# self._inverted = aTF +# self.command(_INVERTDISPLAY if aTF else _NORMALDISPLAY) + +# @micropython.native + def fill( self, aValue ) : + for x in range(1, self.bytes + 1): + self.buffer[x] = aValue; + +# @micropython.native + def clear( self ) : + self.fill(0) + +# @micropython.native + def pixel( self, aPos, aOn ) : + '''Draw a pixel at the given position''' + x, y = aPos + w, h = self._size + if 0 <= x < w and 0 <= y < h: + if self._rotation == 1: + aPos = (w - y - 1, x) + elif self._rotation == 2: + aPos = (w - x - 1, h - y - 1) + elif self._rotation == 3: + aPos = (y, h - x - 1) + + bit = 1 << (aPos[1] % 8) + index = (aPos[0] + (aPos[1] // 8) * w) + 1 + + if aOn : + self.buffer[index] |= bit + else : + self.buffer[index] &= not bit + +# @micropython.native + def line( self, aStart, aEnd, aOn ) : + '''Draws a line from aStart to aEnd in the given color. Vertical or horizontal + lines are forwarded to vline and hline.''' + px, py = aStart + ex, ey = aEnd + dx = ex - px + dy = ey - py + inx = 1 if dx > 0 else -1 + iny = 1 if dy > 0 else -1 + + dx = abs(dx) + dy = abs(dy) + if (dx >= dy): + dy <<= 1 + e = dy - dx + dx <<= 1 + while (px != ex): + self.pixel((px, py), aOn) + if (e >= 0): + py += iny + e -= dx + e += dy + px += inx + else: + dx <<= 1 + e = dx - dy + dy <<= 1 + while (py != ey): + self.pixel((px, py), aOn) + if (e >= 0): + px += inx + e -= dy + e += dx + py += iny + +# @micropython.native + def fillrect( self, aStart, aSize, aOn ) : + '''Draw a filled rectangle. aStart is the smallest coordinate corner + and aSize is a tuple indicating width, height.''' + x, y = aStart + w, h = aSize + ex = x + w + for i in range(y, y + h): + self.line((x, i), (ex, i), aOn) + +# @micropython.native + def text( self, aPos, aString, aColor, aFont, aSize = 1 ) : + '''Draw a text at the given position. If the string reaches the end of the + display it is wrapped to aPos[0] on the next line. aSize may be an integer + which will size the font uniformly on w,h or a or any type that may be + indexed with [0] or [1].''' + + if aFont == None: + return + + #Make a size either from single value or 2 elements. + if (type(aSize) == int) or (type(aSize) == float): + wh = (aSize, aSize) + else: + wh = aSize + + px, py = aPos + width = wh[0] * aFont["Width"] + 1 + for c in aString: + self.char((px, py), c, aColor, aFont, wh) + px += width + #We check > rather than >= to let the right (blank) edge of the + # character print off the right of the screen. + if px + width > self._size[0]: + py += aFont["Height"] * wh[1] + 1 + px = aPos[0] + +# @micropython.native + def char( self, aPos, aChar, aOn, aFont, aSizes ) : + '''Draw a character at the given position using the given font and color. + aSizes is a tuple with x, y as integer scales indicating the + # of pixels to draw for each pixel in the character.''' + + if aFont == None: + return + + startchar = aFont['Start'] + endchar = aFont['End'] + + ci = ord(aChar) + if (startchar <= ci <= endchar): + fontw = aFont['Width'] + fonth = aFont['Height'] + ci = (ci - startchar) * fontw + + charA = aFont["Data"][ci:ci + fontw] + px = aPos[0] + if aSizes[0] <= 1 and aSizes[1] <= 1 : + for c in charA : + py = aPos[1] + for r in range(fonth) : + if c & 0x01 : + self.pixel((px, py), aOn) + py += 1 + c >>= 1 + px += 1 + else: + for c in charA : + py = aPos[1] + for r in range(fonth) : + if c & 0x01 : + self.fillrect((px, py), aSizes, aOn) + py += aSizes[1] + c >>= 1 + px += aSizes[0] + +# def doscrollLR( self, start, stop, aDir ) : +# self.command = aDir +# self.command = 0x00 +# self.command = start +# self.command = 0x00 +# self.command = stop +# self.command = 0x01 +# self.command = 0xFF +# self.command = _ACTIVATE_SCROLL +# +# def startscrollright( self, start, stop ) : +# self.doscrollLR(start, stop, _RIGHT_HORIZONTAL_SCROLL) +# +# def startscrollleft( self, start, stop ) : +# self.doscrollLR(start, stop, _LEFT_HORIZONTAL_SCROLL) +# +# def doscrollDiag( self, start, stop, aDir ) : +# self.command = _SET_VERTICAL_SCROLL_AREA +# self.command = 0x00 +# self.command = self.size()[1] +# self.command = aDir +# self.command = 0x00 +# self.command = start +# self.command = 0x00 +# self.command = stop +# self.command = 0x01 +# self.command = _ACTIVATE_SCROLL +# +# def startscrolldiagright( self, start, stop ) : +# self.doscrollDiag(start, stop, _VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL) +# +# def startscrolldiagleft( self, start, stop ) : +# self.doscrollDiag(start, stop, _VERTICAL_AND_LEFT_HORIZONTAL_SCROLL) +# +# def stopscroll( self ) : +# self.command = _DEACTIVATE_SCROLL + + def display( self ) : + self.command(_SETLOWCOLUMN) #| 0x00 + self.command(_SETHIGHCOLUMN) #| 0x00 + self.command(_SETSTARTLINE) #| 0x00 + #buffer starts with 0x40 in 1st byte which is the command to start the buffer write. + self.write(self.buffer) + + diff --git a/esp8266/seriffont.py b/esp8266/seriffont.py new file mode 100644 index 0000000..836d863 --- /dev/null +++ b/esp8266/seriffont.py @@ -0,0 +1,99 @@ + +seriffont = {"Width": 6, "Height": 8, "Start": 32, "End": 127, "Data": bytearray([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, #0x00, + 0x12, 0x3F, 0x12, 0x3F, 0x12, 0x00, #0x00, + 0x26, 0x7F, 0x32, 0x00, 0x00, 0x00, #0x00, + 0x13, 0x0B, 0x34, 0x32, 0x00, 0x00, #0x00, + 0x1A, 0x25, 0x1A, 0x28, 0x00, 0x00, #0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x7E, 0x81, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x08, 0x1C, 0x08, 0x00, 0x00, 0x00, #0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x1E, 0x21, 0x21, 0x1E, 0x00, 0x00, #0x00, + 0x02, 0x3F, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x32, 0x29, 0x29, 0x36, 0x00, 0x00, #0x00, + 0x12, 0x21, 0x25, 0x1A, 0x00, 0x00, #0x00, + 0x18, 0x16, 0x3F, 0x10, 0x00, 0x00, #0x00, + 0x27, 0x25, 0x19, 0x00, 0x00, 0x00, #0x00, + 0x1E, 0x25, 0x25, 0x18, 0x00, 0x00, #0x00, + 0x03, 0x39, 0x07, 0x00, 0x00, 0x00, #0x00, + 0x1A, 0x25, 0x25, 0x1A, 0x00, 0x00, #0x00, + 0x06, 0x29, 0x29, 0x1E, 0x00, 0x00, #0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, #0x00, + 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, #0x00, + 0x22, 0x14, 0x08, 0x00, 0x00, 0x00, #0x00, + 0x02, 0x29, 0x05, 0x02, 0x00, 0x00, #0x00, + 0x1C, 0x22, 0x49, 0x55, 0x59, 0x12, #0x0C, + 0x30, 0x2C, 0x0B, 0x0B, 0x2C, 0x30, #0x00, + 0x21, 0x3F, 0x25, 0x25, 0x1A, 0x00, #0x00, + 0x1E, 0x21, 0x21, 0x21, 0x13, 0x00, #0x00, + 0x21, 0x3F, 0x21, 0x21, 0x1E, 0x00, #0x00, + 0x21, 0x3F, 0x25, 0x33, 0x00, 0x00, #0x00, + 0x21, 0x3F, 0x25, 0x03, 0x00, 0x00, #0x00, + 0x1E, 0x21, 0x21, 0x29, 0x3B, 0x00, #0x00, + 0x3F, 0x04, 0x04, 0x3F, 0x00, 0x00, #0x00, + 0x3F, 0x21, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x21, 0x1F, 0x01, 0x00, 0x00, 0x00, #0x00, + 0x21, 0x3F, 0x0C, 0x33, 0x21, 0x00, #0x00, + 0x3F, 0x21, 0x30, 0x00, 0x00, 0x00, #0x00, + 0x21, 0x3F, 0x0C, 0x30, 0x0C, 0x3F, #0x21, + 0x3F, 0x03, 0x0C, 0x3F, 0x01, 0x00, #0x00, + 0x1E, 0x21, 0x21, 0x21, 0x1E, 0x00, #0x00, + 0x3F, 0x29, 0x06, 0x00, 0x00, 0x00, #0x00, + 0x1E, 0x21, 0x21, 0x61, 0x1E, 0x00, #0x00, + 0x21, 0x3F, 0x09, 0x36, 0x00, 0x00, #0x00, + 0x32, 0x25, 0x25, 0x1B, 0x00, 0x00, #0x00, + 0x01, 0x3F, 0x01, 0x00, 0x00, 0x00, #0x00, + 0x1F, 0x21, 0x20, 0x21, 0x1F, 0x00, #0x00, + 0x03, 0x0D, 0x30, 0x30, 0x0D, 0x03, #0x00, + 0x0F, 0x31, 0x0C, 0x0C, 0x31, 0x0F, #0x00, + 0x21, 0x33, 0x0C, 0x0C, 0x33, 0x21, #0x00, + 0x03, 0x24, 0x38, 0x24, 0x03, 0x01, #0x00, + 0x29, 0x25, 0x33, 0x00, 0x00, 0x00, #0x00, + 0x7F, 0x41, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x41, 0x7F, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, #0x00, + 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, #0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x14, 0x24, 0x38, 0x00, 0x00, 0x00, #0x00, + 0x3F, 0x28, 0x24, 0x18, 0x00, 0x00, #0x00, + 0x18, 0x24, 0x24, 0x00, 0x00, 0x00, #0x00, + 0x18, 0x24, 0x25, 0x3F, 0x00, 0x00, #0x00, + 0x18, 0x24, 0x28, 0x00, 0x00, 0x00, #0x00, + 0x3E, 0x25, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x18, 0xA4, 0xA4, 0x7C, 0x00, 0x00, #0x00, + 0x3F, 0x04, 0x38, 0x00, 0x00, 0x00, #0x00, + 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x3F, 0x18, 0x24, 0x00, 0x00, 0x00, #0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x3C, 0x04, 0x38, 0x04, 0x38, 0x00, #0x00, + 0x3C, 0x04, 0x38, 0x00, 0x00, 0x00, #0x00, + 0x18, 0x24, 0x24, 0x18, 0x00, 0x00, #0x00, + 0xFC, 0xA4, 0x24, 0x18, 0x00, 0x00, #0x00, + 0x18, 0x24, 0xA4, 0xFC, 0x00, 0x00, #0x00, + 0x3C, 0x04, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, #0x00, + 0x1E, 0x24, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x1C, 0x20, 0x3C, 0x00, 0x00, 0x00, #0x00, + 0x0C, 0x30, 0x0C, 0x00, 0x00, 0x00, #0x00, + 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x00, #0x00, + 0x24, 0x18, 0x24, 0x00, 0x00, 0x00, #0x00, + 0x9C, 0x60, 0x1C, 0x00, 0x00, 0x00, #0x00, + 0x34, 0x24, 0x2C, 0x00, 0x00, 0x00, #0x00, + 0x08, 0x77, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x77, 0x08, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, #0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, #0x00 +])}