【PersimmonUI柿饼学习营】+ A Byte of China + 网络/串口/天气预报

发布于 2018-10-07 20:06:55
最后一周作业:基于网络和串口的天气预报
实现以下板块:
1,日历板块:要求从网上获取当天各类实时信息,如天气,黄历信息等,显示并记录下来。
2,分析板块:根据日历板块记录的数据信息,使用画布制作出数据分析图(如柱状图等)。
3,能通过串口输入指定日期(一天,或者连续几天),并从串口返回出相应的数据。

开发环境
RTT + PerimmonUI + 野火RT1052Mini + 5寸800*480触屏
TIM截图20181007194820.png

开发过程
本次作业的关键是网络和串口,本次终于可以从UI接触到硬件交互,作业可以分成3部分:1、网络;2、数据展示;3串口。1、网络的重点在于使用网络API接口获取需要的数据,通过GET方法我们获取到JSON格式的天气预报,然后对格式进行处理,获得我们需要的对应数据(高低温,天气,风速等)
2、数据展示使用画布来实现,灵感来自折线图,两条折现展示近三天的高低温,三天中的天气通过背景色来区别(晴天为蓝色,多云为灰色,其他为绿色),同时利用定时器回调函数,可以实现折线上升的动画效果;关于城市选择我们通过card控件+城市图片来实现,通过card值来切换城市,进而改变天气预报API地址以获取对应城市的天气,当然获取到的当天天气会通过label展示在预报区之上,整体布局如下。
TIM截图20181007194621.png
3、串口需要解决的问题是对接收到的报文进行组帧在分组,串口询问天气的固定格式为“yyyy-mm-dd n” (yyyy-mm-dd为起始日期,n为1~3,表示需要的天数,因天气预报API限制,只能获取当前三天的数据,如起始日期为明天,需要三天,我们只能获得两天的数据)。获得固定格式的串口数据请求,再将相应的数据返回到串口,如果数据格式有误,会提示正确格式模板。

具体请详见JS源码
app.js源码,主要实现uart串口应用
var strcon_flg = 0;
var str1
var str2
var app = {
page : "page1/page1",

onLaunch: function (event) //app加载回调函数
{
console.log('app onLaunch');

uart = pm.openSerialPort({device: "uart5"}); //打开串口设备uart5
if (uart)
{
console.log('pm.openSerialPort OK');
uart.write(Buffer("Please input the date: e.g '2018-10-01 3' which means 3 days data from 2018-10-01 ", 'ascii')); //串口发送数据,一个ascii字符串数据BUFF
uart.onData(this.onUart, this); //设置串口接收函数为 onUart
}
},

onUart : function (data) //串口接收数据回调函数,data 为串口数据
{
if(strcon_flg == 0)
{
strcon_flg = 1;
str1 = data.toString('ascii')
return;
}
else
{
str2 = data.toString('ascii');
strcon_flg = 0;
}
var str3 = str1.concat(str2)
console.log(str3)
str3 = str3.substring(str3.indexOf("2"))
console.log(str3)
var udate = str3.split(" ")
console.log(udate)
var x = udate[1]
for(var i = 0; i < 3; i++)
{
console.log(udate[0],' vs ',date
    )
    console.log(x)
    if(udate[0] == date
      )
      break
      }
      if((i >= 3)||(parseInt(x) > 3))
      {
      var str_err = "Invalid date(recent 3 days) or format(2018-10-01 3) "
      console.log(str_err)
      uart.write(Buffer(str_err, 'ascii'));
      }
      else
      {
      var n = parseInt(x);
      if(i+n > 3)
      {
      n = 3;
      }
      else
      {
      n = i+n
      }
      for(var j = i; j < n; j++)
      {
      var strj = date[j]+ ' High Temperature: '+ high1[j] + ' Low Temperature:' + low1[j] + ' \r\n';
      console.log(strj)
      uart.write(Buffer(strj, 'ascii'));
      }
      }
      }
      };

      App(app);

page1.js源码,主要实现网络和显示
var date = ['2018-09-29','2018-09-30','2018-10-01']
var CITIES = ['beijing','shanghai','guangzhou','nanjing','new']
var City_name = 'Beijing'
var code = ['Sunny','Cloudy','Snowy']
var CODE = ['Sunny','Cloudy','Snowy']
var DIRECTION = ['N','E','W','S','NE','NW','SE','SW']
var wind_s = ['20','10','30']
var wind_l = ['2','1','3']
var wind_d = ['W','E','N']
var high = [0, 0, 0]
var low = [0, 0, 0]
var high1 = [25, 26, 24]
var low1 = [15, 10, 17]

var DISTANCE = 110

var jsons = '{"results":[{"location":{"id":"WX4FBXXFKE4F","name":"Beijing","country":"CN","path":"Beijing,Beijing,China","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"daily":[{"date":"2018-09-30","text_day":"Cloudy","code_day":"4","text_night":"Cloudy","code_night":"4","high":"19","low":"13","precip":"","wind_direction":"NW","wind_direction_degree":"315","wind_speed":"20","wind_scale":"4"},{"date":"2018-10-01","text_day":"Sunny","code_day":"0","text_night":"Sunny","code_night":"1","high":"24","low":"12","precip":"","wind_direction":"N","wind_direction_degree":"0","wind_speed":"20","wind_scale":"4"},{"date":"2018-10-02","text_day":"Sunny","code_day":"0","text_night":"Sunny","code_night":"1","high":"24","low":"11","precip":"","wind_direction":"N","wind_direction_degree":"0","wind_speed":"10","wind_scale":"2"}],"last_update":"2018-09-30T11:00:00+08:00"}]}'
var json_obj = JSON.parse(jsons)

var s1 = ';
var city = 'beijing'
var s2 = '&language=en&unit=c&start='
var day = '0'
var s3 = '&days=3'
var url_w = s1+city+s2+day+s3

var ctx_1 = ' Temp:'
var ctx_2 = '~'
var ctx_3 = ' Wind Speed:'
var ctx_4 = ' Wind Direction:'
var ctx_5 = ' Wind Scale: '
var ctx_0 = ' Date:'
var i = 0;
var j = 0;
var dly = 195;
var ani_flg = 0;

var page = {

dataa : {timer1 : 0},

onLoad : function()
{
var thiz = this;

// Register a timer callback function
this.dataa.timer1 = setInterval(function()
{
if((ani_flg & 0x0F) == 0)
{
for(var k = 0; k < high.length; k++)
{
if(high[k] < high1[k]){
high[k] += Math.round(high1[k]/5)
}
else
{
ani_flg |= 0x10;
}
if(low[k] < low1[k]){
low[k] += Math.round(low1[k]/5)
}
else
{
ani_flg |= 0x20;
}
}

thiz.display();
if((ani_flg & 0xF0) == 0x30)
{
ani_flg = 0xFF;
}
}
if(dly >= 200)
{
//thiz.onUpdate()
thiz.getSkyInfo()
dly = 0;
}
dly++
}, 50);
this.getSkyInfo();
this.display()
//this.onUpdate()
},

getRndW : function()
{
for(var k = 0; k < high.length; k++)
{
high[k] = 0;
low[k] = 0;
high1[k] = Math.floor(Math.random()*5+20)
low1[k] = Math.floor(Math.random()*5+10)
code[k] = CODE[Math.floor(Math.random()*3)]
wind_s[k] = Math.floor(Math.random()*20)
wind_l[k] = Math.floor(Math.random()*10)
wind_d[k] = DIRECTION[Math.floor(Math.random()*8)]
var ctx = 'Zootopia'+' '+code[0]+ctx_1+low1[0]+ctx_2+high1[0]+ctx_0+date[0]
var ctxw = ctx_3+wind_s[0]+ctx_4+wind_d[0]+ctx_5+wind_l[0]
this.setData({label1: { value : ctx , refresh : true}});
this.setData({label2: { value : ctxw, refresh : true}});
ani_flg = 0;
}
},

display : function()
{
var context = pm.createCanvasContext('Canvas1', this) //获取画布对象

if (context)
{
var max = 36;

for(var j = 0; j < 3; j++)
{
grd = context.createLinearGradient(0, 0, 0, 480)
grd.addColorStop(0, 'black')
if(code[j] == 'Sunny')
{
grd.addColorStop(1, '#FF9090FF')
}else if(code[j] == 'Cloudy')
{
grd.addColorStop(1, 'GRAY')
}else
{
grd.addColorStop(1, '#FFB9FFB9')
}
// Fill with gradient
context.setFillStyle(grd)
context.fillRect(60 + j * DISTANCE, 30, DISTANCE , 350)
}

max = Math.floor(max / 6);

/* 画浅灰色竖线竖线 */
context.beginPath(); //开启新路径
context.setStrokeStyle('#C0C0C0') //设置边框颜色

for( index = 1; index <= 3; index++ )
{
var y = 380;
context.moveTo(60 + index * DISTANCE, y) //线条起点
context.lineTo(60 + index * DISTANCE, 30) //线条终点
}

context.stroke() //画线
context.closePath(); //关闭当前路径

/* 画横纵坐标刻度 */
context.beginPath(); //开启新路径
context.setStrokeStyle('black') //设置边框颜色

context.fillText('Temperature', 5, 5) //绘制文字,坐标(5,5)

context.setTextBaseline('middle') //设置文本绘制纵坐标对齐方式
for( index = 0; index < 7; index++ )
{
var y = 30 + index * 50;
var value = max * (7 - index);
/*纵坐标刻度*/
context.moveTo(55, y)
context.lineTo(60, y)
context.fillText(value.toString(), 20, y)
}

context.moveTo(55, 30 + index * 50)
context.lineTo(60, 30 + index * 50)
context.fillText('0', 20, 30 + index * 50)

context.moveTo(60, 30)
context.lineTo(60, 30 + index * 50)

context.moveTo(60, 30 + index * 50)
context.lineTo(60 + 3 * DISTANCE, 30 + index * 50)

context.setTextAlign('center') //设置文本绘制横坐标对齐方式

for( index = 1; index <= 3; index++ )
{
var x = 60 + (index-0.5) * DISTANCE;
/*横坐标刻度*/
context.moveTo(x, 380)
context.lineTo(x, 385)
context.fillText(date[index-1], x, 395)
}

context.setTextAlign('left') //设置文本绘制横坐标对齐方式
context.fillText('Date', 60 + (low.length) * DISTANCE + 10 , 395)
context.stroke()
context.closePath();



/* High temp */
var x = 60 + DISTANCE/2;
context.moveTo(x, 380)
context.beginPath();
context.setLineCap('round')
context.setStrokeStyle('#FFFF6969')
context.setLineWidth(4) //设置线条宽度
for( index = 0; index < high.length; index++ )
{
var x = 60 + DISTANCE/2 + index * DISTANCE;
var y = high[index] * 350 / (max * 7)
context.lineTo(x, 380 - y)
context.arc(x, 380 - y, 3, 0, 2 * Math.PI)
}
context.stroke()
context.closePath();



/* Low temp */
var x = 60 + DISTANCE;
context.moveTo(x, 380)
context.beginPath();
context.setStrokeStyle('#FF6969FF')
for( index = 0; index < low.length; index++ )
{
var x = 60 + DISTANCE/2 + index * DISTANCE;
var y = low[index] * 350 / (max * 7)
context.lineTo(x, 380 - y)
context.arc(x, 380 - y, 3, 0, 2 * Math.PI)
}
context.stroke()
context.closePath();

// Value
context.setTextAlign('center')
context.setFillStyle('white')
for( index = 0; index < 3; index++ )
{
var x = 60 + DISTANCE/2 + index * DISTANCE;
var y = high[index] * 350 / (max * 7)
context.fillText(high[index].toString(), x, 365-y)

var x = 60 + DISTANCE/2 + index * DISTANCE;
var y = low[index] * 350 / (max * 7)
context.fillText(low[index].toString(), x, 395-y)

var x = 60 + DISTANCE/2 + index * DISTANCE;
context.fillText(code[index], x, 50)
}

context.draw();

}
},
onUpdate : function(){ //根据json_obj中的数据,来设定对应Label的值
for(var i = 0; i < 3; i++){
high
    = 0;
    low
      = 0;
      City_name = json_obj.results[0].location.name
      date
        = json_obj.results[0].daily
          .date
          code
            = json_obj.results[0].daily
              .text_day
              high1
                = json_obj.results[0].daily
                  .high
                  low1
                    = json_obj.results[0].daily
                      .low
                      wind_d
                        = json_obj.results[0].daily
                          .wind_direction
                          wind_s
                            = json_obj.results[0].daily
                              .wind_speed
                              wind_l
                                = json_obj.results[0].daily
                                  .wind_scale
                                  var ctx = City_name+' '+code[0]+ctx_1+low1[0]+ctx_2+high1[0]+ctx_0+date[0]
                                  var ctxw = ctx_3+wind_s[0]+ctx_4+wind_d[0]+ctx_5+wind_l[0]
                                  this.setData({label1: { value : ctx , refresh : true}});
                                  this.setData({label2: { value : ctxw, refresh : true}});
                                  ani_flg = 0;
                                  }
                                  },

                                  getSkyInfo: function(e) {
                                  var thiz = this;
                                  var str =0;
                                  url_w = s1+city+s2+day+s3
                                  console.log(url_w)
                                  var rq1 = pm.request({
                                  url: url_w, // 获取天气预报的API
                                  method : 'GET',
                                  header:{
                                  "Content-Type":"application/json"
                                  },
                                  success: function(res) { //与开发者服务器连接成功后,执行的回调函数
                                  str = res.data.toString('utf8'); // 把data从Buffer转成string
                                  json_obj=JSON.parse(str); //把JSON格式的string转成JSON对象,以便获取数据
                                  thiz.onUpdate(); //更新各个控件的值
                                  },
                                  fail: function(){
                                  console.log('request failed')
                                  }
                                  });
                                  },


                                  onChange : function(e){
                                  console.log(e.detail.value)
                                  console.log(CITIES[e.detail.value])
                                  dly = 0;

                                  if(CITIES[e.detail.value] == 'new')
                                  {
                                  this.getRndW()
                                  }
                                  else
                                  {
                                  city = CITIES[e.detail.value]
                                  //this.onUpdate()
                                  this.getSkyInfo()
                                  }
                                  }
                                  };

                                  Page(page);


效果展示
仿真的界面效果
wf.gif
板子上跑的效果
l5-v.gif
串口效果
l5-2.gif


总结
本周作业有了硬件的内容,更加嵌入式了,具体功能细节还有待研究。
网络功能很方便,使用内置的http的get方法可以很轻松获取网络资源,小伙伴们可以结合《网络编程学习营》的内容进一步探索更有趣更实用的功能。
串口的使用也很简单,还考虑到串口数据与各个page的交互,这个很不错。
我的js编程还不是很熟,还是用面向过程的方式编写代码,相信开发js的同学来使用柿饼的话应该能写出更高效优美的代码。

这是最后一课,用一句话总结:柿饼通过所见即所得的开发方式和便捷易学的JS脚本语言编程模式颠覆了UI的开发!
整过学习过程的代码我都有放到个人的github中,欢迎一起交流 https://github.com/ianhom/JavaScript-Noob/tree/master/app/PersimmonUI
熟练使用好柿饼,还有其他效果哦,下图是我使用柿饼快速制作的冒泡排序动图,是不是很方便呢。
后面柿饼正式发布后,期待更多的小伙伴们优秀作品!
感谢柿饼,感谢RT-Thread!!
RT-Thread总会给我们相聚的机会,让我们下次再见吧~
bubblesort.gif

柿饼工程
下载附件[hw5-3-4.rar]








查看更多

关注者
0
被浏览
1.3k
2 个回答
bernard
bernard 2018-10-07
ABC大佬厉害!而且这次又是第一个交作业!
flyboy
flyboy 2018-10-10
顶一下,看着很流畅,而且这个排序很精髓:loveliness:

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友