UPS BP650CH实现nas自动关机

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

家里有个自己拼凑的nas需要防止断电不正常关机因此购买了施耐德后背式BP650CH之所以选这款是因为带了串口串口终究还是很方便的东西。不管linux还是window还是其他系统都能够使用通过串口直接获得ups的信息就不需要关心操作系统是什么了。

BP650CH的串口协议参考这篇文档是BP650CHBP1000CH串口通信协议.pdf-原创力文档 (book118.com)

默认波特兰24008bit数据1bit停止位无检验。SecureCRT设置如下

由于这个施耐德的UPS串口并非终端交互式的因此我们不适合用SecureCRT采用其他输入和输出分离的串口助手。

我使用Arduino IDE中的串口助手设置如下

按照手册首先需要登录到UPS方法就是输入M回车那么串口会返回一个V

接着输入QS回车这个命令是检索状态。注意施耐德的串口交互命令都是回车结尾。

反正QS命令返回的结果格式为

(是结果的开始然后后面的数值用空格分开

216.1=输入电压216.1伏特有效值rms

216.1=输入故障电压216.1伏特有效值rms

216.1=输出电压216.1伏特有效值rms

 000=输出负载因为我UPS没有接负载所以就是0单位是百分比

50.0=输出电压的频率是50Hz

13.5=电池电压13.5V有效值rms

 --.-=内部温度似乎没法显示内部温度

00001001=UPS状态字节b7~b0

b6为高位的话指示电池电压低

b4为高位指示UPS故障

 剩下的看手册

测试1关闭UPS的市电输入蜂鸣器10秒叫一次QS命令检索状态发现UPS的状态字节最高位变成了1这个时候就可以通知用户进行关机了

测试2使用破旧没有电池的笔记本电脑装linux系统做断电自动关机的测试。

        将USB转串口线插到电脑lsusb发现串口PL2303驱动

ls /dev/tty*发现串口设备ttyUSB0

安装minicom软件yum install minicom

然后minicom -s进入设置选择Serial port setup。然后设置如下

然后save setup as dfl下次就不用设置了

然后exit from minicom

接着输入minicom

输入M登录后输入QS就可以查询数据了

退出minicom方法ctrl+A后然后按Z然后按X然后yes

 但是minicom是交互式的因此我们需要自己写一个程序实现ups的状态扫描来实现关机

#include<stdio.h>  
#include<fcntl.h>  
#include<stdlib.h>  
#include<unistd.h>  
#include<termios.h>  
#include<string.h>  
#include<sys/select.h>  
int serialDevFD;//串口设备描述符
char *serialDev = "/dev/ttyUSB0";
void serialDevOpen()
{
    serialDevFD = open(serialDev,O_RDWR|O_NOCTTY|O_NDELAY);//读写方式打开串口设备后面两个属性不用管
    if(serialDevFD<0)
    {
        printf("Serial port device %s open failed!!!\n",serialDev);
        perror(serialDev);
        exit(1);
    }
    printf("Serial port device %s open success, file descriptor = %d\n",serialDev,serialDevFD);
}
void serialDevClose()
{
    close(serialDevFD);
    printf("Serial port device %s close success\n",serialDev);
}
//延时n秒
void sleepSecond(int n)
{
    sleep(n);
}
//串口通信参数配置
void serialDevCfg()
{
   struct termios newT;//新的配置信息结构体  
   memset(&newT,0,sizeof(newT));//结构体清零  
   cfsetispeed(&newT,B2400);//设置input/output波特率都是2400
   cfsetospeed(&newT,B2400);  
   //  
   newT.c_cflag |= (CLOCAL|CREAD);  
   newT.c_cflag &=~CSIZE;  
   //设置数据位8位  
   newT.c_cflag |= CS8;  
   //设置无校验  
   newT.c_cflag &= ~PARENB;  
   newT.c_iflag &= ~INPCK;  
   //设置停止位1位  
   newT.c_cflag &= ~CSTOPB;  
   //  
   newT.c_cc[VTIME]=0;  
   newT.c_cc[VMIN]=0;  
  
   tcflush(serialDevFD,TCIOFLUSH);  
   if(tcsetattr(serialDevFD,TCSANOW,&newT)!=0){  
    perror("set Baud failed");  
    exit(1);  
   }else {
   	printf("Serial port device %s set params success\n",serialDev);
   }
}

//登录和监控ups
char *UPS_LOGIN = "M\r";
char *UPS_STATUS = "QS\r";
void serialMonitor()
{
    fd_set rds;
	int	ret;
	int len;
	char buf;
	char arr[300];
	write(serialDevFD,UPS_LOGIN,2);//登录
	int loginIndex = 0;
    while(1)
	{
		FD_ZERO(&rds);
		FD_SET(serialDevFD,&rds);
		/*调用select检查是否能够从/dev/input/event0设备读取数据*/
		ret = select(serialDevFD+1, &rds, NULL, NULL, NULL );
		if ( ret < 0 )
		{
			perror( "select" );
			exit(2);
		}
		/*能够读取到数据*/
		else if (FD_ISSET(serialDevFD,&rds))
		{
		    len = read(serialDevFD,&buf,1);
		    if(len==1)
		    {
		        arr[loginIndex++] = buf;
		        if(loginIndex==2)
		        {
		            if(arr[1]=='\r'){
                      arr[1]='<';
                      arr[2] = 'c';
                      arr[3]='r';
                      arr[4] = '>';
                    }
		            arr[5] = '\0';
		            printf("接收到UPS登录响应=%s\n",arr);
		            break;
		        }
		    }
		}
	}
	
	
	//
	int duration = 3;//监测周期单位秒
	int count = 0;//计数器检测到断电后再检测2次如果真的没有电就关机
	int statusIndex = 0;
	write(serialDevFD,UPS_STATUS,3);
	while(1)
	{
		FD_ZERO(&rds);
		FD_SET(serialDevFD,&rds);
		/*调用select检查是否能够从/dev/input/event0设备读取数据*/
		ret = select(serialDevFD+1, &rds, NULL, NULL, NULL );
		if ( ret < 0 )
		{
			perror( "select" );
			exit(2);
		}
		/*能够读取到数据*/
		else if (FD_ISSET(serialDevFD,&rds))
		{
		    len = read(serialDevFD,&buf,1);
		    if(len==1)
		    {
                if(buf=='\r'){
                   //printf("<cr>\n");
                   arr[statusIndex] = '\0';
                   printf("接收到ups状态响应=%s<cr>\t",arr);
                   int pass = 1;
                   /*                  ups状态数据合法性检查         */
                   //检查长度是否是46个字符
                   if(statusIndex!=46) pass = 0;
                   //检查第一个字符是不是左括号
                   if(arr[0]!='(') pass = 0;
                  
                   if(pass==1)
                   {
                       printf("ups状态数据合法,");
                       //检测市电是否发生了断电
                       if(arr[38]=='1')
                       {
                           printf("发生了断电\n");
                           count++;
                           if(count==3)
                           {
                             printf("ups发生断电执行关机命令\n");
                             system("shutdown -h now");  
                           }
                       }else
                       {
                           printf("市电输入正常\n");
                           count = 0;
                       }
                   }else
                   {
                       printf("ups状态数据不合法跳过\n");
                   }
                   
                   sleepSecond(duration);
                   statusIndex = 0;
                   write(serialDevFD,UPS_STATUS,3);
                }else{
                   //printf("%c",buf);
                   arr[statusIndex++] = buf;
                }
		    }
		}
	    
	   
	}
}
int main(){  
  serialDevOpen();
  serialDevCfg();
  serialMonitor();
  serialDevClose();
  return 0;  
}  

 Makefile如下

CROSS=  
TARGET=upsMonitor
SRC=main.c  
all: $(TARGET)  
  
$(TARGET):$(SRC)  
	$(CROSS)gcc -std=c99 -o $(TARGET) $(SRC)  
	$(CROSS)strip $(TARGET)  
clean:  
	@rm -vf $(TARGET) *.o *~  

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6