物联网实战--驱动篇之(六)4G通讯(Air780E)

目录

一、4G模块简介

二、AIR780E驱动程序

三、AIR780使用注意事项

四、结合MQTT传输测试


一、4G模块简介

        4G应该是我们日常生活最常见的一种互联网通讯方式了,每个智能手机都配置了,不过手机的4G跟我们物联网领域要用的4G有点区别。首先是物联网采用模块化4G,手机是CPU内部直接集成了4G基带,技术水平肯定高很多了;其次是网速,手机为了满足日常看视频的需求,速度比较快,理论下载速率是150M,采用CAT4标准;而我们要用的物联网4G模块采用CAT1标准,下载速率10M左右,从成本和需求上来讲比较适合。

        基于CAT1标准的4G模组没出来之前,4G模块一般是作为网关与服务器的通讯链路,因为成本比较高,一个都要150左右;后来基于CAT1的4G模块出来后价格来到了60元左右,随着用量的增长以及技术的更新,成本在不断地降低,前两年AIR720差不多40元左右,如今我们的测试型号AIR780E单纯模组TB价格也就差不多20块左右。就这4G成本,用在设备端作为通讯链路应该没什么问题了,比如充电宝、共享单车、按摩椅等等,而且AIR780E还有一定的低功耗水平,根据手册(目前还没测试过),极致低功耗可以达到2uA,已经差不多SX1278的水平了。

        总体来讲,基于CAT1的4G模组可以满足绝大多数的应用需求,所以需要好好学习,我们这里以合宙的AIR780E为例,写个驱动程序。

二、AIR780E驱动程序

        AIR780的驱动跟之前的esp8266 WiFi驱动很类似,可以参考。物联网实战--入门篇之(六)嵌入式-WIFI驱动(ESP8266)-CSDN博客     

以及AT指令手册Luat社区  

        首先来了解下4G模块的入网流程,模块上电后首先会进行网络注册,就是看下有没有信号了,这时候属于基站跟4G模组底层之间的事情,有没有4G卡暂时不影响;第二步就是PDP上下文移动场景创建,简单理解就是创建一个可以用于互联网通讯的身份,这时候需要你的4G模组里有可用的4G卡;第三步是网络附着,4G模组和基站相互交换认证信息,只有附着了网络,后续才能分配IP地址,进行网络通讯。到这一步为止,只要你的卡能用,每个步骤都是自动完成的,驱动程序只要间隔几秒查询下各个步骤的执行情况就行了。

        接下来就是配置一些网络参数了,比如多路连接、快发模式和APN,这里的APN相当于VIP等级了,跟开的卡有关系,有的卡保密等级比较高,会有专门的APN账户密码,普通卡就默认就行了。

        剩下就是最后一步,激活移动场景,获取IP地址就行了,其它网络连接部分跟esp8266是一样的,对于4G模组一般不作为服务端的,所以不必关系服务端的问题,都是作为客户端连接。接下来先从头文件开始:

       

         基本上都有注释了,AIR780E支持6个客户端连接,基本够用了。状态值就是刚才说明的那些了,入网成功后就可以进行连接操作了。客户端的定义跟esp8266的一样,整体工作的结构体有些区别,首先这里少了服务器相关的内容,其次是多了信号强度、模块序列号IMEI、卡号ICCID和定位信息,其中定位信息其他型号的4G模组不一定有,这个型号有的,我们主要采用了经纬度和海拔,其他暂时忽略。

        下面主要看下注册流程和接收处理,代码如下:


/*		
================================================================================
描述 : 网络注册函数
输入 : 
输出 : 
================================================================================
*/
void drv_air780_reg_process(void)
{
	static u32 last_sec_time=0, wait_time=2;
//  static char cmd_buff[100]={0};
	u32 now_sec_time=drv_get_sec_counter();
	if(now_sec_time-last_sec_time>wait_time)
	{
		switch(g_sAir780Work.state)
		{
			case AIR780_STATE_START:
			{
				drv_air780_uart_send("ATE0\r\n");
				delay_os(200);         
        

        drv_air780_send_at("RESET");//复位模块        
				g_sAir780Work.state=AIR780_STAT_INIT;		
        wait_time=5;			
				break;
			}
			case AIR780_STAT_INIT:
			{
				drv_air780_uart_send("ATE0\r\n");
				delay_os(200);     
        drv_air780_send_at("CSQ");//查询信号强度
				delay_os(200);     
        drv_air780_send_at("CGNSPWR=1");//打开GPS       
				delay_os(200);     
        drv_air780_send_at("CGNSAID=31,1,1,1");//使能位置辅助定位           
        g_sAir780Work.state=AIR780_STAT_CGREG;
				break;
			}          
			case AIR780_STAT_CGREG://查询网络注册状态
			{
        drv_air780_send_at("CGREG?");//查询网络连接信息
				wait_time=3;
        break;
			}
			case AIR780_STAT_CGACT://查询PDP状态
			{
        drv_air780_send_at("CGACT?");//查询网络连接信息
				wait_time=3;
        break;
			}      
			case AIR780_STAT_CGATT0://查询附着状态
			{
        drv_air780_send_at("CGATT?");//查询网络连接信息
				wait_time=3;
        break;
			}   
			case AIR780_STAT_CGATT1://已附着
			{
        drv_air780_send_at("CIPMUX=1");//多路连接
        delay_os(200);
        drv_air780_send_at("CIPQSEND=0");//快发模式
        delay_os(200);
        drv_air780_send_at("CSTT");//设置APN      =\"cmnet\",\"\",\"\"     
				wait_time=2;
        g_sAir780Work.state=AIR780_STAT_CIICR;
        break;
			}    
			case AIR780_STAT_CIICR:
			{
        drv_air780_send_at("CIICR");激活移动场景,获取IP地址
        delay_os(1000);
        drv_air780_send_at("CIFSR");//查询IP地址           
        g_sAir780Work.state=AIR780_STAT_OK;
				wait_time=3;
        break;
			}   
			case AIR780_STAT_OK://入网成功
			{
        static u8 counts=0;
        if(counts++%2==0)
        {
          if(g_sAir780Work.imei_buff[0]==0)
          {
            drv_air780_send_at("CGSN");
          }
          else if(g_sAir780Work.iccid_buff[0]==0)
          {
            drv_air780_send_at("ICCID");
          }
          else
          {
            static u8 flag=0;
            if(flag++%2==0)
            {
              drv_air780_send_at("CSQ");//查询信号强度
            }
            else
            {
              drv_air780_send_at("CGNSINF");//查询GPS信息
            }
          }
          
        }
        else
        {
          drv_air780_connect_process();
        }
				wait_time=3;
        break;
			}         
		}
		last_sec_time=drv_get_sec_counter();
	}
}

           在起始状态下我们直接复位了模块,这样可以避免与之前的状态冲突,这个模块的启动速度很快,2~3秒就完成了;在初始化阶段,把回显关掉,避免影响接收解析,同时打开定位的相关功能,定位功能要几十秒甚至几分钟才能有效果,而且最好天线要靠窗户边或者户外才行;接下去几步都是查询状态,接收处理等等,这里先跳到入网成功后的状态。分别分时进行了网络连接、请求IMEI、请求ICCID、查询信号强度和查询定位信息等任务,其中IMEI和ICCID只要请求到后就不再重复执行了,剩下就是信号强度和定位信息不断更新。

        接下来是接收处理的代码:


/*		
================================================================================
描述 : 接收处理
输入 : 
输出 : 
================================================================================
*/
void drv_air780_recv_process(void)
{
	u16 recv_len;
	char *pData=NULL;
	
	if(g_sAir780Work.pUART->iRecv>0)
	{
		recv_len=0;
		while(recv_len<g_sAir780Work.pUART->iRecv)
		{
			recv_len=g_sAir780Work.pUART->iRecv;
			delay_ms(5);
		}
		char *pBuff=(char*)g_sAir780Work.pUART->pBuff;
		printf("air780 recv=%s\n", g_sAir780Work.pUART->pBuff);
		if( (pData=strstr(pBuff, "+RECEIVE,"))!=NULL )//接收到数据
		{
      pData=pBuff;
      while((pData=strstr(pData, "+RECEIVE,"))!=NULL)
      {
        pData+=strlen("+RECEIVE,");
        u8 sock_id=atoi(pData);
//        printf("sock_id=%d\n", sock_id);
        if(sock_id<AIR780_MAX_LINK_NUM && (pData=strstr(pData, ","))!=NULL )
        {
          pData+=1;
          u16 data_len=atoi(pData);
          if( (pData=strstr(pData, ":"))!=NULL )
          {
            pData+=3;//: 0D 0A
            g_sAir780Work.client_list[sock_id].keep_time=drv_get_sec_counter();
            printf_hex("air780 data buff= ", (u8*)pData, data_len);
            if(g_sAir780Work.fun_recv_parse!=NULL)
            {
              g_sAir780Work.fun_recv_parse(sock_id, (u8*)pData, data_len);//接收处理
            }
            pData+=data_len;
          }
        }		        
      }
	
		}		
    else if( (pData=strstr(pBuff, "+CSQ: "))!=NULL )//信号强度
    {
      pData+=strlen("+CSQ: ");
      g_sAir780Work.rssi=atoi(pData);
      printf("***rssi=%d\n", g_sAir780Work.rssi);
    }
    else if( (pData=strstr(pBuff, "+CGNSINF:"))!=NULL )//定位信息
    {
      pData+=strlen("+CGNSINF:");
//      printf("GPS=%s", pBuff);
      for(u8 i=0; i<5; i++)
      {
        if( (pData=strstr(pData, ","))!=NULL )
        {
          pData+=1;
          switch(i)
          {
            case 2:
            {
              g_sAir780Work.deg_sn=atof(pData);//纬度
              break;
            }
            case 3:
            {
              g_sAir780Work.deg_we=atof(pData);//经度
              break;
            }    
            case 4:
            {
              g_sAir780Work.hight=atof(pData);//海拔
              break;
            }            
          } 
        }
        else
        {
          break;
        }
      }
      printf("GPS: WE=%.3f deg, SN=%.3f deg, H=%.3f m\n", g_sAir780Work.deg_we, g_sAir780Work.deg_sn, g_sAir780Work.hight);
    }    
		else if((pData=strstr((char*)pBuff, "+CGEV: "))!=NULL)//PDP变化
		{
			pData+=strlen("+CGEV: ");
			if(strstr(pData, "DEACT")!=NULL)//移动场景去活
			{
						
        drv_air780_send_at("CIPSHUT");//去激活
				delay_os(100);
				g_sAir780Work.state=AIR780_STATE_START;
				printf("===RDY net_state=AIR780_STATE_START\n");			
			}
			else if(strstr(pData, "ACT")!=NULL)//移动场景激活
			{
				if(g_sAir780Work.state==AIR780_STAT_CIICR)
				{
					g_sAir780Work.state=AIR780_STAT_OK;
				}
			}						
		}	    
		else if((pData=strstr((char*)pBuff, "+CGREG:"))!=NULL)//注册查询
		{
			pData+=strlen("+CGREG:");
			if((pData=strstr((char*)pBuff, ",1"))!=NULL || (pData=strstr((char*)pBuff, ",5"))!=NULL)//注册成功
			{
				g_sAir780Work.state=AIR780_STAT_CGACT;
				printf("=== +CGREG:1\n");
			}
		}			
		else if((pData=strstr((char*)pBuff, "+CGACT: "))!=NULL && (pData=strstr((char*)pBuff, ",1"))!=NULL)//PDP查询
		{
			g_sAir780Work.state=AIR780_STAT_CGATT0;
			printf("=== AIR780_STAT_CGATT0\n");
		} 
		else if((pData=strstr((char*)pBuff, "+CGATT: 1"))!=NULL)//附着查询
		{
			g_sAir780Work.state=AIR780_STAT_CGATT1;
			printf("=== +CGATT: 1\n");
		}	    
		else if((pData=strstr((char*)pBuff, ", CONNECT OK"))!=NULL)//连接检测  || (pData=strstr((char*)pBuff, ", ALREADY CONNECT"))!=NULL
		{
			u8 sock_id=0;
			pData--;
			printf("### pData=%s\n", pData);
			if(pData[0]>='0' && pData[0]<='9')
			{
				sock_id=pData[0]-'0';
				printf("=== air780 sock_id=%d connect ok!\n", sock_id);
				if(sock_id<AIR780_MAX_LINK_NUM)
				{
					g_sAir780Work.client_list[sock_id].conn_state=true;
					g_sAir780Work.client_list[sock_id].keep_time=drv_get_sec_counter();
				}
			}
			
		}		
		else if((pData=strstr((char*)pBuff, ",CLOSE OK"))!=NULL)//关闭
		{
			u8 sock_id=0;
			pData--;
			if(pData[0]>='0' && pData[0]<='9')
			{
				sock_id=pData[0]-'0';
				printf("=== sock_id=%d close ok!\n", sock_id);
				if(sock_id<AIR780_MAX_LINK_NUM)
				{
					g_sAir780Work.client_list[sock_id].conn_state=false;
				}
			}
		}		
		else if((pData=strstr((char*)pBuff, "+PDP: DEACT"))!=NULL)//PDP失效
		{		
      drv_air780_send_at("CIPSHUT");//去激活
			delay_os(100);
			g_sAir780Work.state=AIR780_STATE_START;
			printf("===RDY net_state=AIR780_STATE_START\n");			
		}	 
		else if(pBuff[2]>='0' && pBuff[2]<='9' && g_sAir780Work.imei_buff[0]==0)//IMEI检测
		{
			bool is_number=true;
			pBuff+=2;
			for(u8 i=0; i<15; i++)
			{
				if(pBuff[i]<'0' || pBuff[i]>'9')
				{
					is_number=false;
					break;
				}
			}
			if(is_number==true)
			{
				memset(g_sAir780Work.imei_buff, 0, sizeof(g_sAir780Work.imei_buff));
				memcpy(g_sAir780Work.imei_buff, pBuff, 15);
				printf("imei=%s\n", g_sAir780Work.imei_buff);
			}
		}
		else if((pData=strstr((char*)pBuff, "+ICCID: "))!=NULL)//ICCID检测
		{
			pData+=strlen("+ICCID: ");
			memset(g_sAir780Work.iccid_buff, 0, sizeof(g_sAir780Work.iccid_buff));
			memcpy(g_sAir780Work.iccid_buff, pData, 20);
			printf("iccid=%s\n", g_sAir780Work.iccid_buff);
		}    
		UART_Clear(g_sAir780Work.pUART);
	}
}

        接收处理的基本思路是根据AT指令返回的关键字进行匹配,得到对应动作的状态值并解析。这里要注意的点是,数据接收"+RECEIVE,"要放在第一个,这样才不会被数据区的内容干扰;定位信息要根据AT指令手册去对应解析,我们只获取了经度、纬度和海拔3个信息,有需要详细的自行添加;还有一个比较重要的场景去激活,简单讲就是信号比较差,4G模组跟基站暂时断开了,这时候是需要重新走一遍流程注册的,所以这个状态要检测,不然数据发送和网络连接都会发生错误的,在产品应用的时候如果忽略这个问题那么很可能导致的后果是大面积失联,客户投诉,市场丢失等问题,这个事件在一些共享充电宝厂商身上发生过的。

        其它也没有什么大问题了,模块的功能很丰富,我们这里只是基本应用,有需要可以根据AT指令手册自己扩展,剩下的就是回调函数注册、初始化等常规操作了。

  

三、AIR780使用注意事项

        最基本的是天线要插,而且要4G的,不要433M的天线插一根,没啥用的,很多胶棒天线长得都一样,自己要记得标识;然后就是4G卡的问题,很多物联网卡是专用的,不能放在手机使用,不能换设备使用,跟第一个4G模块是绑定的,换设备是不能用的,需要联系开卡的客户经理解除绑定,这么做主要是怕卡被拿去非法使用。

        电源问题也是容易被忽略的,模块在网络注册的时候峰值电流比较大,如果电源芯片功率不够的话会导致模块一直重启,在电路设计的时候要严格对照手册进行。

        我这里使用的是串口2进行驱动,自己接线要注意,PA2接模块的RX,PA3接模块的RX,我购买的模块供电是5V的,接线就是5V-TX-RX-GND 4根线就行了,很简单,具体购买链接如下,可作参考。https://item.taobao.com/item.htm?_u=apfmfmg56fd&id=720428359893&spm=a1z09.2.0.0.20d42e8dQqVw5W&skuId=5332410429413

        

四、结合MQTT传输测试

        之前的净化器项目是用WiFi作为网络链路的,我们现在改成4G,操作很简单,这也是为什么我们要模块化封装驱动程序的原因,分级分层,实际做项目的时候根据用户需求随时可以切换方案,维护起来不容易出错,下面看下代码,只要改几行就足够了。      ​​​

        就是把app_mqtt.c文件中的WIFI参数配置改成4G的配置就行了,4G的配置更为简单,不要账户密码什么的。这里因为4G模块必须连接互联网,所以MQTT的服务器用EMQ官方的测试服务器,域名是broker.emqx.io,端口1883,MQTT账户密码随便设置一个就行了,服务器不做检查。用户端我们就不用手机了,直接用客户端工具看下有没有数据发过来就行了。

        很明显这些数据就是从设备端发送出来的。

        4G模组AIR780E的驱动程序暂时就这些了,驱动代码在这里下载:https://download.csdn.net/download/ypp240124016/89110607

本项目的交流QQ群:701889554

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/530801.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker容器嵌入式开发:MySQL表的外键约束及其解决方法

本文内容涵盖了使用MySQL创建数据库和表、添加数据、处理字符集错误、解决外键约束问题以及使用SQL查询数据的过程。通过创建表、插入数据和调整字符集等操作&#xff0c;成功解决了数据库表中的字符集问题&#xff0c;并使用INSERT语句向各个表中添加了示例数据。同时&#xf…

乘苏州金龙客车,览西北无边胜境

2023年&#xff0c;甘肃省共接待游客3.88亿人次&#xff0c;实现旅游收入2745.8亿元&#xff0c;分别较上年同期增长187.8%和312.9%&#xff0c;分别恢复到2019年同期的104%和102.4%。随着旅游市场的持续火爆&#xff0c;甘肃保利旅游客运有限责任公司&#xff08;简称“甘肃保…

Day105:代码审计-PHP原生开发篇SQL注入数据库监控正则搜索文件定位静态分析

目录 代码审计-学前须知 Bluecms-CNVD-1Day-常规注入审计分析 emlog-CNVD-1Day-常规注入审计分析 emlog-CNVD-1Day-2次注入审计分析 知识点&#xff1a; 1、PHP审计-原生态开发-SQL注入&语句监控 2、PHP审计-原生态开发-SQL注入&正则搜索 3、PHP审计-原生态开发-SQ…

vulhub之Webmin篇

Webmin是功能最强大的基于Web的Unix系统管理工具。管理员通过浏览器访问Webmin的各种管理功能并完成相应的管理动作。Webmin支持绝大多数的Unix系统&#xff0c;这些系统除了各种版本的linux以外还包括&#xff1a;AIX、HPUX、Solaris、Unixware、Irix和FreeBSD等。 影响版本&…

Codigger Desktop:使用体验与获得收益双赢的革新之作(二)

昨天&#xff0c;我们介绍了Codigger Desktop的最大亮点在于&#xff0c;它不仅仅是一个普通的桌面应用程序&#xff0c;更是一个能够产生实际价值的平台。无论您是开发者还是使用者&#xff0c;Desktop都能给您带愉快体验&#xff1a; 首先&#xff0c;Codigger Desktop具备零…

51单片机入门_江协科技_25~26_OB记录的笔记_蜂鸣器教程

25. 蜂鸣器 25.1. 蜂鸣器介绍 •蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常用来产生设备的按键音、报警音等提示信号 •蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器&#xff08;开发板上用的无源蜂鸣器&#xff09; •有源蜂鸣器&#xff1a;内部自带振荡源&a…

卡塔尔世界杯中的“先进技术”

2010年&#xff0c;卡塔尔赢得了世界杯的主办权&#xff0c;在这个年轻而雄心勃勃的国家历史上留下了金色的足迹。从那时起&#xff0c;卡塔尔开始与时间赛跑&#xff0c;以履行东道主的义务&#xff0c;并履行组织一届特殊世界杯的承诺&#xff0c;这是世界上最重要的体育赛事…

P5356 [Ynoi2017] 由乃打扑克

我手把手教她打扑克 qwq 综合分析一下2个操作&#xff0c;查找区间第k小的值&#xff0c;感觉可以用主席树&#xff0c;区间修改那没事了 考虑分块做法,块长B 分析第一个操作 只需要维护数列的单调性&#xff0c;然后二分答案上二分就ok了 分析第二个操作 维护一个加法懒…

抖音电商发布2024春茶消费数据报告,90后00后占春茶消费数量四成

清明已过&#xff0c;茶香正浓。进入4月&#xff0c;各类春茗进入销售旺季。近日&#xff0c;抖音电商发布《2024春茶消费数据报告》&#xff08;以下简称“报告”&#xff09;&#xff0c;展示平台用户在原叶茶消费领域的最新趋势。 报告显示&#xff0c;在刚过去的3月&#…

dayjs 判断是否今天、本周内、本年内、本年外显示周几、月份等

效果: 判断是否今天需从 dayjs 中引入 isToday 插件&#xff1b; 判断是否两个日期之间需从 dayjs 中引入 isBetween 插件 import dayjs from dayjs import isToday from dayjs/plugin/isToday import isBetween from dayjs/plugin/isBetween// 注册插件 dayjs.extend(isBet…

论文学习D2UNet:用于地震图像超分辨率重建的双解码器U-Net

标题&#xff1a;&#xff1a;Dual Decoder U-Net for Seismic Image Super-Resolution Reconstruction ——D2UNet&#xff1a;用于地震图像超分辨率重建的双解码器U-Net 期刊&#xff1a;IEEE Transactions on Geoscience and Remote Sensing 摘要&#xff1a;从U-Net派生…

Python实现滑块验证码识别,最简单的一种,没有任何加密

网址链接&#xff1a;衣丰 & 2010-聚衣网(juyi5.cn) - 常熟市聚衣网&#xff0c;聚衣网女装&#xff0c;江苏省女装批发&#xff0c;苏州市女装批发&#xff0c;常熟市女装批发&#xff0c;网销女装一件代发&#xff0c;全国最低价 平时采集数据&#xff0c;频率过快&…

2024年租用阿里云服务器一年多少钱?免费版不要钱,收费版61元起

2024年阿里云服务器一年多少钱&#xff1f;如果是申请试用版云服务器就不需要钱&#xff0c;如果是购买收费版目前最低仅需61元&#xff0c;不同阶段&#xff0c;阿里云所推出的最低价格的云服务器不同&#xff0c;2024年4月份&#xff0c;阿里云服务器的活动价格在再次更新了&…

Ubuntu 20.04.06 PCL C++学习记录(二十一)【切记使用rm * -rf前先确认是否是对应文件夹】

[TOC]PCL中点云分割模块的学习 学习背景 参考书籍&#xff1a;《点云库PCL从入门到精通》以及官方代码PCL官方代码链接,&#xff0c;PCL版本为1.10.0&#xff0c;CMake版本为3.16&#xff0c;测试点云下载地址 学习内容 根据欧几里得距离和需要保持的用户可自定义条件对点进…

8. 托盘图标与菜单

内容概要&#xff1a; 托盘图标的设置与事件 右键菜单的相关操作 窗口组件&#xff1a; 1.组件的属性 组件属性&#xff1a;位置 组件属性&#xff1a;可视 2.组件的事件 窗口_托盘事件-带有参数的事件的使用方法 3.组件的方法 置托盘图标 菜单的操作 1.创建菜单 …

大话设计模式——20.解释器模式(Interpreter Pattern)

简介 给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解释器使用该表示来解释语言中的句子 UML图 应用场景 某种特定类型的问题发生的频率足够多&#xff0c;就可能值得将该问题的各个实例表述为一个简单语言中的句子&#xff0…

关于pandas 无法读取 csv 文件数据的解决方式

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 …

词频统计程序

使用Hadoop MapReduce处理文本文件&#xff0c;Mapper负责将文本分割为单词&#xff0c;然后Reducer对每个单词进行计数&#xff0c;最后将结果写入输出文件。 // 定义WordCount公共类 public class WordCount {// 主入口方法&#xff0c;处理命令行参数public static void m…

C++实现幻方实验

我们这个实验目的是实现大于2的奇数的n阶幻方 根据上述的例子我们可以看到一些规律&#xff0c;显示1放在最上方中间的位置&#xff0c;然后向右上方延申&#xff0c;在达到n这个数字时&#xff0c;停止延申&#xff0c;然后在n的下方开始n1的新一轮延申。明白了原理之后就很容…

羊大师说:“羊奶”,每一滴都值得珍惜

亲爱的读者们&#xff0c;我是羊大师。在无数次探索自然的奥秘和追求健康生活的旅途中&#xff0c;我发现了一种珍贵的液体——羊奶。今天&#xff0c;我要带大家深入了解羊奶&#xff0c;看看它是如何成为餐桌上的超级食品。 1. 羊奶的营养价值 首先&#xff0c;羊奶含有丰富…