前几天发布了开源4.3寸触摸屏的文章 《开源4.3寸触摸屏》,里面有WIFI获取时间和天气预报相关的功能,今天就来介绍一下这个功能是怎样实现的。
首先,硬件上,单片机通过串口AT指令访问WIFI模块(ESP12S)。具体AT指令怎样是写,可以参考之前的文章《AT指令程序的编写》。上电后,先配置WIFI模块的工作模式等,用到的AT指令如下:
1. 关闭回显
ATE0
2. 设置为STA模式
AT+CWMODE_DEF=1
3. 设置WIFI名称和密码
AT+CWJAP_DEF=”SSID”,”Password”
4. 开启DHCP
AT+CWDHCP_DEF=1,1
5. 设置为单连接模式
AT+CIPMUX=0
6. 透传模式
AT+CIPMODE=1
获取网络时间的方式有两种,对于较新版本的固件,可以直接通过AT指令来获取。(可以通过AT+GMR指令来查询固件的版本。)
AT+CIPSNTPTIME?
对于较老版本的固件,不支持该AT指令,只能通过自己链接网络服务器来获取当前时间。具体需要的AT指令如下:
1.建立TCP链接
AT+CIPSTART="TCP","www.beijing-time.org",80
2.打开传透模式
AT+CIPSEND
3.收到‘>’符号后,随便发送个数据
4.等待接收数据
接收的数据可以先用网络调试助手来测试,如下,可以看大,数据中包含了当前的日期和时间信息,我们可以先找到字符串中“Data”的位置,再解析后面的数据。数据格式固定,直接按照字符位置解析就行。
需要注意的是,获取的这个时间是格林威治时间,与北京时间相差8个小时。
当然,也可以通过连接其它NTP时间服务器来获取时间,步骤是类似的。

天气的获取就要比时间稍复杂一些,网上有很多可以提供天气预报的服务器,有些可以免费试用,一般都需要注册一个账号,这里以“心知天气”为例进行介绍。步骤如下:
1.在该网站注册一个账号:
心知天气 - 高精度气象数据 - 天气数据API接口 - 行业气象解决方案。
注册之后就可以通过其开放的API来获取天气预报。有免费版和付费版,区别在于访问频次和获取的天气信息内容不同。如下:

2.在控制台查看账号的私钥,这个在程序用要用。

3.有了这个私钥,我们就可以通过API来获取天气预报了,相关的API可以在这里查询:
先用网络调试助手测试一下:

可以看到,通过TCP连接服务器后,发送GET请求就可以收到天气预报数据,接下来就是解析数据了。
4.接收的数据是JSON格式的字符串,在单片机中我们可以移植cJSON来进行解析。cJSON是一个开源的C语音解析库,只有cJSON.c、cJSON.h两个文件,直接添加到工程中就可以使用相关函数。程序如下:
void Weather_JSON( )
{
char *data;
cJSON *root;
cJSON *results;
cJSON *last_update;
cJSON *loc_json, *daily_json;
cJSON *forecast_json;
char *loc_tmp, *weather_tmp;
int i = 0;
data = (char*)(Uart1.RxBuf);//接受到的数据
root = cJSON_Parse(data);
if(root)
{
//printf("JSON格式正确:\n%s\n\n",cJSON_Print(root)); //输出json字符串
results = cJSON_GetObjectItem(root, "results");
results = cJSON_GetArrayItem(results,0);
if(results)
{
loc_json = cJSON_GetObjectItem(results, "location"); //得到location键对应的值,是一个对象
loc_tmp = cJSON_GetObjectItem(loc_json, "id") -> valuestring;
//printf("城市ID:%s\n",loc_tmp);
loc_tmp = cJSON_GetObjectItem(loc_json, "name") -> valuestring;
memset(loc_str,0,20);
memcpy(loc_str,loc_tmp,strlen(loc_tmp));
loc_tmp = cJSON_GetObjectItem(loc_json, "timezone") -> valuestring;
//printf("城市时区:%s\n\n",loc_tmp);
daily_json = cJSON_GetObjectItem(results, "daily");
if(daily_json)
{
Weather_Dat[0].Flag = 1;
for(i = 0; i < 3; i++)
{
forecast_json = cJSON_GetArrayItem(daily_json, i);
//weather_tmp = cJSON_GetObjectItem(forecast_json, "date") -> valuestring;//日期
weather_tmp = cJSON_GetObjectItem(forecast_json, "code_day") -> valuestring;//白天天气代码
Weather_Dat[i].Weathcode = atoi(weather_tmp);
//weather_tmp = cJSON_GetObjectItem(forecast_json, "code_night") -> valuestring;//晚上天气代码
weather_tmp = cJSON_GetObjectItem(forecast_json, "high") -> valuestring;//最高温度
memset(Weather_Dat[i].HighT,0,4);
memcpy(Weather_Dat[i].HighT,weather_tmp,strlen(weather_tmp));
weather_tmp = cJSON_GetObjectItem(forecast_json, "low") -> valuestring;//最低温度
memset(Weather_Dat[i].LowT,0,4);
memcpy(Weather_Dat[i].LowT,weather_tmp,strlen(weather_tmp));
weather_tmp = cJSON_GetObjectItem(forecast_json, "wind_direction") -> valuestring;//风向
memset(Weather_Dat[i].Wind_Dir,0,10);
memcpy(Weather_Dat[i].Wind_Dir,weather_tmp,strlen(weather_tmp));
weather_tmp = cJSON_GetObjectItem(forecast_json, "wind_scale") -> valuestring;//风力
memset(Weather_Dat[i].WindScale,0,4);
memcpy(Weather_Dat[i].WindScale,weather_tmp,strlen(weather_tmp));
weather_tmp = cJSON_GetObjectItem(forecast_json, "humidity") -> valuestring;//湿度
memset(Weather_Dat[i].Humi,0,4);
memcpy(Weather_Dat[i].Humi,weather_tmp,strlen(weather_tmp));
}
}
else
{
//printf("daily json格式错误\r\n");
Weather_Dat[0].Flag = 0;
}
// last_update = cJSON_GetObjectItem(results, "last_update");
// update_tmp = last_update->valuestring;
// if(last_update)
// {
// //printf("更新时间:%s\r\n", update_tmp);
// }
}
else
{
Weather_Dat[0].Flag = 0;
//printf("results格式错误:%s\r\n", cJSON_GetErrorPtr());
}
}
else
{
Weather_Dat[0].Flag = 0;
//printf("JSON格式错误\r\n");
}
cJSON_Delete(root);
}
解析后,将有用数据转换为自己想要的格式存储在变量中。需要注意的是,cJSON可能占用较大的堆栈,因此堆栈需要设大一些。
总结一下,获取天气预报的步骤如下:
1.注册账号
2.获取私钥
3.连接服务器
AT+CIPSTART="TCP”,” api.seniverse.com”,80
4..打开传透模式
AT+CIPSEND
5..收到‘>’符号后,发送GET请求
6.等待接收数据,并解析
以上就是单片机通过AT指令访问WIFI模块获取网络时间和天气预报的方法,需要注意的是上述所有指令后面别忘了加回车换行符。
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co