PLC+Arduino实战笔记-02:绿键启动、红键停止,让LED松手也能亮
PLC+Arduino实战笔记-02:绿键启动、红键停止,让LED松手也能亮
PLC其实也是单片机,不过是叫工业单片机,而arduino是教育单片机。之所以放在一起,主要是好玩,打下兴趣的基础。而且arduino编程用的c语言和上位机用的C#同源,很多地方高度相似。本系列一反传统的先学梯形图,后学C#代码的方式,而是放在一起,两个同时进行。plc做主控制,arduino干细活的方式寓教于乐。方便有志于成为plc工程师和机器视觉工程师的人自学。

- 按一下PLC绿色按钮,Arduino外接LED亮。
- 松开绿色按钮,LED继续亮。
- 按下红色按钮,LED灭。
- 松开红色按钮,LED保持灭。
看起来就是一盏灯,但调试时,PLC梯形图、隔离板、Arduino引脚和程序逻辑,任何一处没对齐,结果都会不一样。
本来想轻松点灯,最后发现:灯很小,坑不少。
一、这次要做什么
这次的目标不是“按住亮、松手灭”,而是实现一个基本的启保停实验:

绿键:启动
松开绿键:保持运行
红键:停止
松开红键:保持停止
我使用西门子S7-200 SMART CR40作为主控,Arduino作为信号接收和显示端。
PLC按钮
↓
PLC梯形图:启动、保持、停止
↓
PLC输出Q1.5
↓
光耦隔离板
↓
Arduino D10读取信号
↓
Arduino D9控制外接LED
这里的分工很明确:
PLC决定是否运行,Arduino负责显示运行状态。
本次只做LED实验,不连接电机或其他危险执行机构。
二、地址和引脚分工
PLC侧
| 地址 | 本实验用途 | 已确认的状态 |
|---|---|---|
I0.0 | 绿色启动按钮 | 按下ON,松开OFF |
I0.1 | 红色停止按钮 | 松开ON,按下OFF |
M10.0 | 内部运行状态位 | 启动后保持,停止后清除 |
Q1.5 | 发送运行状态到隔离板 | 跟随M10.0 |
在我的软件里,输出符号名显示为CPU_输出13,对应的实际地址是Q1.5。符号名称和地址不是一回事,接线时要核对实际地址。3143
Arduino侧
| 引脚 | 用途 |
|---|---|
D10 | 读取隔离板输出O1 |
D9 | 控制外接LED |
GND | 连接隔离板输出侧GND及LED回路 |
外接裸LED采用下面的接法,并串联限流电阻:

Arduino D9 → 限流电阻 → LED正极
LED负极 → Arduino GND
隔离板O1接D10,不再把外接LED也挂在D10上。
PLC侧和Arduino侧按隔离板的端子定义分别接线,不跨接两侧公共端;具体电源、电平和极性,以模块资料及实际测量为准。
三、PLC启保停梯形图
网络1:保存运行状态



读这段逻辑,可以用一句话:
红键未按下,并且“绿键按下”或者“已经处于运行状态”,就让M10.0保持ON。
实际动作过程:
- 初始状态下,
M10.0为OFF。 - 按绿键,
I0.0变为ON,M10.0线圈接通。 M10.0的并联触点随之接通。- 松开绿键,上面的启动支路断开,但下面的保持支路仍接通。
- 按红键,
I0.1变为OFF,整个网络断开,M10.0变为OFF。
这里红键使用常开指令| |,是因为我的实际输入状态为“松开ON、按下OFF”。不要只凭按钮颜色或物理触点名称选择梯形图指令,要先确认输入状态。
网络2:驱动实际输出

M10.0
────────| |──────────────────────( Q1.5 )
M10.0负责保存状态,Q1.5负责把状态输出给隔离板。
所以这次并不是Arduino自己记住“灯要亮”,而是PLC保持运行输出,Arduino持续读取并显示。
四、Arduino程序
本次成功版本使用D10读取信号,D9控制外接LED。
const byte SIGNAL_PIN = 10; // 隔离板O1:只读输入
const byte LED_PIN = 9; // 外接LED:输出控制
void setup() {
pinMode(SIGNAL_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // 初始化时先关灯
Serial.begin(9600);
Serial.println("D10_INPUT_D9_LED_TEST");
}
void loop() {
int level = digitalRead(SIGNAL_PIN);
// 按之前的现象:绿色按钮按下时,D10为HIGH
bool ledOn = (level == LOW);
digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
Serial.print("D10=");
Serial.print(level == HIGH ? "HIGH" : "LOW");
Serial.print(" D9_LED=");
Serial.println(ledOn ? "ON" : "OFF");
delay(100);
}
这段代码没有自锁功能,只让外接LED跟随D10。
如果你的隔离模块输出极性相反,应先看串口和测量结果,再调整ledOn的判断条件;不能直接照搬本次HIGH亮的逻辑。
同样,代码中的LOW关灯,也以本帖所示的“D9→电阻→LED→GND”接法为前提。
五、今晚踩过的两个坑
坑1:LED接在输入引脚上
最初我把外接LED接在D10,但代码中:
pinMode(10, INPUT);
D10原本用于读取隔离板信号,程序控制的却是LED_BUILTIN。
结果是:我盯着外接LED看,程序控制的并不是它。
后来把两项任务分开:
D10:读信号
D9:控LED
这才把接线和程序对齐。
坑2:保持支路并联错了位置
最初的梯形图把M10.0并联在红键下面,而绿键仍串联在整个网络最前面:3143

所以绿键一松开,最前面的通路就断了。
当时的现象非常直接:
按绿键:PLC Q灯亮,Arduino LED亮
松开绿键:PLC Q灯灭,Arduino LED也灭
一直盯着Arduino改HIGH、LOW,并不能解决这个问题,因为PLC根本没保持输出。
把两个上方触点位置调整后,让保持支路绕过绿键,实验才成功。
今晚最重要的一句话:
自锁支路绕过启动按钮,停止按钮必须留在公共通路上。
六、验证与保存
最终按下面的顺序验证:
| 操作 | M10.0 | Q1.5 | Arduino外接LED |
|---|---|---|---|
| 按绿键 | ON | ON | 亮 |
| 松开绿键 | 保持ON | 保持ON | 继续亮 |
| 按红键 | OFF | OFF | 灭 |
| 松开红键 | 保持OFF | 保持OFF | 保持灭 |
这次排错最有效的动作,是先看PLC输出灯:
- Q灯松开就灭:先查PLC保持逻辑。
- Q灯保持,但Arduino灯不保持:再查隔离板、D10读数和LED输出。
另外,我也终于记牢了程序传输方向:
上传:PLC → 电脑,取回现有程序
下载:电脑 → PLC,写入修改后的程序
电脑上改完梯形图,要下载到PLC才会生效。成功后,把PLC工程、Arduino代码、接线照片和测试结果一起保存,下一次不用重新猜一遍。
这一课不复杂,但它让我真正分清了三件事:按钮输入是瞬时动作,M位保存运行状态,Q输出把状态送给外部设备。
按住灯亮是点动,松手还亮才是保持。




