让榨汁机定时工作(C#+PLC)

简介:

买了自加热的榨汁机每天补充营养是件好事,但是为此早起一个小时却划不来。如果为了节省时间,早上用微波炉加热昨晚做好的豆汁,口感却不怎么好。怎么办?买定时加热的榨汁机,估计价钱会很高,不过市面上好像也没有带这种功能的。

正好这段时间对硬件比较感兴趣,所以抽时间用西门子PLC224实现了该功能(一个PLC一两千元,用PLC控制好像有点高射炮打蚊子--大材小用,建议最好用单片机或.Net Micro Framework实现,这样成本会很低)。

基本思路:

1、由于PLC外部没有显示和控制接口,所以需要在PC机上编写一个程序,用来设定定时时间和间隔。此外由于PLC的时钟精度较低,长时间运行偏差较大,所以还得提供一个校时功能。

2、PLC程序相对比较简单,只要用当前时间和设定时间进行比较,时间到,则Q0.0输出信号,由此驱动继电器工作,过了时间间隔,则停止输出。

3、PC和PLC通信部分,由于PLC原生支持PPI协议,可以采用我以前编写的西门子PPI控件进行访问。当然也可以采用Modbus Rtu模式进行通信,不过需要PLC程序添加Modbus Rtu Slave库,这样增大了PLC程序空间,由于Modbus协议为公开协议,可以在PC上自行编写Modbus Rtu读写程序,不过也可以采用我编写的Modbus Rtu控件进行通信控制。

实际接线图如下:

 

 

PLC程序如下(语句表)

TITLE=榨汁机控制程序|[叶帆工作室]http://yfsoft.blog.51cto.com

Network 1

// 初始化

LD     SM0.1

MOVB   16#55, VB101                //复位初始状态

Network 2

// 设定日期

LDB=   VB100, 16#AA

MOVB   16#55, VB100

//VB110 年 VB111 月 VB112 日 VB113 时 VB114 分 VB115 秒 VB117 星期

TODW   VB110                       //设置时钟

Network 3

// 读取日期(1s刷新一次)

LD     SM0.5

EU

TODR   VB120                       //读取时钟

Network 4

// 判断是否开始输出

LDB=   16#55, VB101                //没有输出

AB=    VB123, VB130                //时

AB=    VB124, VB131                //分

AB=    VB125, VB132                //秒

EU

S      Q0.0, 1                     //Q0.0输出

MOVB   16#AA, VB101                //置位状态

Network 5

// 判断是否停止输出

LDB=   16#AA, VB101                //没有输出

AB=    VB123, VB140                //时

AB=    VB124, VB141                //分

AB=    VB125, VB142                //秒

EU

R      Q0.0, 1                     //Q0.0输出

MOVB   16#55, VB101                //复位状态

 

PC程序运行后的界面:

 

 

 

相关代码如下:


 
 
  1. using System;  
  2.  
  3. using System.Collections.Generic;  
  4.  
  5. using System.ComponentModel;  
  6.  
  7. using System.Data;  
  8.  
  9. using System.Drawing;  
  10.  
  11. using System.Text;  
  12.  
  13. using System.Windows.Forms;  
  14.  
  15. using System.Text.RegularExpressions;  
  16.  
  17.    
  18.  
  19. namespace PPI_Test  
  20.  
  21. {  
  22.  
  23.     public partial class frmMain : Form  
  24.  
  25.     {  
  26.  
  27.         public frmMain()  
  28.  
  29.         {  
  30.  
  31.             InitializeComponent();  
  32.  
  33.         }  
  34.  
  35.    
  36.  
  37.         private void frmMain_Load(object sender, EventArgs e)  
  38.  
  39.         {  
  40.  
  41.             //"×××公司"    '已注册的公司名称  
  42.  
  43.             axS7_PPI1.InitRegCompany("叶帆测试");              
  44.  
  45.             axS7_PPI1.bps = PPIV2.PPIBps.mb9600;  
  46.  
  47.             axS7_PPI1.CheckOut = PPIV2.PPICheckOut.mbEven;      
  48.  
  49.             if (axS7_PPI1.OpenPort(1, 2, 1024, 512) != 0)  
  50.  
  51.             {  
  52.  
  53.                 MessageBox.Show("打开串口失败!");  
  54.  
  55.             }  
  56.  
  57.         }  
  58.  
  59.    
  60.  
  61.         private void frmMain_FormClosed(object sender, FormClosedEventArgs e)  
  62.  
  63.         {  
  64.  
  65.             axS7_PPI1.ClosePort();  
  66.  
  67.         }  
  68.  
  69.           
  70.  
  71.         /// <summary>  
  72.  
  73.         /// 登录  
  74.  
  75.         /// </summary>  
  76.  
  77.         /// <param name="sender"></param>  
  78.  
  79.         /// <param name="e"></param>  
  80.  
  81.         private void btnLogin_Click(object sender, EventArgs e)  
  82.  
  83.         {  
  84.  
  85.             if (axS7_PPI1.PlcLogin(byte.Parse(txtFixAddr.Text)) == 0)  
  86.  
  87.             {  
  88.  
  89.                 txtFixAddr.BackColor = Color.Green;  
  90.  
  91.             }  
  92.  
  93.             else 
  94.  
  95.             {  
  96.  
  97.                 txtFixAddr.BackColor = Color.Red;  
  98.  
  99.             }  
  100.  
  101.         }  
  102.  
  103.    
  104.  
  105.         //运行  
  106.  
  107.         private void btnRun_Click(object sender, EventArgs e)  
  108.  
  109.         {  
  110.  
  111.             int intAddr = int.Parse(txtFixAddr.Text);  
  112.  
  113.    
  114.  
  115.             long lngRet = axS7_PPI1.PlcRun(intAddr);  
  116.  
  117.             if (lngRet == 0)  
  118.  
  119.             {  
  120.  
  121.                 MessageBox.Show("开始运行!");  
  122.  
  123.             }  
  124.  
  125.             else if (lngRet == 4)  
  126.  
  127.             {  
  128.  
  129.                 MessageBox.Show("PLC拨码开关在停止位置!");  
  130.  
  131.             }  
  132.  
  133.             else 
  134.  
  135.             {  
  136.  
  137.                 MessageBox.Show("操作失败!");  
  138.  
  139.             }  
  140.  
  141.         }  
  142.  
  143.    
  144.  
  145.         //停止  
  146.  
  147.         private void btnStop_Click(object sender, EventArgs e)  
  148.  
  149.         {  
  150.  
  151.             int intAddr = int.Parse(txtFixAddr.Text);  
  152.  
  153.             long lngRet = axS7_PPI1.PlcStop(intAddr);  
  154.  
  155.             if (lngRet == 0)  
  156.  
  157.             {  
  158.  
  159.                 MessageBox.Show("停止运行!");  
  160.  
  161.             }  
  162.  
  163.             else 
  164.  
  165.             {  
  166.  
  167.                 MessageBox.Show("操作失败!");  
  168.  
  169.             }  
  170.  
  171.         }  
  172.  
  173.    
  174.  
  175.         //读取日期  
  176.  
  177.         private void btnGetDate_Click(object sender, EventArgs e)  
  178.  
  179.         {  
  180.  
  181.             int intAddr = int.Parse(txtFixAddr.Text);  
  182.  
  183.             object vData = new object();  
  184.  
  185.    
  186.  
  187.             if (axS7_PPI1.ReadData(120, ref vData, 6, PPIV2.PPILEN.PPI_B, PPIV2.PPITYPE.PPI_V, intAddr) == 0)  
  188.  
  189.             {  
  190.  
  191.                 Int32[] intData = (Int32[])vData;  
  192.  
  193.                 lblDate.Text ="20"+ intData[0].ToString("X2") + "-" + intData[1].ToString("X2") + "-" + intData[2].ToString("X2") + " " +  
  194.  
  195.                                intData[3].ToString("X2") + ":" + intData[4].ToString("X2") + ":" + intData[5].ToString("X2");  
  196.  
  197.             }  
  198.  
  199.             else 
  200.  
  201.             {  
  202.  
  203.                 lblDate.Text = "读日期错!";  
  204.  
  205.             }  
  206.  
  207.         }  
  208.  
  209.         private void btnSetDate_Click(object sender, EventArgs e)  
  210.  
  211.         {  
  212.  
  213.             int intAddr = int.Parse(txtFixAddr.Text);  
  214.  
  215.             Int32[] intData = new Int32[8];  
  216.  
  217.             DateTime dt = DateTime.Now.AddSeconds(1);  
  218.  
  219.             intData[0] = Convert.ToInt32("0x" + (dt.Year - 2000).ToString(), 16);  
  220.  
  221.             intData[1] = Convert.ToInt32("0x" + dt.Month.ToString(), 16);  
  222.  
  223.             intData[2] = Convert.ToInt32("0x" + dt.Day.ToString(), 16);  
  224.  
  225.             intData[3] = Convert.ToInt32("0x" + dt.Hour.ToString(), 16);  
  226.  
  227.             intData[4] = Convert.ToInt32("0x" + dt.Minute.ToString(), 16);  
  228.  
  229.             intData[5] = Convert.ToInt32("0x" + dt.Second.ToString(), 16);  
  230.  
  231.             intData[7] = (int)dt.DayOfWeek;  
  232.  
  233.    
  234.  
  235.             //写日期时间  
  236.  
  237.             if (axS7_PPI1.WriteData(110, intData, 8, PPIV2.PPILEN.PPI_B, PPIV2.PPITYPE.PPI_V, intAddr) != 0)  
  238.  
  239.             {  
  240.  
  241.                 lblDate.Text = "设置日期错!";  
  242.  
  243.                 return;  
  244.  
  245.             }             
  246.  
  247.    
  248.  
  249.             //写设置标志  
  250.  
  251.             intData[0] = 0xAA;  
  252.  
  253.             if (axS7_PPI1.WriteData(100, intData, 1, PPIV2.PPILEN.PPI_B, PPIV2.PPITYPE.PPI_V, intAddr) != 0)  
  254.  
  255.             {  
  256.  
  257.                 lblDate.Text = "设置标志错!";  
  258.  
  259.             }  
  260.  
  261.         }  
  262.  
  263.    
  264.  
  265.         private void btnConfig_Click(object sender, EventArgs e)  
  266.  
  267.         {  
  268.  
  269.            if (!Regex.IsMatch(txtTimeStart.Text, @"^(0?([0-9])|1[0-9]|2[0-3]):(0?([0-9])|[1-5][0-9]):(0?([0-9])|[1-5][0-9])$"))  
  270.  
  271.            {  
  272.  
  273.                MessageBox.Show("时间格式不匹配,正确格式为:HH:MM:SS");  
  274.  
  275.                return;  
  276.  
  277.            }             
  278.  
  279.            if (!Regex.IsMatch(txtSpan.Text, @"^[^0]\d?\d?$"))  
  280.  
  281.            {  
  282.  
  283.                MessageBox.Show("时间间隔不正确,范围:1-999分钟");  
  284.  
  285.                return;  
  286.  
  287.            }                
  288.  
  289.            DateTime dt = DateTime.Parse(txtTimeStart.Text);  
  290.  
  291.            int intAddr = int.Parse(txtFixAddr.Text);  
  292.  
  293.            Int32[] intData = new Int32[3];  
  294.  
  295.    
  296.  
  297.            //写开始时间  
  298.  
  299.            intData[0] = Convert.ToInt32("0x" + dt.Hour.ToString(), 16);  
  300.  
  301.            intData[1] = Convert.ToInt32("0x" + dt.Minute.ToString(), 16);  
  302.  
  303.            intData[2] = Convert.ToInt32("0x" + dt.Second.ToString(), 16);  
  304.  
  305.            if (axS7_PPI1.WriteData(130, intData,3, PPIV2.PPILEN.PPI_B, PPIV2.PPITYPE.PPI_V, intAddr) != 0)  
  306.  
  307.            {  
  308.  
  309.                lblDate.Text = "写开始时间错!";  
  310.  
  311.                return;  
  312.  
  313.            }           
  314.  
  315.            //写停止时间  
  316.  
  317.            dt = dt.AddMinutes(int.Parse(txtSpan.Text));  
  318.  
  319.            intData[0] = Convert.ToInt32("0x" + dt.Hour.ToString(), 16);  
  320.  
  321.            intData[1] = Convert.ToInt32("0x" + dt.Minute.ToString(), 16);  
  322.  
  323.            intData[2] = Convert.ToInt32("0x" + dt.Second.ToString(), 16);  
  324.  
  325.            if (axS7_PPI1.WriteData(140, intData, 3, PPIV2.PPILEN.PPI_B, PPIV2.PPITYPE.PPI_V, intAddr) != 0)  
  326.  
  327.            {  
  328.  
  329.                lblDate.Text = "写停止时间错!";  
  330.  
  331.                return;  
  332.  
  333.            }    
  334.  
  335.         }  
  336.  
  337.     }  
  338.  
  339. }  
  340.  

当然这只是一个初级应用,如果我们扩展一下,用GPRS技术(参见我写的文章:让智能手机和居家电脑互联互通(WM6 GPRS)),我们可以用手机远程操控榨汁机工作,这样我们就可以在下班前让榨汁机工作。不过这得需要有一台能上网的电脑,编一个TCP服务程序,来接收手机发出的命令。这样PLC程序其实可以不用编写了,我们直接用西门子PPI控件操作PLC的Q0.0。当然如果系统中加入了PC,这样PLC似乎就可以免了,我们可以用串口的RTS管脚去驱动5v的继电器,由继电器来驱动榨汁机工作。

注:由于榨汁机并不是接通电源就可以工作(因这一点没有提前考虑到,差点让我的控制计划流产),所以我用了一个小窍门,先用一个小东西预先按在所需要的按钮上(参见第一张图上的黄色方块),这样一上电,榨汁机就可以正常工作了。

 








本文转自yefanqiu51CTO博客,原文链接:http://blog.51cto.com/yfsoft/323890,如需转载请自行联系原作者

相关文章
|
6月前
|
JavaScript C#
【傻瓜级JS-DLL-WINCC-PLC交互】7.​C#直连PLC并读取PLC数据
【傻瓜级JS-DLL-WINCC-PLC交互】7.​C#直连PLC并读取PLC数据
263 0
艾伟_转载:让榨汁机定时工作(C#+PLC)
买了自加热的榨汁机每天补充营养是件好事,但是为此早起一个小时却划不来。如果为了节省时间,早上用微波炉加热昨晚做好的豆汁,口感却不怎么好。怎么办?买定时加热的榨汁机,估计价钱会很高,不过市面上好像也没有带这种功能的。
1123 0
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
188 3
|
6月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
196 3
|
5天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
16 3
|
26天前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
|
2月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
38 2
|
2月前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
45 1
|
2月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
70 11