输出设备


小组作业

个人作业

学习成果:

你是否:


使用的工具


使用的软件


文件

  1. Eagle 文件
  2. G 代码 文件
  3. 代码 文件
  4. 数据手册 文件

小组作业

去看小组作业页面。


简介

我开始这次作业,是想改进农场的灌溉自动化。我们已经有了一台 ESP8266 在控制一个水泵和 4 个继电器,并监测水泵的电流消耗。问题是 ESP8266 和它的程序反应不灵敏,这让设置时间很费时。这是由于 ADC 引脚数量有限。农场上的一位志愿者 Shakeel 向我解释过,ESP8266 用它唯一的 ADC 引脚来监测 WiFi 连接,所以他写了一个程序,减少电流表采样的次数,好把这个引脚和 WiFi 共用。好吧,这确实能用,但让 WiFi 连接变得不稳定,所以我的挑战将是试着弄懂 ESP32,以便移植他的程序,同时完成这次作业。试试看!

另外我想指出的是,我会更专注于代码,因为我已经在其他周演示过电路板制作过程。还有,我完成作业方式的一个重大变化是:我会边学边做边记录,所以这次作业会更像一份日志,而不是教程。


目标

我最初的目标是更好地理解 ESP32;然后,作为第二个目标,让它取代现在的灌溉控制;第三个目标是把这些知识应用到我孵化器的最终项目。


引脚定义

ESP32 引脚定义


代码计划

  1. 检查时间
  2. 如果处在时间范围内就打开水泵
  3. 读取电流表,查找欠流和过流;如果检测到,就停止水泵运行

代码研究

我先看了 techtutorialsx.com 这个网站。

正如页面上解释的,打开继电器的代码和我见过的其他 Arduino 代码很相似,那就来看看。

我会用 int pumprelayPin1 = 22; 把变量 pumprelayPin1 定义为引脚 22,它会把这个值存成整数。

pinMode(pumprelayPin1, OUTPUT); 把 pumprelayPin1 的引脚模式设为输出引脚,意思是它会主动输出,而不是像输入那样被动地监测信号。

digitalWrite(pumprelayPin1, LOW); 会用 LOW 把引脚设为关闭状态。

delay(4000); 会让程序等待 4 秒,也就是 4000 毫秒。

到目前为止我的代码是这样的:

int pumprelayPin1 = 22;

void setup() {
  
  pinMode(pumprelayPin1, OUTPUT);
  digitalWrite(pumprelayPin1, LOW);

}

void loop() {

  digitalWrite(pumprelayPin1, HIGH);
  delay(4000);
  digitalWrite(pumprelayPin1, LOW);
  delay(4000);

}

到目前为止,这段代码会启动,然后每隔 4 秒把继电器打开和关闭一次。

现在,第二部分,我想从实时时钟获取时间,以便在特定时间启动水泵。所以首先需要包含 RTC 时钟库。我用的是 DS1302 RTC 时钟芯片,所以这个库配上这段代码 #include <virtuabotixRTC.h> 应该就行了,根据库的文档在这里。

其次,我们需要用这段代码声明 RTC 时钟的引脚 virtuabotixRTC myRTC(5, 18, 19); 和 RtcDS1302<ThreeWire> Rtc(myWire);。这意味着当 ESP32 与实时时钟芯片通信时,它会用引脚 5 作为 CLK、18 作为 DAT、19 作为 RST。

警告: RtcDS1302<ThreeWire> Rtc(myWire); 来自另一个库(Makuna 的 Rtc 库,RtcDS1302.h),而 myWire 从未定义,所以这一行照原样是编译不过的。用这里使用的 virtuabotixRTC 库,virtuabotixRTC myRTC(5, 18, 19); 就够了。

现在,为了显示实时时钟的时间,我把它写在 void setup() {} 区域或 void loop() {} 区域里。前者只打印一次;后者会在我让它运行期间一直打印。当然,我们需要加上至少一分钟的延时,用 delay(60000);。变量的单位是毫秒,所以 1000 表示 1 秒,60000 表示 60 秒。

    Serial.begin(115200);

 myRTC.updateTime();
    Serial.println();

如果这个 RTC 以前从未使用过,你可以运行一次下面的代码来设置时间:myRTC.setDS1302Time(00, 28, 16, 7, 23, 5, 2020);,前几个数字依次是秒、分、时(24 小时制)、星期几、月中的某天、月、年。(后来更正:星期几从 1 = 星期日开始计数,所以 2020 年 5 月 23 日星期六是 7,2020 年 5 月 24 日星期日是 1;原来的代码两个都写成了 6。)

到目前为止代码是这样的:

#include <virtuabotixRTC.h>

virtuabotixRTC myRTC(5, 18, 19);// (CLK,DAT,RST)

int pumprelayPin1 = 22;

void setup() {

    Serial.begin(115200);
    pinMode(pumprelayPin1, OUTPUT);
}
void loop() {

    myRTC.updateTime();
  
    Serial.print(myRTC.hours);
    Serial.print(":");
    Serial.print(myRTC.minutes);
    delay(58000);
  
    digitalWrite(pumprelayPin1, HIGH);
    delay(1000);
    digitalWrite(pumprelayPin1, LOW);
    delay(1000);

}

到目前为止还没有逻辑测试:代码每分钟显示一次时间,并打开和关闭继电器。所以,为了加上逻辑测试,我们需要 if 语句。它会检查某个条件是否为真,然后执行程序里接下来的内容。

摘自 Arduino 参考文档:

if (condition) {
    //statement(s)
}

所以,为了检查时间并打开我的继电器,我会试着这样写:if ((myRTC.hours) == 15 && (myRTC.minutes) == 0) {digitalWrite(pumprelayPin1, HIGH);}

要关闭它,代码会是这样:if ((myRTC.hours) >= 15 && (myRTC.minutes) >= 1) {digitalWrite(pumprelayPin1, LOW);}

我的代码现在是这样:

//libraries
#include <virtuabotixRTC.h>

//Settings
virtuabotixRTC myRTC(5, 18, 19);// (CLK,DAT,RST)
int pumprelayPin1 = 22;


//run once
void setup() {

   Serial.begin(115200);
   
myRTC.setDS1302Time(50, 15, 16, 1, 24, 5, 2020); // sets seconds, minutes, hours (24h mode), day of the week, day, month, year) comment after first compile
  
  pinMode(pumprelayPin1, OUTPUT);
  
}

//run in loop
void loop() {
  myRTC.updateTime();//updates time
  Serial.print("Current Time: ");
  Serial.print(myRTC.hours);
  Serial.print(":");
  Serial.print(myRTC.minutes);
  Serial.print(":");
  Serial.print(myRTC.seconds);
  Serial.println();
  delay (1000);
//condition (logic test)
if ((myRTC.minutes) == 16) digitalWrite(pumprelayPin1, HIGH);  
if ((myRTC.minutes) >= 17) digitalWrite(pumprelayPin1, LOW);

}

这是它在 ESP32 开发板上的运行情况:

想以更高分辨率观看,请到 YouTube


板子设计

这块板我至少需要 4 个数字引脚,如果以后想测量水泵的电流,还需要一个模拟引脚。所以我认为 ATtiny44 就足够合适了。ESP32 我会留到网络周再用 ;-)。另外,我的 ESP32 SMD 元件还没到;我觉得是因为 COVID-19 导致供应链中断。所以我才用手头已有的 ESP32 开发板做实验。

ATtiny44 的引脚定义

ATtiny44 引脚定义

有了这个,我会修改我在输入设备周做的 tiny45 板,再加一个排针,方便编程和串口监视,并把所有引脚都引出来,让这块板尽可能模块化。

我不会详细讲 Eagle 设计或铣削,因为我在输入设备周已经做过了。我只讲我可能没用过的命令,比如 replace,然后放几张主图和测试。

那就从重新设计开始吧!

  1. 我先打开旧的 Eagle 文件,在这里。
  2. 我试了 replace 命令,但把 ATtiny45 换成 ATtiny44 没成功;我想它们差别太大了。所以我只能添加新元件,用 net 重新连接并命名,然后在板上重新布线。看看吧。

这些是板的原理图和布局:

Tiny44 原理图 原理图

Tiny44 电路板布局电路板布局

接下来,我运行 ULP pcb-gcode,就像输入设备里解释的那样。

G 代码隔离走线可视化G 代码隔离走线可视化

接下来,我用 Autoleveller 这个程序,也是在输入设备里解释过的,然后得到一份根据空白电路板的不平整做了调整的 G 代码。

Autoleveller 输出Autoleveller 输出

铣削电路板

铣好的电路板

铣好的电路板铣好的电路板

焊上元件后的 ATtiny44 板是这样的。

焊好元件的 ATtiny44 板焊好元件的板

现在电路板做好了,我改代码让它在 ATtiny44 上运行。一个值得注意的地方是,ATtiny44 需要软件串口库,我用这段代码引入它 #include <SoftwareSerial.h>。还有两件事让串口工作:库需要知道 RX 和 TX 是哪些引脚,SoftwareSerial Monitor(1, 0);;在 void setup() {} 里,我用 Monitor.begin(115200); 代替通常的 Serial.begin(115200); 来做串口监视器的初始化;最后,在 void loop() {} 里,每次我想让串口打印东西,都得用 Monitor.print(); 代替通常的 Serial.print();。

最后我的测试代码是这样的:

//libraries
#include <virtuabotixRTC.h>
#include <SoftwareSerial.h>

//Settings
virtuabotixRTC myRTC(3, 8, 7);// (CLK,DAT,RST)
int pumprelayPin1 = 2;
SoftwareSerial Monitor(1, 0);

//run once
void setup() {

   Monitor.begin(115200);
   
myRTC.setDS1302Time(50, 15, 16, 1, 24, 5, 2020); // sets seconds, minutes, hours (24h mode), day of the week, day, month, year) comment after first compile
  
  pinMode(pumprelayPin1, OUTPUT);
  
}

//run in loop
void loop() {
  myRTC.updateTime();//updates time
  Monitor.print("Current Time: ");
  Monitor.print(myRTC.hours);
  Monitor.print(":");
  Monitor.print(myRTC.minutes);
  Monitor.print(":");
  Monitor.print(myRTC.seconds);
  Monitor.println();
  delay (1000);
//condition (logic test)
if ((myRTC.minutes) == 16) digitalWrite(pumprelayPin1, HIGH);  
if ((myRTC.minutes) >= 17) digitalWrite(pumprelayPin1, LOW);

}

警告: 在跑内部 8 MHz 振荡器的 ATtiny44 上,115200 波特的 SoftwareSerial 不可靠(时序误差会让字符乱掉)。用 Monitor.begin(9600); 并把串口监视器设为 9600,这才是安全的速度。

现在来编程吧!想看不用 Arduino IDE 的另一种方法,请看嵌入式编程。

USBasp 编程器USBasp

用 USBasp 配合 Arduino 给 ATtiny44 板编程。

FabISP 编程器FabISP

用我做的 FabTinyISP 或 USBtinyISP 配合 Arduino 给 ATtiny44 板编程也没问题。唯一不方便的是得给目标板供电。

先打开 Arduino IDE,然后在 Tools 里选择板子,我这里是 ATtiny24/44/84。接下来在 Processor 里,我选择 ATtiny44。

Arduino IDE 选择板子

接下来,选择时钟。因为我没有用外部晶振,所以我选了“Internal 8MHz”。

Arduino IDE 选择时钟

接下来,到 Programmer:“USBasp”,或者 FabISP 用“USBtiny”。

Arduino IDE 选择编程器

下面的视频里,我展示用 FabISP 编程的过程。

想看全分辨率的视频,请看 YouTube

这个视频里,我展示用 USBasp 编程的过程。

想看全分辨率的视频,请看 YouTube

我可以展示 ATtiny44 控制一个继电器,在程序设定的特定时间打开一台压缩机。

想看全分辨率的视频,请看 YouTube

主图

板子的主图板子的主图


脚注

以后要看看 millis 的用法。

← (4月15日) 应用与影响 · 所有周 · 成型与铸造 (4月29日) →