import time
from machine import Pin, ADC, Timer, PWM
import btree
from time import sleep_ms, sleep, sleep_us
configs={
"flow":20, # 泵的流速
'speed':10, # xyz的移动
"b_pumps":50,
"v":10,
"k_sn":3,
"k_chem":5,
"conti":0,
"contr":0,
"Z":1,
"LENGTH":1,
"X0":10,
"Y0":10,
'status':4, # 1暂停,2停止,3运行,4设置
'reset': False, # 如果为True,则限位开关需要退出仪器;如果为False,则限位开关定位
'waring':[],
'signal':[],
'protection':1, # 1 流速不能设置太高,要根据VH确定;2 运行时不能调整流速
}
Pins={
"X_limit":33, # X限位
"Y_limit":32, # Y限位
"Z_limit":100, # Z限位
"XY_limit":100, # XY限位
"X_EN":100, # X使能
"X_STEP":100, # X步进
"X_DIR":100, # X方向
"Y_EN":100, # Y使能
"Y_STEP":100, # Y步进
"Y_DIR":100, # Y方向
"Z_EN":100, # Z使能
"Z_STEP":100, # Z步进
"Z_DIR":100, # Z方向
"RACK_1":36, # 试管架I
"RACK_2":39, # 试管架II
"CLK":13, # 旋转编码器
"DT":12, # 旋转编码器
"SW":14, # 旋转编码器开关
"UV":21, # 光信号检测
"DET":35, # 光信号检测
"BEEPER":100, # 蜂鸣器
"LEAK":2, # 漏液检测
"BLANK":22, # 空管检测
"I2S_BCK":25, # HC595
"I2S_WS":26,
"I2S_DATA":27,
"SPI_DET":34, # SD卡
"SPI_CS" :5,
"SPI_CS" :18,
"SPI_MISO" :19,
"SPI_MOSI" :23,
"X_limit":33, # X限位
"Y_limit":32, # Y限位
"Z_limit":22, # Z限位
"XY_limit":100, # XY限位
"X_EN":100, # X使能
"X_STEP":100, # X步进
"X_DIR":100, # X方向
"Y_EN":100, # Y使能
"Y_STEP":100, # Y步进
"Y_DIR":100, # Y方向
"Z_EN":100, # Z使能
"Z_STEP":100, # Z步进
"Z_DIR":100, # Z方向
"RACK_1":36, # 试管架I
"RACK_2":39, # 试管架II
"CLK":13, # 旋转编码器
"DT":15, # 旋转编码器
"SW":4, # 旋转编码器开关
"UV":1, # 光信号检测
"DET":35, # 光信号检测
"BEEPER":21, # 蜂鸣器
"LEAK":16, # 漏液检测
"BLANK":17, # 空管检测
"I2S_BCK":25, # HC595
"I2S_WS":26,
"I2S_DATA":27,
"SPI_DET":34, # SD卡
"SPI_CS" :5,
"SPI_CS" :18,
"SPI_MISO" :19,
"SPI_MOSI" :23,
}
def tube_id():
for i in range(1000):
try:
print('读取试管架编号')
rack1 = Pin(Pins("RACK_1"), Pin.IN, Pin.PULL_UP)
rack2 = Pin(Pins("RACK_2"), Pin.IN, Pin.PULL_UP)
if rack1.value==1 and rack2.value==1:
return 1
elif rack1.value==1 and rack2.value==0:
return 2
elif rack1.value==0 and rack2.value==1:
return 3
elif rack1.value==0 and rack2.value==0:
return 4
except Exception as e:
print('读取失败,设置默认值1')
return 1
class Signal(object):
# 'X_EN','X_STEP','X_DIR','Y_EN','Y_STEP','Y_DIR','Z_EN','Z_STEP',
# 'Z_DIR','E0_EN','E0_STEP','E0_DIR','E1_EN','E1_STEP','E1_DIR','NC',
# 'UV_1','UV_2','UV_3','FAN1','FAN2','BEEPER','LED_CONTR','LED_CONTINU'
def __init__(self, HC_list=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], RackID=1, contr=0, conti=0):
RackDict = {
1 : [20, 10, 1.5, 1.5],
2 : [20, 10, 2.0, 2.0],
3 : [40, 20, 1.0, 1.0],
4 : [80, 40, 0.5, 0.5],}
self.HC_list = HC_list
self.RackID = RackID
self.now_ID = 0
# contr空白溶液归0, conti时next归1
self.contr = contr
self.conti = conti
self.ratio = 0
self.racklist=[]
for i in range(RackDict[self.RackID][0]):
for j in range(RackDict[self.RackID][1]):
self.racklist.append([i*RackDict[self.RackID][2], j*RackDict[self.RackID][3]])
# 控制HC595
# IO27 >>> 14脚 >>> 输入 >>> DS
# IO26 >>> 12脚 >>> 输出 >>> ST_CP
# IO25 >>> 11脚 >>> 移位 >>> SH_CP
def __send(self):
# DS=Pins["motor_data"]
# ST_CP=Pins["motor_ws"]
# SH_CP=Pins["motor_bck"]
DS=Pins["I2S_DATA"]
ST_CP=Pins["I2S_WS"]
SH_CP=Pins["I2S_BCK"]
ds=Pin(DS, Pin.OUT); ds.value(0)
st=Pin(ST_CP, Pin.OUT); st.value(0)
sh=Pin(SH_CP, Pin.OUT); sh.value(0)
for i in self.HC_list:
ds.value(i)
sh.value(1)
sh.value(0)
st.value(0)
st.value(1)
# print(self.HC_list)
# 泵开关
def en_switch(self, xyz, en):
if xyz == "X":
self.HC_list[0] = en
elif xyz == "Y":
self.HC_list[3] = en
elif xyz == "Z":
self.HC_list[6] = en
self.__send()
# EN:0, STEP:1, DIR:2
def x(self, speed=configs['speed'], length=100):
# 开泵
self.HC_list[0] = 0
# 控制方向
if length>0:
self.HC_list[2]=1
else:
self.HC_list[2]=0
# 运行泵
for i in range(0, abs(length)):
self.HC_list[1]=(1-self.HC_list[1])
time.sleep(1/speed)
self.__send()
# 关泵
self.HC_list[0] = 1
print('x:',length)
# EN:3, STEP:4, DIR:5
def y(self, speed=configs['speed'], length=100):
# 开泵
self.HC_list[3] = 0
# 控制方向
if length>0:
self.HC_list[5]=1
else:
self.HC_list[5]=0
# 运行泵
for i in range(0, abs(length)):
self.HC_list[4]=(1-self.HC_list[4])
time.sleep(1/speed)
self.__send()
# 关泵
self.HC_list[3] = 1
print('y:',length)
# EN:6, STEP:7, DIR:8
def z(self, speed=configs['speed']*2, length=100):
# 开泵
self.HC_list[6] = 0
# 控制方向
if length>0:
self.HC_list[8]=1
else:
self.HC_list[8]=0
# 运行泵
for i in range(0, abs(length)):
self.HC_list[7]=(1-self.HC_list[7])
time.sleep(1/speed)
self.__send()
# 关泵
# self.HC_list[6] = 1
# P1 >> EN:9, STEP:10, DIR:11
# P2 >> EN:12, STEP:13, DIR:14
# 仅click一下
def pump(self, speed=configs['flow'], B=50, i=100000000000000000):
# self.ratio用于分配比例;i达到加速度效果
# speed必须大于1,可以在1/speed处加入其他参数
# speed = min(speed, i/3) # 这行最慢
if speed > i/3: # 替换min
speed = i/3+1
self.ratio += 1
if self.ratio > 100:
self.ratio = 0
if self.ratio < B: # B%100,可以避免判读是否大于100,但是这样较慢
self.HC_list[10]=1
time.sleep(1/speed)
self.__send()
self.HC_list[10]=0
time.sleep(1/speed)
self.__send()
else:
self.HC_list[13]=1
time.sleep(1/speed)
self.__send()
self.HC_list[13]=0
time.sleep(1/speed)
self.__send()
# x, y为移动距离;m, n 为试管架坐标
# racklist=[[0, 0], [0, 2], [0, 4], [0, 6], [0, 8], [0, 10], [0, 12], [0, 14], [0, 16], [0, 18], [2, 0], [2, 2], [2, 4], [2, 6], [2, 8], [2, 10], [2, 12], [2, 14], [2, 16], [2, 18], [4, 0], [4, 2], [4, 4], [4, 6], [4, 8], [4, 10], [4, 12], [4, 14], [4, 16], [4, 18], [6, 0], [6, 2], [6, 4], [6, 6], [6, 8], [6, 10], [6, 12], [6, 14], [6, 16], [6, 18], [8, 0], [8, 2], [8, 4], [8, 6], [8, 8], [8, 10], [8, 12], [8, 14], [8, 16], [8, 18], [10, 0], [10, 2], [10, 4], [10, 6], [10, 8], [10, 10], [10, 12], [10, 14], [10, 16], [10, 18], [12, 0], [12, 2], [12, 4], [12, 6], [12, 8], [12, 10], [12, 12], [12, 14], [12, 16], [12, 18], [14, 0], [14, 2], [14, 4], [14, 6], [14, 8], [14, 10], [14, 12], [14, 14], [14, 16], [14, 18], [16, 0], [16, 2], [16, 4], [16, 6], [16, 8], [16, 10], [16, 12], [16, 14], [16, 16], [16, 18], [18, 0], [18, 2], [18, 4], [18, 6], [18, 8], [18, 10], [18, 12], [18, 14], [18, 16], [18, 18]]
# 0号为废液管,最后一管(或1号)为定量管
def move(self, to_ID):
print(0)
if self.now_ID == len(self.racklist)-1:
print(1)
if self.conti == 1:
print(2)
to_ID = 0
self.now_ID-=1
else:
print(3)
return False
# print(1)
self.x(length=self.racklist[to_ID][0]-self.racklist[self.now_ID][0])
self.y(length=self.racklist[to_ID][1]-self.racklist[self.now_ID][1])
self.now_ID=to_ID
def next(self):
self.move(self.now_ID+1) # 在move时,将now_ID+1赋值给now_ID
def home(self):
self.move(0)
# pass
# 'UV或状态灯','contr','conti','FAN1','FAN2','BEEPER','OUT_1','OUT_2'
# 16 17 18 19 20 21 22 23
# def uv(self, led):
# self.HC_list[16]=led
# self.__send()
def settings(self, led):
self.HC_list[16]=led
self.__send()
def contr_led(self, led):
self.HC_list[17]=led
self.__send()
def conti_led(self, led):
self.HC_list[18]=led
self.__send()
def blink(self,ID ="contr", num=3):
if ID == "contr":
n = 17
elif ID == 'conti':
n = 18
elif ID == 'settings':
n = 16
for i in range(num):
self.HC_list[n]=1
self.__send()
time.sleep(0.5)
self.HC_list[n]=0
self.__send()
time.sleep(0.5)
def fan1(self,run):
self.HC_list[19]=run
self.__send()
def fan2(self,run):
self.HC_list[20]=run
self.__send()
def beeper(self, nums):
for i in range(0, nums):
self.HC_list[21]=1
self.__send()
time.sleep(1)
self.HC_list[21]=0
self.__send()
a=tube_id()
p=Signal(RackID=a, contr=0, conti=1)
# print('测试电扇1和2')
# p.fan2(1)
# p.fan1(1)
# print('测试信号灯')
# p.settings(1)
# p.contr_led(1)
# p.conti_led(1)
# sleep(2)
# p.settings(0)
# p.contr_led(0)
# p.conti_led(0)
# print('测试信号灯闪烁')
# p.blink('contr')
# p.blink('conti', 4)
# p.blink('settings', 5)
# print('测试蜂鸣器')
# p.beeper(2)
# print('测试XYZ')
# # 确定参数length具体值
# p.x(length=10)
# p.en_switch(xyz="X", en=1)
# p.en_switch("X", 1)
# p.y(length=10)
# p.en_switch(xyz="Y", en=1)
# p.z(length=10)
# p.en_switch(xyz="Z", en=1)
# print('测试移动')
# p.next()
# for i in range(0,100):
# p.next()
# p.home()
# 确定参数speed具体值
# 确定循环数与体积的关系
# 判断速度对体积没有影响(1/2)
# print('测试泵1')
# for i in range(0,200):
# print(i)
# p.pump(speed=100, B=50)
# print('测试泵2')
# for i in range(0,200):
# print(i)
# p.pump(speed=100, B=100)
# print('测试泵3')
# for i in range(0,200):
# print(i)
# p.pump(speed=100, B=0)
# 判断速度对体积没有影响(2/2)
# print('测试泵加速')
# for i in range(1,200):
# print(i)
# p.pump(speed=100, B=50, i=i)
print('测试信号')
photo_ADC = ADC(Pin(Pins["DET"])) # ADC6复用管脚为GP34
photo_ADC.atten(ADC.ATTN_0DB) # 11dB 衰减, 最大输入电压约3.6v
chemical_status =0 # 是否有物质
# 判断参数具体指
uv0=4095 # 初始值
k0=0 # 初始值
uv_signal=4 # 无物质斜率
uv_k=4 # 突跃斜率
def check_uv():
global chemical_status # 存放在列表里
global uv0
global k0
uv1=photo_ADC.read()
k1=uv1-uv0
##################当吸光度弱时(优先)###############
# 灵敏度值uv_signal
# 斜率uv_k
# 0:无化合物 abs(k1)<uv_signal, uv1>3000
# 1:有物质且增加 abs(k1)>uv_signal, k1<0
# 2:有物质且减少 abs(k1)>uv_signal, k1>0
# 3:【拐点1】物质A减少、物质B增多:abs(k1)>uv_signal, k0>0, k1<0(0的斜率未必合适)
# 4:【拐点2】没有物质,变成有物质:chemical_status=0, abs(k1)>uv_signal
# 5:【拐点3】 k1/(k0+1.1)>5(斜率uv_k)
###################################################
# if abs(k1)<uv_signal:
if abs(k1)<uv_signal or uv1>3800:
chemical_status = 0
if chemical_status == 0 and abs(k1)>uv_signal:
chemical_status = 4
elif k1/(k0+1.1)>uv_k: # 1.1防止分母出现0
chemical_status = 5
elif abs(k1)>uv_signal and k0>0 and k1<0:
chemical_status = 3
uv0 = uv1
k0 = k1
print(chemical_status, k1)
if chemical_status>2:
return True, uv0
else:
return False, uv0
# while True:
# if check_uv()[0] is True:
# print('---下一个----')
# p.beeper(1)
print('测试外部中断')
# Pin.IRQ_FALLING(下降沿触发)、Pin.IRQ_RISING(上升沿触发)、Pin.IRQ_LOW_LEVEL(低电平触发)、Pin.IRQ_HIGH_LEVEL(高电平触发)。
def leak_warning(leak):
configs['status']==1
p.beeper(5)
print('漏液')
def blank_warning(blank): # 导致无法开启
configs['status']==1
p.beeper(3)
print('空液')
leak=Pin(Pins['LEAK'], Pin.IN, Pin.PULL_UP) ; leak.irq(leak_warning,Pin.IRQ_FALLING)
blank=Pin(Pins['BLANK'], Pin.IN, Pin.PULL_UP) ; blank.irq(blank_warning,Pin.IRQ_FALLING)
def xlimit(x_limit):
p.en_switch('X', 1)
p.beeper(1)
if configs['reset'] is True:
p.beeper(2)
print('X限位开关异常')
configs['status']=1
def ylimit(y_limit):
p.en_switch('Y', 1)
p.beeper(1)
if configs['reset'] is True:
p.beeper(2)
print('Y限位开关异常')
configs['status']=1
x_limit=Pin(Pins['X_limit'], Pin.IN, Pin.PULL_UP) ; x_limit.irq(xlimit,Pin.IRQ_FALLING)
y_limit=Pin(Pins['Y_limit'], Pin.IN, Pin.PULL_UP) ; y_limit.irq(ylimit,Pin.IRQ_FALLING)
# 长按缓慢开启泵排气(此处不适用)
# 单击开启/暂停
# 双击停止
clk = Pin(Pins["CLK"],Pin.IN, Pin.PULL_UP) # 旋转编码器CLK管脚,设置为输入模式
dt = Pin(Pins["DT"],Pin.IN, Pin.PULL_UP) # 旋转编码器DT管脚,设置为输入模式
sw = Pin(Pins["SW"],Pin.IN, Pin.PULL_UP) # 设置按钮管脚为输入模式
key_up_flag = 0
key_state = 0 # 按键状态
state = 0 # 2长按、3单击、4双击
def button_down(sw):
# 按键按下消抖
if sw.value()==0:
sleep_ms(50)
if sw.value()==0:
key_state=0
else:
sleep_ms(50)
if sw.value()==1:
key_state=1
key_up_flag=0
# 这里主要防止,按键在识别长按后,又会执行一遍单击操作
if state==0 and key_state==0 and key_up_flag==0:
state=1
time0=time.time()
if state==1:
time1=time.time()
time2=time1-time # 计算按键按下时长
if time2>50: # 长按判断
state=2
if key_state==1: # 按键弹起
state=3
time0=time.time() # 标志什么时间按键弹起的
if state==2: # 长按:进入设置状态
state=0 # 重置状态
key_up_flag=1
key_return=3
configs['status'] = 4 # 调整流速
p.beeper(4)
if state==3 and key_up_flag==0: # 单击:暂停/开始
time1=time.time()
time2=time1-time0 # 计算按键弹起后时间
if time2>6: # 判断按键弹起后的时间,超过300ms,则说明为单击
state=0
key_return=1
if configs['status'] == 2:
configs['status'] = 3
p.beeper(3)
elif configs['status'] == 3:
configs['status'] = 2
p.beeper(2)
elif key_state==0: # 双击:停止运行(按键弹起后,300ms内又有按键按下)
state=4
configs['status'] = 1 # 停止
p.beeper(1)
if state==4:
if key_state==1: # 按键弹起
state=0
key_return=2
def rotary(dt):
if (configs['status'] == 4) or (configs['protection'] != 1):
Last_Status = dt.value()
while(not clk.value()): # 判断CLK管脚的电平变化来区分方向
sleep_us(1)
Current_Status = dt.value()
flag = 1 # 发生旋转标记
if flag == 1: # 标记位为1 发生了旋转
flag = 0 # 复位标记位
if (Last_Status == 0) and (Current_Status == 1):
configs['flow'] += 0.1 # 逆时针方向,正
if (Last_Status == 1) and (Current_Status == 0):
configs['flow'] += -0.1 # 顺时针方向,负
if configs['flow']<0.1:
configs['flow']=0
elif configs['flow']>10:
configs['flow']=10.0
print(configs['flow'])
sw.irq(button_down, Pin.IRQ_FALLING) # 下降沿
dt.irq(rotary, Pin.IRQ_FALLING)