Linux 开发 _ 摄像头编程 (实现拍照、网页监控功能)
- 2022 年 6 月 23 日
 本文字数:9810 字
阅读完需:约 32 分钟
这篇文章主要介绍 Linux 下 UVC 免驱摄像头操作流程,介绍 V4L2 框架、完成摄像头拍照保存为 BMP 图像到本地,最后贴出了利用 CJSON 库解析天气预报、北京时间接口返回的数据例子代码(上篇文章的案例补充)。
任务 1:摄像头操作方式
(1)摄像头框架介绍
Linux、windows 这些系统下很多摄像头都是免驱(UVC)。
v4l2 :免驱摄像头框架----一堆结构体。
Linux 下开发摄像头的不同角度问题:(嵌入式开发)
【1】上层软件系统开发(系统编程),控制驱动提供的接口使用摄像头。
【2】底层硬件系统开发(驱动编程),直接控制摄像头采集数据。
摄像头功能:将采集到的图像数据转为二进制数据返回。
驱动的代码集成在操作系统里。
在操作系统里使用摄像头步骤:学习接口函数的调用。
fread(); read();
Linux 下是一切设备皆文件: 摄像头、网卡、声卡、鼠标、键盘………………….
Linux 下设备文件存放的位置: /dev 目录下。
在虚拟机跑 Linux,使用外设设备都需要单独挂载才可以使用。
挂载摄像头:
 查看摄像头的设备节点:
[root@wbyq /]# ls /dev/video* 
/dev/video0 /dev/video1
(2)运行网页监控项目代码
远程网页视频监控示例
第一步:编译libjpeg-turbo
1. 解压:libjpeg-turbo-1.2.1.tar.gz
2. 生成Makefile(如果报错,就安装报错提示安装包):./configure
3. 编译安装:make && make install
4. 将生成lib和include目录下的文件拷贝到/usr目录下的对应文件夹
第二步:编译mjpg-streamer-r63
1.直接make进行编译
2.运行程序:./mjpg_streamer -i "/work/mjpeg/mjpg-streamer-r63/input_uvc.so -f 10 -r 800*480 -y" -o "/work/mjpeg/mjpg-streamer-r63/output_http.so -w www"
注意: 可以使用电脑自带的摄像头。在虚拟机的右下角进行将windows系统的摄像头挂载到Linux系统
3. 在浏览器里输入地址进行查看摄像头:
例如:192.168.11.123:8080
将远程摄像头监控的代码编译运行实现效果。
(1) 修改端口号,8080 不能作为公网的端口访问。
(2) 修改线程数量。
(3) 分析下线程的函数实现原理。
(4) 使用花生壳软件实现公网监控。
花生壳: 将本地 IP 地址映射为一个域名。
外网想要访问本地电脑 IP 就直接访问域名。
(3)摄像头编程,实现拍照功能
学习摄像头的使用
(1)摄像头实现拍照源程序流程:(50%程序学过的)
(2)打开摄像头的设备文件 open(“/dev/xxx”);
(3)获取摄像头参数。判断摄像头是否支持一些特有的操作。read
(4)配置摄像头的参数。(1) 输出的源数据格式 RGB888 (2) 输出的图像尺寸
RGB888:数字数据格式
其他格式: YUV 模拟数据格式
(5)判断摄像头的参数是否设置成功。
(6)读取摄像头的数据。 队列的形式。
(7)将读取的 YUV 格式数据转为 RGB 格式
(8)将 RGB 格式数据编码为 BMP 格式的图片存放到电脑上
安装 rpm 软件包:rpm -ivh xxxxx.rpm
Linux 下安装软件有两种方式:
【1】rpm 软件安装包(已经编译好的二进制文件的集合),使用 rpm -ivh xxx.rpm
【2】直接下载源码包自己编译安装。libjpeg-turbo-1.2.1.tar.gz
(1) 没有 makefile 文件,就有: configure 文件,用来生成 makefile 文件
示例: ./configure
(2) make 编译源码
(3) make install 安装源码。(会将编译好的文件拷贝到指定的目录下)
 void *memset(void *s, int c, size_t count) //给指定地址的空间填入指定大小的数据参数:*s :起始地址c :填入的数据count :填入的数量RGB888 : 000 FFF
摄像头拍照示例代码:
#include "camera_bmp.h"
T_PixelDatas Pixedata; //存放实际的图像数据
/*    USB摄像头相关参数定义*/struct v4l2_buffer tV4l2Buf;int iFd;int ListNum;unsigned char* pucVideBuf[4];  // 视频BUFF空间地址
int main(int argc ,char *argv[]){  if(argc!=2)  {      printf("./app /dev/videoX\n");      return -1;  }    camera_init(argv[1]);  //摄像头设备初始化
  //开始采集摄像头数据,并编码保存为BMP图片  camera_pthread();  return 0;}
//YUV转RGB实现unsigned int Pyuv422torgb32(unsigned char * ptr,unsigned int width, unsigned int height){  unsigned int i, size;  unsigned char Y, Y1, U, V;  unsigned char *buff = ptr;         //源数据  unsigned char *output_pt=Pixedata.VideoBuf; //存放转换之后的数据   unsigned char r, g, b;  size = width * height /2;  for (i = size; i > 0; i--)   {    Y = buff[0];    U = buff[1];    Y1= buff[2];    V = buff[3];    buff += 4;    r = R_FROMYV(Y,V);    g = G_FROMYUV(Y,U,V); //b    b = B_FROMYU(Y,U); //v    *output_pt++ = b;    *output_pt++ = g;    *output_pt++ = r;    r = R_FROMYV(Y1,V);    g = G_FROMYUV(Y1,U,V); //b    b = B_FROMYU(Y1,U); //v    *output_pt++ = b;    *output_pt++ = g;    *output_pt++ = r;  }  return 0;} 
//摄像头设备的初始化int camera_init(char *video){  int i=0;  int cnt=0;  //定义摄像头驱动的BUF的功能捕获视频  int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;    /* 1、打开视频设备 */  iFd = open(video,O_RDWR);  if(iFd < 0)  {    printf("摄像头设备打开失败!\n");    return 0;  }    struct v4l2_format  tV4l2Fmt;    /* 2、 VIDIOC_S_FMT 设置摄像头使用哪种格式 */  memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));  tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获
  //设置摄像头输出的图像格式    tV4l2Fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
  /*设置输出的尺寸*/  tV4l2Fmt.fmt.pix.width       = 640;  tV4l2Fmt.fmt.pix.height      = 480;  tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;      //VIDIOC_S_FMT 设置摄像头的输出参数    ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt); 
  //打印摄像头实际的输出参数  printf("Support Format:%d\n",tV4l2Fmt.fmt.pix.pixelformat);  printf("Support width:%d\n",tV4l2Fmt.fmt.pix.width);  printf("Support height:%d\n",tV4l2Fmt.fmt.pix.height);
  /* 初始化Pixedata结构体,为转化做准备 */  Pixedata.iBpp =24;                  //高度 和宽度的赋值  Pixedata.iHeight = tV4l2Fmt.fmt.pix.height;  Pixedata.iWidth = tV4l2Fmt.fmt.pix.width;
  //一行所需要的字节数  Pixedata.iLineBytes = Pixedata.iWidth*Pixedata.iBpp/8;  //一帧图像的字节数  Pixedata.iTotalBytes = Pixedata.iLineBytes * Pixedata.iHeight;  Pixedata.VideoBuf=malloc(Pixedata.iTotalBytes); //申请存放图片数据空间
  //v412请求命令  struct v4l2_requestbuffers tV4l2ReqBuffs;    /* 3、VIDIOC_REQBUFS  申请buffer */  memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
  /* 分配4个buffer:实际上由VIDIOC_REQBUFS获取到的信息来决定 */  tV4l2ReqBuffs.count   = 4;    /*支持视频捕获功能*/  tV4l2ReqBuffs.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;    /* 表示申请的缓冲是支持MMAP */  tV4l2ReqBuffs.memory  = V4L2_MEMORY_MMAP;    /* 为分配buffer做准备 */  ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);    for (i = 0; i < tV4l2ReqBuffs.count; i++)   {    memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));    tV4l2Buf.index = i;    tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;    tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    /* 6、VIDIOC_QUERYBUF 确定每一个buffer的信息 并且 mmap */    ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);    //映射空间地址    pucVideBuf[i] = mmap(0 /* start anywhere */ ,          tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,          tV4l2Buf.m.offset);    printf("mmap %d addr:%p\n",i,pucVideBuf[i]);  }
  /* 4、VIDIOC_QBUF  放入队列*/    for (i = 0; i <tV4l2ReqBuffs.count; i++)     {      memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));      tV4l2Buf.index = i;      tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;      tV4l2Buf.memory = V4L2_MEMORY_MMAP;      ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf); //放入队列    }    /*5、启动摄像头开始读数据*/    ioctl(iFd, VIDIOC_STREAMON, &iType);  return 0;}
/*循环采集摄像头数据*/void camera_pthread(){  int error;  int cnt=0;  int i=0;  int ListNum;    /* 8.1、等待是否有数据 */  fd_set readfds;     /* YUV格式的数据<------>在LCD上显示:rgb888 */  initLut();  printf("开始采集数据.......\n");  FD_ZERO(&readfds);  FD_SET(iFd,&readfds);  select(iFd+1,&readfds,NULL,NULL,NULL);    memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));  tV4l2Buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE; //类型  tV4l2Buf.memory  = V4L2_MEMORY_MMAP; //存储空间类型
  /* 9、VIDIOC_DQBUF    从队列中取出 */  error = ioctl(iFd, VIDIOC_DQBUF, &tV4l2Buf); //取出一帧数据  ListNum = tV4l2Buf.index; //索引编号    //将YUV转换为RGB  Pyuv422torgb32(pucVideBuf[ListNum],Pixedata.iWidth,Pixedata.iHeight);    //保存BMP  save_bmp(Pixedata.VideoBuf);   memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));  tV4l2Buf.index  = ListNum;  tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;  tV4l2Buf.memory = V4L2_MEMORY_MMAP;  error = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);}
/*-----------------保存为BMP格式的图片------------------*/typedef unsigned char  BYTE;typedef unsigned short  WORD;typedef unsigned long  DWORD;
#pragma pack(1)typedef struct tagBITMAPFILEHEADER{     WORD    bfType;                // the flag of bmp, value is "BM"     DWORD    bfSize;                // size BMP file ,unit is bytes     DWORD    bfReserved;            // 0     DWORD    bfOffBits;             // must be 54  RGB数据存放位置}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{     DWORD    biSize;            // must be 0x28     DWORD    biWidth;           // 宽度     DWORD    biHeight;          // 高度     WORD     biPlanes;          // must be 1     WORD    biBitCount;            // 像素位数     DWORD    biCompression;         //     DWORD    biSizeImage;       //     DWORD    biXPelsPerMeter;   //     DWORD    biYPelsPerMeter;   //     DWORD    biClrUsed;             //     DWORD    biClrImportant;        //}BITMAPINFOHEADER;
//保存为BMP格式的文件void save_bmp(char *src){  /*-----------------------------------------------------------          获取时间参数,用来给图片命名  -------------------------------------------------------------*/  time_t t;    struct tm *tmp;    char buffer[1024] = {0};    t = time(NULL);    tmp=localtime(&t);    if(strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S_", tmp) == 0)     {      printf("timer error\n");    }    static int cnt=0;      //静态变量存放总数量  cnt++;  if(cnt>=20)cnt=0;      //清理计数器  char str[10];  sprintf(str,"%d",cnt); //整数转为字符串    strcat(buffer,str);  strcat(buffer,".bmp");  printf("%s\n",buffer); //打印图片的名称    /*-----------------------------------------------------------            获取图片数据,用来保存为BMP图片格式  -------------------------------------------------------------*/    FILE * fp;    int i;    BITMAPFILEHEADER   bf;    BITMAPINFOHEADER   bi;       memset(&bf ,0 ,sizeof(BITMAPFILEHEADER));    memset(&bi ,0 ,sizeof(BITMAPINFOHEADER));
    fp = fopen(buffer, "wb");    if(!fp)    {    printf("open %s error\n",buffer);    return ;  }
  //Set BITMAPINFOHEADER 设置BMP信息头  bi.biSize = sizeof(BITMAPINFOHEADER);//40;  bi.biWidth  = Pixedata.iWidth;//IMAGEWIDTH;  bi.biHeight = Pixedata.iHeight;//IMAGEHEIGHT;  bi.biPlanes = 1;  bi.biBitCount = 24;//8;  bi.biCompression = 0;  bi.biSizeImage =Pixedata.iHeight*Pixedata.iWidth*3; //;0  bi.biXPelsPerMeter = 0;  bi.biYPelsPerMeter = 0;  bi.biClrUsed = 0;// 1<<(bi.biBitCount)  bi.biClrImportant = 0;
    //Set BITMAPFILEHEADER    bf.bfType = 0x4d42; //'B''M'    bf.bfSize = 54 + bi.biSizeImage;// sizeof(BITMAPFILEHEADER);        bf.bfReserved = 0;    bf.bfOffBits = 54;           fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, fp);    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, fp);        fwrite(src, Pixedata.iWidth*Pixedata.iHeight*3,1,fp);    fclose(fp);}
/*----------------------YUV转RGB算法-------------------*/
static int *LutYr = NULL;static int *LutYg = NULL;;static int *LutYb = NULL;;static int *LutVr = NULL;;static int *LutVrY = NULL;;static int *LutUb = NULL;;static int *LutUbY = NULL;;static int *LutRv = NULL;static int *LutGu = NULL;static int *LutGv = NULL;static int *LutBu = NULL;
unsigned char RGB24_TO_Y(unsigned char r, unsigned char g, unsigned char b){  return (LutYr[(r)] + LutYg[(g)] + LutYb[(b)]);}
unsigned char YR_TO_V(unsigned char r, unsigned char y){  return (LutVr[(r)] + LutVrY[(y)]);}
unsigned char YB_TO_U(unsigned char b, unsigned char y){  return (LutUb[(b)] + LutUbY[(y)]);}
unsigned char R_FROMYV(unsigned char y, unsigned char v){  return CLIP((y) + LutRv[(v)]);}
unsigned char G_FROMYUV(unsigned char y, unsigned char u, unsigned char v){  return CLIP((y) + LutGu[(u)] + LutGv[(v)]);}
unsigned char B_FROMYU(unsigned char y, unsigned char u){  return CLIP((y) + LutBu[(u)]);}
void initLut(void){  int i;  #define Rcoef 299   #define Gcoef 587   #define Bcoef 114   #define Vrcoef 711 //656 //877   #define Ubcoef 560 //500 //493 564    #define CoefRv 1402  #define CoefGu 714 // 344  #define CoefGv 344 // 714  #define CoefBu 1772    LutYr = malloc(256*sizeof(int));  LutYg = malloc(256*sizeof(int));  LutYb = malloc(256*sizeof(int));  LutVr = malloc(256*sizeof(int));  LutVrY = malloc(256*sizeof(int));  LutUb = malloc(256*sizeof(int));  LutUbY = malloc(256*sizeof(int));    LutRv = malloc(256*sizeof(int));  LutGu = malloc(256*sizeof(int));  LutGv = malloc(256*sizeof(int));  LutBu = malloc(256*sizeof(int));  for (i= 0;i < 256;i++)  {      LutYr[i] = i*Rcoef/1000 ;      LutYg[i] = i*Gcoef/1000 ;      LutYb[i] = i*Bcoef/1000 ;      LutVr[i] = i*Vrcoef/1000;      LutUb[i] = i*Ubcoef/1000;      LutVrY[i] = 128 -(i*Vrcoef/1000);      LutUbY[i] = 128 -(i*Ubcoef/1000);      LutRv[i] = (i-128)*CoefRv/1000;      LutBu[i] = (i-128)*CoefBu/1000;      LutGu[i] = (128-i)*CoefGu/1000;      LutGv[i] = (128-i)*CoefGv/1000;  }  }
void freeLut(void){  free(LutYr);  free(LutYg);  free(LutYb);  free(LutVr);  free(LutVrY);  free(LutUb);  free(LutUbY);    free(LutRv);  free(LutGu);  free(LutGv);  free(LutBu);}
(4)解析北京时间
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "cJSON.h"#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>
/*标准时间运行格式: ./app*/int GetData(char *src);int main(int argc,char **argv){    /*1. 拼接访问的链接路径*/  char src[200];  char str[]="http://api.k780.com:88/?app=life.time'&'appkey=10003'&'sign=b59bc3ef6191eb9f747dd4e83c99f2a4'&'format=json >data.txt";    strcpy(src,"curl ");  strcat(src,str);  system(src);  //执行浏览器数据    /*2. 读取源数据*/  FILE *file=fopen("data.txt","rb");  if(file==NULL)  {    printf("文件打开失败!\n");    exit(-1);  }    struct stat file_stat_buf;  stat("data.txt",&file_stat_buf); //获取文件的状态    char *src_data=malloc(file_stat_buf.st_size);  if(src_data==NULL)  {    printf("%s文件第%d行,出现错误:空间失败!\n",__FILE__,__LINE__);    exit(-1);  }    fread(src_data,1,file_stat_buf.st_size,file); //读取源数据到缓冲区    /*3. 数据解析*/  GetData(src_data);    /*4. 释放空间*/  free(src_data);  return 0;}
/*函数功能:获取具体的数据函数形参:保存json的源数据首地址*/int GetData(char *src){  /*1. 载入源数据,获取根对象*/  cJSON *root=cJSON_Parse(src);  if(root==NULL)  {    printf("%s文件第%d行,出现错误:获取根对象失败!\n",__FILE__,__LINE__);    return -1;  }    /*2. 获取对象中的值*/  cJSON *json=cJSON_GetObjectItem(root,"success");  if(json==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }    //判断获取的值是否成功  if(strcmp(json->valuestring,"1")!=0)  {    printf("%s文件第%d行,出现错误:时间获取失败!\n",__FILE__,__LINE__);    return -1;  }      //获取结果  json=cJSON_GetObjectItem(root,"result");  if(json==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }    //获取秒单位时间  cJSON *data=cJSON_GetObjectItem(json,"timestamp");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("秒单位的时间:%s\n",data->valuestring);    data=cJSON_GetObjectItem(json,"datetime_1");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("datetime_1:%s\n",data->valuestring);    data=cJSON_GetObjectItem(json,"datetime_2");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("datetime_2:%s\n",data->valuestring);      data=cJSON_GetObjectItem(json,"week_1");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("week_1:%s\n",data->valuestring);      data=cJSON_GetObjectItem(json,"week_2");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("week_2:%s\n",data->valuestring);    data=cJSON_GetObjectItem(json,"week_3");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("week_3:%s\n",data->valuestring);    data=cJSON_GetObjectItem(json,"week_4");  if(data==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }  printf("week_4:%s\n",data->valuestring);  /* */  cJSON_Delete(root);}
(5)解析天气预报
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "cJSON.h"#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>
/*标准时间运行格式: ./app*/int GetData(char *src);int main(int argc,char **argv){    if(argc!=2)  {    printf("参数格式: ./app <城市名称>\n");    return 0;  }  /*1. 拼接访问的链接路径*/  char src[200];  char str1[]="http://api.k780.com:88/?app=weather.future'&'weaid=";  char str2[]="'&&'appkey=10003'&'sign=b59bc3ef6191eb9f747dd4e83c99f2a4'&'format=json >data.txt";    strcpy(src,"curl ");  strcat(src,str1);  strcat(src,argv[1]);  strcat(src,str2);    system(src);  //执行浏览器数据    /*2. 读取源数据*/  FILE *file=fopen("data.txt","rb");  if(file==NULL)  {    printf("文件打开失败!\n");    exit(-1);  }    struct stat file_stat_buf;  stat("data.txt",&file_stat_buf); //获取文件的状态    char *src_data=malloc(file_stat_buf.st_size);  if(src_data==NULL)  {    printf("%s文件第%d行,出现错误:空间失败!\n",__FILE__,__LINE__);    exit(-1);  }    fread(src_data,1,file_stat_buf.st_size,file); //读取源数据到缓冲区    /*3. 数据解析*/  GetData(src_data);    /*4. 释放空间*/  free(src_data);  return 0;}
/*函数功能:获取具体的数据函数形参:保存json的源数据首地址*/int GetData(char *src){  /*1. 载入源数据,获取根对象*/  cJSON *root=cJSON_Parse(src);  if(root==NULL)  {    printf("%s文件第%d行,出现错误:获取根对象失败!\n",__FILE__,__LINE__);    return -1;  }    /*2. 获取对象中的值*/  cJSON *json=cJSON_GetObjectItem(root,"success");  if(json==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }    //判断获取的值是否成功  if(strcmp(json->valuestring,"1")!=0)  {    printf("%s文件第%d行,出现错误:时间获取失败!\n",__FILE__,__LINE__);    return -1;  }      //获取结果  json=cJSON_GetObjectItem(root,"result");  if(json==NULL)  {    printf("%s文件第%d行,出现错误:获取json对象失败!\n",__FILE__,__LINE__);    return -1;  }
  //获取数组大小  int cnt=cJSON_GetArraySize(json);   printf("cnt=%d\n",cnt);  int i;  for(i=0;i<cnt;i++)  {    cJSON *data=cJSON_GetArrayItem(json,i);        cJSON *p=cJSON_GetObjectItem(data,"weaid");    printf("weaid=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"days");    printf("days=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"week");    printf("week=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"cityno");    printf("cityno=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"citynm");    printf("citynm=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"cityid");    printf("cityid=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"temperature");    printf("temperature=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"humidity");    printf("humidity=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"weather");    printf("weather=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"wind");    printf("wind=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"winp");    printf("winp=%s\n",p->valuestring);        p=cJSON_GetObjectItem(data,"winp");    printf("winp=%s\n",p->valuestring);    printf("\n\n");  }  cJSON_Delete(root);}
版权声明: 本文为 InfoQ 作者【DS小龙哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/897263defddd4a3e5e489a459】。文章转载请联系作者。
DS小龙哥
之所以觉得累,是因为说的比做的多。 2022.01.06 加入
熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域










    
评论