项目背景:

        本人一直在寻找一种可以不知道具体设备信息的情况下,可以跨网快速发现设备,修改IP和远程控制的网络协议。比较了WS-Discovery,onvif,gvcp几种协议后,发现GigeVision的gvcp协议还是最简洁高效的,毕竟所有工业相机都在用,既然有这么好的协议,所以就全网搜索了一下,除了发现几篇比较好的介绍文章,竟然没有一个可以用的代码,所以就花了一周时间自己码了一个,不足之处请大家指正。

方案简介:

        网口工业相机的基本方案是采用Gige-Vision的标准,采用gvcp协议来发现、配置和控制相机,采用gvsp协议来传输未压缩的图像数据,一般需要千兆网速。我的嵌入式设备运行ubuntu core系统,只有百兆网速,所以图像传输采用mjpg-streamer,可以支持uvc和mipi多种摄像头。我只要再实现gvcp的协议就可以实现工业相机的大部分功能,岂不乐哉。

摄像头监控平台开源 网络摄像机开源项目_摄像头监控平台开源

 

 硬件组成图

    硬件采用100万全局曝光的OV9281作为图像采集芯片,主板采用H3的4核A7核心板,具有网口/WIFI/USB/GPIO等接口,大小40x40mm。 已测试通过的开源硬件链接为 https://item.taobao.com/item.htm?id=652142910541

   

摄像头监控平台开源 网络摄像机开源项目_摄像头监控平台开源_02

 

  64位升级板硬件已经发布,采用200万彩色摄像头作为图像采集芯片,主板采用4核A53处理器,内置Python和OpenCV,算力更强,支持深度学习推理,具有网口/WIFI/USB/GPIO等接口,大小40x40mm。 已测试通过的开源硬件链接为 https://item.taobao.com/item.htm?id=684106700771

摄像头监控平台开源 网络摄像机开源项目_c++_03

 软件架构示意图

        本项目实现了一种基于GVCP协议和mjpeg-steamer的网口工业相机。使用GigeVision中的gvcp协议来实现对相机的发现,修改IP和远程控制功能,使用mjpeg-steamer来传输图像数据.配合PC端的OpenMVS软件使用,可以方便的查找相机、管理相机、获取图像,保存图片等功能。

GvcpServer的实现

        在GvcpServer中,我主要实现了常用的几种协议

#define GVCP_DISCOVERY_CMD	2
#define GVCP_DISCOVERY_ACK	3
#define GVCP_FORCEIP_CMD	4
#define GVCP_FORCEIP_ACK	5
#define GVCP_READREG_CMD	0x80
#define GVCP_READREG_ACK	0x81
#define GVCP_WRITEREG_CMD	0x82
#define GVCP_WRITEREG_ACK	0x83
#define GVCP_READMEM_CMD	0x84
#define GVCP_READMEM_ACK	0x85

gvcp的发现命令代码如下

int gvcp_cmd_discover(int iFd)
{
	//char rgMessage[128] = "I am sending message to you!";
	//int iFd;
	int iSendbytes;
	
	struct sockaddr_in Addr;
	int bNeedClose = 0;
	if (iFd < 0)
	{
		if ((iFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
		{
			printf("socket fail\n");
			return -1;
		}
		bNeedClose = 1;
	}
	int iOptval = 1;
#ifdef _WINDOWS_
	if (setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, (CHAR*)&iOptval, sizeof(int)) < 0)
	{
		printf("setsockopt SO_REUSEADDR failed!");
	}
	BOOL	bBroadcast = true;
	if (setsockopt(iFd, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)) < 0)
	{
		printf("setsockopt SO_BROADCAST failed!");
	}
#else
	if (setsockopt(iFd, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &iOptval, sizeof(int)) < 0)
	{
		printf("setsockopt failed!");
	}
#endif
	memset(&Addr, 0, sizeof(struct sockaddr_in));
	Addr.sin_family = AF_INET;
	Addr.sin_addr.s_addr = inet_addr("255.255.255.255");
	Addr.sin_port = htons(3956);

	static struct gvcp_discover_cmd cmd_msg;
	memset(&cmd_msg, 0, sizeof(struct gvcp_discover_ack));
	cmd_msg.header.cMsgKeyCode = 0x42;
	cmd_msg.header.cFlag=0x11;//0x11 allow broadcast ack;ack required
	cmd_msg.header.wCmd= htons(GVCP_DISCOVERY_CMD);//discovery_cmd=2;FORCEIP_CMD = 4;READREG_CMD=0x80
	cmd_msg.header.wLen = htons(0);//payload length
	cmd_msg.header.wReqID = htons(1);// request id = 1;READREG id=12345

	char* rgMessage = (char*)&cmd_msg;
	uint32 dwMsgLen = sizeof(struct gvcp_discover_cmd);
	//while (1)
	{
		if ((iSendbytes = sendto(iFd, rgMessage, dwMsgLen, 0, (struct sockaddr*)&Addr, sizeof(struct sockaddr))) == -1)
		{
			printf("sendto fail, errno=%d,%s\n", errno, strerror(errno));
			return -1;
		}
		printf("gvcp_cmd_discover=%s, rgMessageLen=%d,iSendbytes=%d\n", rgMessage, dwMsgLen, iSendbytes);
		//sleep(1);
	}
	if (bNeedClose > 0)
	{
#ifdef _WINDOWS_
		closesocket(iFd);
#else
		close(iFd);
#endif
	}

	return 0;
}

gvcp的发现应答命令如下:

int gvcp_ack_discover(int iFd,char* szIp,char* szMask,char* szGateway, uint16 wReqID,uint32 dwPort,uint8* pMac)
{
    //char rgMessage[128] = "I am sending message to you!";
    //int iFd;
    int iSendbytes;
    int iOptval = 1;
    struct sockaddr_in Addr;
	int bNeedClose=0;
	if(iFd<0)
	{
		if ((iFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
		{
		    printf("socket fail\n");
		    return -1;
		}
		bNeedClose=1;
	}
    if (setsockopt(iFd, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, (char*)&iOptval, sizeof(int)) < 0)
    {
        printf("setsockopt failed!");
    }
    memset(&Addr, 0, sizeof(struct sockaddr_in));
    Addr.sin_family = AF_INET;
    Addr.sin_addr.s_addr = inet_addr("255.255.255.255");
    Addr.sin_port = htons(dwPort);
	
	struct gvcp_discover_ack ack_msg;
	memset(&ack_msg, 0, sizeof(struct gvcp_discover_ack));
	ack_msg.header.wStatus=htons(0);
	ack_msg.header.wAck=htons(GVCP_DISCOVERY_ACK);
	ack_msg.header.wLen=htons(sizeof(struct gvcp_ack_payload));
	ack_msg.header.wReqID=htons(1);
	ack_msg.payload.dwSpecVer=htonl(0x010002);;
	ack_msg.payload.dwDevMode=htonl(1);
	//uint8 MyMac[6]={0xc4,0x2f,0x90,0xf1,0x71,0x3e};
	memcpy(&ack_msg.payload.Mac[2],m_LocalMacAddr,6);
	ack_msg.payload.dwSupIpSet=htonl(0x80000007);
	ack_msg.payload.dwCurIpSet=htonl(0x00005);
	//uint8 unused1[12];
	*((uint32*)&ack_msg.payload.CurIP[12])=inet_addr(m_szLocalIp);//last 4 byte
	*((uint32*)&ack_msg.payload.SubMask[12])=inet_addr(m_szLocalMask);//last 4 byte
	*((uint32*)&ack_msg.payload.Gateway[12])=inet_addr(m_szLocalGateway);//last 4 byte
	strcpy(ack_msg.payload.szFacName,"GEV");//first
	strcpy(ack_msg.payload.szModelName,"MV-CA010-GM");//first
	strcpy(ack_msg.payload.szDevVer,"V2.8.6 180210 143913");
	strcpy(ack_msg.payload.szFacInfo,"GEV");
	strcpy(ack_msg.payload.szSerial,"00C31976084");
	strcpy(ack_msg.payload.szUserName,"");
	char* rgMessage=(char*)&ack_msg;
	uint32 dwMsgLen = sizeof(struct gvcp_discover_ack);
    //while (1)
    {
        if ((iSendbytes = sendto(iFd, rgMessage, dwMsgLen, 0, (struct sockaddr*)&Addr, sizeof(struct sockaddr))) == -1)
        {
            printf("sendto fail, errno=%d,%s\n", errno,strerror(errno));
            return -1;
        }
        printf("gvcp_ack_discover=%s, rgMessageLen=%d,iSendbytes=%d\n", rgMessage, dwMsgLen, iSendbytes);
        //sleep(1);
    }
	if(bNeedClose>0)
	{
#ifdef _WINDOWS_
		closesocket(iFd);
#else
		close(iFd);
#endif
	}

    return 0;
}

其他完整代码的安装步骤如下:

在ubuntu系统下,切换到su权限

git clone https://github.com/BigSensor/GvcpServer cd GvcpServer
make
./GvcpServer

mjpg-streamer代码的实现

        mjpg-streamer的使用网上有很多介绍了,我不在赘述,有些板子里已经内置了mjpg-streamer的代码,直接运行就可,如果还没有的朋友,我付一个下载地址

git clone https://github.com/BigSensor/mjpg-streamer cd mjpg-streamer
make
./start

PC端管理软件OpenMVS的使用

        本软件暂时叫OpenMVS,实现了一种工业相机的PC管理控制与播放软件,实现了GVCP的Client端和mjpeg-steamer的客户端。使用GigeVision中的gvcp协议来实现对相机的发现,修改IP和远程控制功能,使用http协议来读取并解码mjpeg-steamer传输图像数据。可以方便的查找相机、管理相机、获取图像,保存图片等功能。

        目前只实现了windows端的功能,运行时只需要安装vc运行库即可。安装方式

git clone https://github.com/BigSensor/OpenMVS

软件已经实现了查询所有网卡下的gige设备,预览mjpg-streamer中的图片,保存图片等功能

摄像头监控平台开源 网络摄像机开源项目_iot_04

摄像头监控平台开源 网络摄像机开源项目_物联网_05

 后记:

        本文的代码和程序已经上传到github中,大家可以下载、测试、批评、指正,有各种建议和意见请留言。