From 2160dcacfd8b2cd3ba7a4b0658c972a448a4d606 Mon Sep 17 00:00:00 2001 From: androiddrew Date: Mon, 23 Dec 2019 00:36:13 -0500 Subject: [PATCH] Adding DHT and OLED projects --- basics/README.md | 2 + basics/boot.py | 5 +- neo_pixel_DHT/README.md | 0 neo_pixel_DHT/boot.py | 28 ++++++++ neo_pixel_DHT/main.py | 49 ++++++++++++++ ssd1306_oled/README.md | 33 +++++++++ ssd1306_oled/main.py | 66 ++++++++++++++++++ ssd1306_oled/ssd1306.py | 145 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 neo_pixel_DHT/README.md create mode 100644 neo_pixel_DHT/boot.py create mode 100644 neo_pixel_DHT/main.py create mode 100644 ssd1306_oled/README.md create mode 100644 ssd1306_oled/main.py create mode 100644 ssd1306_oled/ssd1306.py diff --git a/basics/README.md b/basics/README.md index f39cc28..a487ffe 100644 --- a/basics/README.md +++ b/basics/README.md @@ -53,3 +53,5 @@ To resume a detach session use: ``` screen -r ``` + +To kill a screen session press `Ctrl-A` then `k`. You will be prompted to confirm with `y\n` whether to kill the session or not. diff --git a/basics/boot.py b/basics/boot.py index df56c8f..76821dd 100644 --- a/basics/boot.py +++ b/basics/boot.py @@ -15,7 +15,7 @@ def connect(): if not sta_if.isconnected(): print("connecting to wireless network....") sta_if.active(True) - sta_if.connect(b"YOUR_WIRELESS_SSID", b"YOUR_WIRELESS_PASSWORD") + sta_if.connect(b"Candy", b"whatisdelcious") while not sta_if.isconnected(): pass print("network config:", sta_if.ifconfig()) @@ -23,6 +23,3 @@ def connect(): # def no_debug(): # import esp # esp.osdebug(None) - - -connect() diff --git a/neo_pixel_DHT/README.md b/neo_pixel_DHT/README.md new file mode 100644 index 0000000..e69de29 diff --git a/neo_pixel_DHT/boot.py b/neo_pixel_DHT/boot.py new file mode 100644 index 0000000..902bf00 --- /dev/null +++ b/neo_pixel_DHT/boot.py @@ -0,0 +1,28 @@ +# This file is executed on every boot (including wake-boot from deepsleep) +import esp32 +esp32.osdebug(None) +import gc + +# import webrepl +# webrepl.start() +gc.collect() + + +def connect(): + import network + + sta_if = network.WLAN(network.STA_IF) + if not sta_if.isconnected(): + print("connecting to wireless network....") + sta_if.active(True) + sta_if.connect(b"YOUR_WIRELESS_SSID", b"YOUR_WIRELESS_PASSWORD") + while not sta_if.isconnected(): + pass + print("network config:", sta_if.ifconfig()) + + # def no_debug(): + # import esp + # esp.osdebug(None) + + +connect() diff --git a/neo_pixel_DHT/main.py b/neo_pixel_DHT/main.py new file mode 100644 index 0000000..44c4838 --- /dev/null +++ b/neo_pixel_DHT/main.py @@ -0,0 +1,49 @@ +import time + +from dht import DHT11 +from machine import Pin +from neopixel import NeoPixel + +NP_PIN = NeoPixel(Pin(15), 2) +DHT_PIN = DHT11(Pin(5)) +RED = 0 +GREEN = 1 +BLUE = 2 + + +def init_neopixels(np_pin): + """Initializes NP array.""" + np_pin[0] = (128, 0, 0) + np_pin[1] = (0, 0, 128) + np_pin.write() + + +def blue_to_red(np_pin, index): + start = (0, 0, 56) + r, g, b = np_pin[index] + if b > 0: + r += 4 + b -= 4 + np_pin[index] = (r, g, b) + else: + np_pin[index] = start + np_pin.write() + + +def dht_report(dht_pin): + dht_pin.measure() + temp = dht_pin.temperature() + hum = dht_pin.humidity() + result = "Temp: {}, Humidity: {}".format(temp, hum) + return result + + +def main(): + init_neopixels(NP_PIN) + while True: + #print(dht_report(DHT_PIN)) + blue_to_red(NP_PIN, 0) + time.sleep_ms(100) + + +main() diff --git a/ssd1306_oled/README.md b/ssd1306_oled/README.md new file mode 100644 index 0000000..5ea9e00 --- /dev/null +++ b/ssd1306_oled/README.md @@ -0,0 +1,33 @@ +# SSD1306 OLED 128x64 Display + +The ESP32 has two Hardware supported [SPI Buses](https://docs.micropython.org/en/latest/esp32/quickref.html#hardware-spi-bus). This project makes use of the VSPI Bus(id = 2) to drive the [Adafruit ssd1306 OLED Display](https://www.adafruit.com/product/326). + +## PIN Outs + +| Channel | ESP32 | OLED | Comments | +|---------|-----------------|-----------|---------------------------------| +| MISO | | | Master in slave out. *Unused* | +| MOSI | D23 : GPIO23 | Data | Master out slave in. | +| SCK | D18 : GPIO18 | Clk | Clock. | +| DC | D4 : GPIO4 | DC | | +| CS | D5 : GPIO5 | CS | Chip select. | +| RST | D2 : GPIO2 | Rst | Reset. | +| V+ | 3v3 | Vin | | +| GND | GND | Gnd | | + +# Usage + +The `SSD1306_SPI` driver has a simple interface. + +To clear the screen: +``` +oled.fill(0) +oled.show() +``` + +To invert colors: +``` +oled.invert(True) +``` + + diff --git a/ssd1306_oled/main.py b/ssd1306_oled/main.py new file mode 100644 index 0000000..4386cca --- /dev/null +++ b/ssd1306_oled/main.py @@ -0,0 +1,66 @@ +from dht import DHT11 +from machine import Pin, SPI +from ssd1306 import SSD1306_SPI +from time import sleep + +# DHT Sensor +DHT_PIN = DHT11(Pin(26)) + +# OLD PINS +MISO_PIN = Pin(19) +MOSI_PIN = Pin(23) +SCK_PIN = Pin(18) +DC_PIN = Pin(4, Pin.OUT) +CS_PIN = Pin(5, Pin.OUT) +RST_PIN = Pin(2, Pin.OUT) + +# VSPI Hardware channel. 80 MHz signal rate +vspi = SPI(2, baudrate=2600000, polarity=0, phase=0, bits=8, firstbit=0, sck=SCK_PIN, mosi=MOSI_PIN, miso=MISO_PIN) + +oled = SSD1306_SPI(128, 64, vspi, DC_PIN, RST_PIN, CS_PIN) + + +class DisplayWriter: + + def __init__(self, spi): + self.spi = spi + self.height = spi.height + self.width = spi.width + + def msg(self, msg: str, l_offset=0, top_offset=0) -> None: + """Writes a message to an OLED display. + + Will wrap text as needed. + """ + self.spi.fill(0) + formatted_msg = self._split_msg(msg, l_offset, top_offset) + for s, left, top in formatted_msg: + self.spi.text(s, left, top) + self.spi.show() + + # Todo make this a generator + def _split_msg(self, msg, l_offset, top_offset) -> list: + """Splits a message at screen width to be used for text wrapping.""" + _top_offset = top_offset + result = [] + for e in msg.split('\n'): + result.append((e, l_offset, _top_offset)) + _top_offset += 10 + return result + + +def dht_report(dht_pin): + dht_pin.measure() + _temp = dht_pin.temperature() + _hum = dht_pin.humidity() + return _temp, _hum + + +display = DisplayWriter(oled) + +while True: + temp, hum = dht_report(DHT_PIN) + report = "Temp: {}C\nHum: {}%".format(temp, hum) + print(report) + display.msg(report) + sleep(1) diff --git a/ssd1306_oled/ssd1306.py b/ssd1306_oled/ssd1306.py new file mode 100644 index 0000000..78f06f0 --- /dev/null +++ b/ssd1306_oled/ssd1306.py @@ -0,0 +1,145 @@ +# MicroPython SSD1306 OLED driver, I2C and SPI interfaces +# https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py + +from micropython import const +import framebuf + +# register definitions +SET_CONTRAST = const(0x81) +SET_ENTIRE_ON = const(0xa4) +SET_NORM_INV = const(0xa6) +SET_DISP = const(0xae) +SET_MEM_ADDR = const(0x20) +SET_COL_ADDR = const(0x21) +SET_PAGE_ADDR = const(0x22) +SET_DISP_START_LINE = const(0x40) +SET_SEG_REMAP = const(0xa0) +SET_MUX_RATIO = const(0xa8) +SET_COM_OUT_DIR = const(0xc0) +SET_DISP_OFFSET = const(0xd3) +SET_COM_PIN_CFG = const(0xda) +SET_DISP_CLK_DIV = const(0xd5) +SET_PRECHARGE = const(0xd9) +SET_VCOM_DESEL = const(0xdb) +SET_CHARGE_PUMP = const(0x8d) + + +# Subclassing FrameBuffer provides support for graphics primitives +# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html +class SSD1306(framebuf.FrameBuffer): + def __init__(self, width, height, external_vcc): + self.width = width + self.height = height + self.external_vcc = external_vcc + self.pages = self.height // 8 + self.buffer = bytearray(self.pages * self.width) + super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) + self.init_display() + + def init_display(self): + for cmd in ( + SET_DISP | 0x00, # off + # address setting + SET_MEM_ADDR, 0x00, # horizontal + # resolution and layout + SET_DISP_START_LINE | 0x00, + SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 + SET_MUX_RATIO, self.height - 1, + SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 + SET_DISP_OFFSET, 0x00, + SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, + # timing and driving scheme + SET_DISP_CLK_DIV, 0x80, + SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, + SET_VCOM_DESEL, 0x30, # 0.83*Vcc + # display + SET_CONTRAST, 0xff, # maximum + SET_ENTIRE_ON, # output follows RAM contents + SET_NORM_INV, # not inverted + # charge pump + SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, + SET_DISP | 0x01): # on + self.write_cmd(cmd) + self.fill(0) + self.show() + + def poweroff(self): + self.write_cmd(SET_DISP | 0x00) + + def poweron(self): + self.write_cmd(SET_DISP | 0x01) + + def contrast(self, contrast): + self.write_cmd(SET_CONTRAST) + self.write_cmd(contrast) + + def invert(self, invert): + self.write_cmd(SET_NORM_INV | (invert & 1)) + + def show(self): + x0 = 0 + x1 = self.width - 1 + if self.width == 64: + # displays with width of 64 pixels are shifted by 32 + x0 += 32 + x1 += 32 + self.write_cmd(SET_COL_ADDR) + self.write_cmd(x0) + self.write_cmd(x1) + self.write_cmd(SET_PAGE_ADDR) + self.write_cmd(0) + self.write_cmd(self.pages - 1) + self.write_data(self.buffer) + + +class SSD1306_I2C(SSD1306): + def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): + self.i2c = i2c + self.addr = addr + self.temp = bytearray(2) + self.write_list = [b'\x40', None] # Co=0, D/C#=1 + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.temp[0] = 0x80 # Co=1, D/C#=0 + self.temp[1] = cmd + self.i2c.writeto(self.addr, self.temp) + + def write_data(self, buf): + self.write_list[1] = buf + self.i2c.writevto(self.addr, self.write_list) + + +class SSD1306_SPI(SSD1306): + def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): + self.rate = 10 * 1024 * 1024 + dc.init(dc.OUT, value=0) + res.init(res.OUT, value=0) + cs.init(cs.OUT, value=1) + self.spi = spi + self.dc = dc + self.res = res + self.cs = cs + import time + self.res(1) + time.sleep_ms(1) + self.res(0) + time.sleep_ms(10) + self.res(1) + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(0) + self.cs(0) + self.spi.write(bytearray([cmd])) + self.cs(1) + + def write_data(self, buf): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(1) + self.cs(0) + self.spi.write(buf) + self.cs(1)