maasier/src/main.py

385 lines
10 KiB
Python

# 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
from ST7735 import TFT
import m5stack
import tt14
import glcdfont
import tt14
import tt24
import tt32
import uasyncio
from rotary_irq_esp import RotaryIRQ
fonts = [glcdfont,tt14,tt24,tt32]
from sysfont import sysfont
import json
from enum import Enum
class EditMenu(Enum):
DEPOSIT = 1
COATING = 2
TIME = 3
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 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(1500)
# 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.text("Deposit")
draw_rpm(state["rpm"])
display.text("Press to")
display.text("continue")
def coating_view(state, rotary):
# display.fill_rect()
display.text("Coating")
draw_rpm(state["rpm"])
display.text("{: >{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.fill(color565(0,0,0))
state["view"](state, rotary)
# display.show()
await uasyncio.sleep_ms(33)
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(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,
)
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)
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,
max_val=1000,
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(
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
return
if state["view"] == deposit_view:
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)
text = 'Now is the time for all good men to come to the aid of the party.'
power = Pin(m5stack.TFT_LED_PIN, Pin.OUT)
power.value(1)
timer1 = Timer(1)
timer2 = Timer(2)
# No need to change the software. It's just a matter of different names.. Use this translation:
# SDO(MISO) <not used>
# LED BL
# SCK CLK/SCK
# SDI(MOSI) DIN/SDA
# DC D/C (A0)
# RESET RST
# CS CS
# GND GND
# VCC VCC
print("BOOOOOOTTT2T")
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)
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()
# for ff in fonts:
# display.set_font(ff)
# display.print(text)
event_loop = uasyncio.get_event_loop()
event_loop.create_task(update_display())
#event_loop.create_task(update_motor())
event_loop.run_forever()