# test of printing multiple fonts to the ILI9341 on an M5Stack using H/W SP # MIT License; Copyright (c) 2017 Jeffrey N. Magee from ili934xnew import ILI9341, color565 from machine import Pin, SPI,Timer, UART,PWM from machine import Counter from ST7735 import TFT,TFTColor import m5stack import tt14 import glcdfont import tt14 import tt24 import tt32 import uasyncio import time import math from rotary_irq_esp import RotaryIRQ fonts = [glcdfont,tt14,tt24,tt32] from sysfont import sysfont import json from pid import PID COILS = 14 # from enum import Enum class SpinnyBoy(object): def __init__(self,state,timer1,timer2,display): self.state = state self.timer1 = timer1 self.timer2 = timer2 self.display = display def splash(): # display.fill(0) # display.fill_rect(0, 0, 32, 32, 1) # display.fill_rect(2, 2, 28, 28, 0) # display.vline(9, 8, 22, 1) # display.vline(16, 2, 22, 1) # display.vline(23, 8, 22, 1) # display.fill_rect(26, 24, 2, 4, 1) # display.text("MicroPython", 40, 0, 1) # display.text("SSD1306", 40, 12, 1) # display.text("OLED 128x64", 40, 24, 1) #display.fill(0) # display.text("SPIN", 34, 4, 1) # display.text("COATER", 50, 14, 1) # display.print("Spinner") # display.print("COATER") # tft.text((0,y),"Spin Coater",TFT.RED,sysfont,1,nowrap=True) display.print("Spincoater",size=1,nowrap=True) display.print("Spincoater",size=3,nowrap=True) # #display.show() def fafo(): with open('fafogreen.bmp','rb') as f: if f.read(2) == b'BM': #header print("BMP@") dummy = f.read(8) #file size(4), creator bytes(4) offset = int.from_bytes(f.read(4), 'little') hdrsize = int.from_bytes(f.read(4), 'little') width = int.from_bytes(f.read(4), 'little') height = int.from_bytes(f.read(4), 'little') print("Image size:", width, "x", height) if int.from_bytes(f.read(2), 'little') == 1: #planes must be 1 depth = int.from_bytes(f.read(2), 'little') print("DEPTH",depth) if depth == 24 and int.from_bytes(f.read(4), 'little') == 0:#compress method == uncompressed print("Image size:", width, "x", height) rowsize = (width * 3 + 3) & ~3 if height < 0: height = -height flip = False else: flip = True w, h = width, height if w > 128: w = 128 if h > 160: h = 160 tft._setwindowloc((0,0),(w - 1,h - 1)) for row in range(h): if flip: pos = offset + (height - 1 - row) * rowsize else: pos = offset + row * rowsize if f.tell() != pos: dummy = f.seek(pos) for col in range(w): bgr = f.read(3) tft._pushcolor(TFTColor(bgr[2],bgr[1],bgr[0])) time.sleep(5) def tftprinttest(): tft.fill(TFT.BLACK) v = 30 tft.text((0, v), "Hello World!", TFT.RED, sysfont, 1, nowrap=True) v += sysfont["Height"] tft.text((0, v), "Hello World!", TFT.YELLOW, sysfont, 2, nowrap=True) v += sysfont["Height"] * 2 tft.text((0, v), "Hello World!", TFT.GREEN, sysfont, 3, nowrap=True) v += sysfont["Height"] * 3 tft.text((0, v), str(1234.567), TFT.BLUE, sysfont, 4, nowrap=True) time.sleep_ms(100) tft.fill(TFT.BLACK) v = 0 tft.text((0, v), "Hello World!", TFT.RED, sysfont) v += sysfont["Height"] tft.text((0, v), str(math.pi), TFT.GREEN, sysfont) v += sysfont["Height"] tft.text((0, v), " Want pi?", TFT.GREEN, sysfont) v += sysfont["Height"] * 2 tft.text((0, v), hex(8675309), TFT.GREEN, sysfont) v += sysfont["Height"] tft.text((0, v), " Print HEX!", TFT.GREEN, sysfont) v += sysfont["Height"] * 2 tft.text((0, v), "Sketch has been", TFT.WHITE, sysfont) v += sysfont["Height"] tft.text((0, v), "running for: ", TFT.WHITE, sysfont) v += sysfont["Height"] tft.text((0, v), str(time.ticks_ms() / 1000), TFT.PURPLE, sysfont) v += sysfont["Height"] tft.text((0, v), " seconds.", TFT.WHITE, sysfont) def start_view(state, rotary): display.set_pos(0,0) text="Spin Coater" display.print(text,size=2) #display.set_font(tt14) before = "> " if rotary.value() == 0 else " " display.print(before + "Edit") before = "> " if rotary.value() == 1 else " " display.print(before + "Start") #print(rotary.value()) def draw_edit_menu(state, rotary): display.print("Deposit speed:") display.print("{: >{w}} RPM".format(config["deposit_rpm"], w=5)) display.print("Coating speed:") display.print("{: >{w}} RPM".format(config["coating_rpm"], w=5)) display.print("Coating time:") display.print("{: >{w}} sec".format(config["coating_time"],w=2)) display.reset_pos() def edit_deposit_view(state, rotary): config["deposit_rpm"] = rotary.value() * 100 draw_edit_menu(state, rotary) def edit_coating_rpm_view(state, rotary): config["coating_rpm"] = rotary.value() * 100 draw_edit_menu(state, rotary) def edit_coating_time_view(state, rotary): config["coating_time"] = rotary.value() draw_edit_menu(state, rotary) def draw_rpm(rpm): display.print("RPM:{: >{w}.0f}".format(rpm, w=5)) def deposit_view(state, rotary): # display.fill_rect(0, 0, 127, 14, 1) display.print("Deposit") draw_rpm(state["rpm"]) display.print("Press to") display.print("continue") def coating_view(state, rotary): # display.fill_rect() display.print("Coating") draw_rpm(state["rpm"]) display.print("{: >{w}} sec".format(state["timer"], w=4)) #def decode_ESC_telemetry(data, motor_poles=14): # if len(data) > 10: # # use latest telemetry # data = data[-10:] # # temperature = int(data[0]) # degrees Celsius # voltage = int((data[1] << 8) | data[2]) * 0.01 # Volt # current = ( # int((data[3] << 8) | data[4]) * 0.01 # ) # Amps, only available if the ESC has a current meter # consumption = int( # (data[5] << 8) | data[6] # ) # mAh, only available if the ESC has a current meter # erpm = int((data[7] << 8) | data[8]) * 100 # rpm = erpm / (motor_poles / 2) # crc = data[9] # # print(" Temp (C):", temperature) # print(" Voltage (V):", voltage) # print(" Current (A):", current) # print("Consumption (mAh):", consumption) # print(" Erpm:", erpm) # print(" RPM:", rpm) # print(" CRC:", crc) # print() # # return temperature, voltage, current, consumption, erpm, rpm async def update_display(): global state global rotary while True: display.print("LOLOLOL") state["view"](state, rotary) await uasyncio.sleep_ms(200) async def update_motor(): global state #we want to use 18 for the SPI-Bus! #https://docs.micropython.org/en/latest/esp32/quickref.html#hardware-spi-bus # dshot = Dshot(pin=Pin(25)) # rpm_pid = PID( # Kp=config["PID"]["Kp"], # Ki=config["PID"]["Ki"], # Kd=config["PID"]["Kd"], # setpoint=0, # sample_time=None, # output_limits=(0.0, 1.0), # # proportional_on_measurement=True, # ) # while True: # rpm_pid.setpoint = state["target_rpm"] # # read ESC telemetry # if uart.any() >= 10: # telemetry = decode_ESC_telemetry(uart.read()) # if telemetry is not None: # state["rpm"] = telemetry[5] # throttle = rpm_pid(state["rpm"]) # print( # "Throttle:", # throttle, # "pid components:", # rpm_pid.components, # "RPM:", # state["rpm"], # ) # if state["target_rpm"] == 0 and state["rpm"] < 1000: # throttle = 0 # rpm_pid.reset() # dshot.set_throttle(throttle) # await uasyncio.sleep_ms(1) old_value=0 current_rpm=0 def timer_callback(f): global old_value global current_rpm val = counter.value() current_rpm = val-old_value old_value = val # print(current_rpm) def kill_me(f): state["target_rpm"] = 0 def update_motor_pwm(): global timer3 global current_rpm pwm = PWM(Pin(26)) rpm_pid = PID( Kp=config["PID"]["Kp"], Ki=config["PID"]["Ki"], Kd=config["PID"]["Kd"], setpoint=0, sample_time=None, output_limits=(0.0, 1.0), # proportional_on_measurement=True, ) pwm.freq(50) timer3.init(mode=Timer.PERIODIC, period=100, callback=timer_callback) pwm.duty_ns(1055*10**3) time.sleep(1) print((current_rpm*60)/COILS) pwm.duty_ns(1055*10**3) #pwm.duty_ns(1200*10**3) #global rpm_counter #rpm_counter= 0 #time.sleep(10) # time.sleep(10) # keep low signal to arm # # for i in range(1060,1800): # # pwm.duty_u16(i*3) # # time.sleep(1) # pwm.duty_ns(1070*10**3) # pwm.duty_u16(1700) # time.sleep(200) pwm.duty_ns(1060*10**3) state["target_rpm"] = 5000 timer2.init(mode=Timer.PERIODIC, period=10000, callback=kill_me) while(True): print(f'target RPM:{state["target_rpm"]}') rpm_pid.setpoint = state["target_rpm"] # read ESC telemetry telemetry = (current_rpm*600)/COILS if telemetry is not None: state["rpm"] = telemetry throttle = rpm_pid(state["rpm"]) print( "Throttle:", throttle, "pid components:", rpm_pid.components, "RPM:", state["rpm"], ) if state["target_rpm"] == 0: print("Killing Coater!") throttle = 0 rpm_pid.reset() #pwm.duty_ns(1800*10**3) #time.sleep(3) #return print(throttle) if throttle !=0: new_duty = 1060+(int(660*throttle)) pwm.duty_ns(new_duty*10**3) else: new_duty = 1055 pwm.duty_ns(new_duty*10**3) timer2.deinit() break def set_throttle(pwm,us): hertz = 50 #we now have to calculate the duty_cycle! # Period defined as 1/Hertz, we need to multiply by 1_000_000 to calculate to microseconds period = (1/hertz)*1000000 # us = max(1060, min(1860, us)) #our thing has to be one for x microseconds during the period! duty_cycle_percentage = (us/period)*100 duty = int((duty_cycle_percentage/100))*65535 print(duty) pwm.duty_u16(duty) def debounce_button(p): p.irq(trigger=Pin.IRQ_FALLING, handler=None) # remove irq timer0 = Timer(0) timer0.init(period=20, mode=Timer.ONE_SHOT, callback=lambda t: on_button_press(p)) def on_button_press(p): p.irq(trigger=Pin.IRQ_FALLING, handler=debounce_button) # restore irq if p.value() == 1: # debounce return global state global config global rotary if state["view"] == start_view: if rotary.value() == 0: state["view"] = edit_deposit_view rotary.set( min_val=0, range_mode=RotaryIRQ.RANGE_BOUNDED, value=int(0.01 * config["deposit_rpm"]), ) return if rotary.value() == 1: state["view"] = deposit_view state["target_rpm"] = config["deposit_rpm"] return if state["view"] == edit_deposit_view: state["view"] = edit_coating_rpm_view rotary.set( min_val=0, max_val=1000, range_mode=RotaryIRQ.RANGE_BOUNDED, value=int(0.01 * config["coating_rpm"]), ) return if state["view"] == edit_coating_rpm_view: state["view"] = edit_coating_time_view rotary.set( max_val=1000, min_val=0, max_val=9999, range_mode=RotaryIRQ.RANGE_BOUNDED, value=config["coating_time"], ) return if state["view"] == edit_coating_time_view: save_config() rotary.set(min_val=0, max_val=1, range_mode=RotaryIRQ.RANGE_BOUNDED, value=0) state["view"] = start_view if state["view"] == deposit_view: return state["view"] = coating_view start_coating(state) return if state["view"] == coating_view: stop_coating() return def start_coating(state): global timer1 global timer2 state["timer"] = config["coating_time"] timer1.init( period=config["coating_time"] * 1000, mode=Timer.ONE_SHOT, callback=lambda t: stop_coating(), ) def decrement_timer(t): state["timer"] -= 1 timer2.init(period=1000, mode=Timer.PERIODIC, callback=decrement_timer) # state["throttle"] = 0.10 state["target_rpm"] = config["coating_rpm"] def stop_coating(): global state global rotary global timer1 global timer2 timer1.deinit() timer2.deinit() state["target_rpm"] = 0 rotary.set(min_val=0, max_val=1, range_mode=RotaryIRQ.RANGE_BOUNDED, value=0) state["view"] = start_view def save_config(): global config with open("config.json", "w") as f: json.dump(config, f) power = Pin(m5stack.TFT_LED_PIN, Pin.OUT) power.value(1) timer1 = Timer(1) timer2 = Timer(2) counter = Counter(0,Pin(19,Pin.IN)) timer3 = Timer(3) # No need to change the software. It's just a matter of different names.. Use this translation: # SDO(MISO) # LED BL # SCK CLK/SCK # SDI(MOSI) DIN/SDA # DC D/C (A0) # RESET RST # CS CS # GND GND # VCC VCC spi = SPI( 2, baudrate=40000000, miso=Pin(m5stack.TFT_MISO_PIN), mosi=Pin(m5stack.TFT_MOSI_PIN), sck=Pin(m5stack.TFT_CLK_PIN)) # display = ILI9341( # spi, # cs=Pin(m5stack.TFT_CS_PIN), # dc=Pin(m5stack.TFT_DC_PIN), # rst=Pin(m5stack.TFT_RST_PIN), # w=160, # h=128, # r=5) tft = TFT(spi,m5stack.TFT_DC_PIN,m5stack.TFT_RST_PIN,m5stack.TFT_CS_PIN) display = tft tft.initr() tft.rgb(True) #tftprinttest() rotary = RotaryIRQ( pin_num_clk=25, pin_num_dt=13, min_val=0, max_val=1, range_mode=RotaryIRQ.RANGE_BOUNDED, pull_up=True, ) button = Pin(15, Pin.IN, Pin.PULL_UP) button.irq(trigger=Pin.IRQ_FALLING, handler=on_button_press) #interrupt for rpm sensor! state = { "view": start_view, "rpm": 0, "target_rpm": 0, "timer": 0, "rotary_val":-1 } with open("config.json", "r") as f: config = json.load(f) #display.erase() #display.set_pos(0,0) #display.set_font(tt32) tft.fill(TFT.BLACK) # splash() fafo() # for ff in fonts: # display.set_font(ff) # display.print(text) update_motor_pwm() event_loop = uasyncio.get_event_loop() # event_loop.create_task(update_display()) # #event_loop.create_task(update_motor_pwm()) # event_loop.run_forever()