• 继续我们的天气程序


    继续上一篇的关于天气程序的几个问题

    1   原来是单个汉字单个汉字单蹦显示。想显示多个数字很麻烦。进行了修改

    2   重复请求天气,我想请求两个地方的天气。这个地方给我很大的困扰,研究了好久一直没找到具体原因,直到今天,看了相关http请求的相关内容,才明白了原理。

    OK。我们首先贴一下 代码的方法介绍

    首先,要获取请求,在wiff建立的情况下,就是wiffclient进行请求。记住了,这个东西就是个http请求的玩意。基本上差不多的东东。无论是get or post。查看相关的信息就可以进行处理。这点毋庸置疑。包括给相关的服务端发送json数据等,都是通过这个来操作。

    那我们首先就是要介绍wiffclient的相关方法。这个方法真的很重要

    3.1.1 connect - 启动tcp连接
    函数说明:
    /**
    * 建立一个tcp连接
    * @param ip IPAddress of tcpserver
    * @param port port of tcpserver
    * @return result of tcp connect
    * 1 --- success
    * 0 --- fail
    */
    int connect(IPAddress ip, uint16_t port);

    /**
    * 建立一个tcp连接
    * @param host host of tcpserver (192.xx.xx.xx)
    * @param port port of tcpserver
    * @return result of tcp connect
    * 1 --- success
    * 0 --- fail
    */
    int connect(const char *host, uint16_t port)

    /**
    * 建立一个tcp连接
    * @param host host of tcpserver (192.xx.xx.xx)
    * @param port port of tcpserver
    * @return result of tcp connect
    * 1 --- success
    * 0 --- fail
    */
    int connect(const String host, uint16_t port);
    1234567891011121314151617181920212223242526272829
    3.1.2 connected - 判断client是否还在连接
    函数说明:
    /**
    * 判断tcp连接是否建立起来(ESTABLISHED)
    * @return result of tcp connect
    * 1 --- success
    * 0 --- fail
    */
    uint8_t connected();
    1234567
    3.1.3 stop - 停止tcp连接
    函数说明:
    /**
    * 关闭tcp连接
    */
    void stop();
    1234
    3.1.4 status - 连接状态
    函数说明:
    /**
    * 获取tcp连接状态
    * @return result of tcp connect
    * CLOSED = 0,
    * LISTEN = 1,
    * SYN_SENT = 2,
    * SYN_RCVD = 3,
    * ESTABLISHED = 4,
    * FIN_WAIT_1 = 5,
    * FIN_WAIT_2 = 6,
    * CLOSE_WAIT = 7,
    * CLOSING = 8,
    * LAST_ACK = 9,
    * TIME_WAIT = 10
    */
    uint8_t status();
    12345678910111213141516
    3.2 发送数据操作
    发送操作的源码可以查阅 Print.cpp
    3.2.1 write - 发送数据到client连接的server
    函数说明:
    /**
    * 发送数据
    * @param str 需要单个字节
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t write(uint8_t);

    /**
    * 发送数据
    * @param str 需要发送字符串或者字符数组
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t write(const char *str);

    /**
    * 发送数据
    * @param buffer 需要发送字符串或者字符数组
    * @param size 数据字节数
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t write(const char *buffer, size_t size)

    /**
    * 发送数据
    * @param stream 数据流,比如文件流
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t write(Stream& stream);

    1234567891011121314151617181920212223242526272829
    注意点:

    write(uint8_t)函数是发送数据的底层方法,也就是说print、println底层也是调用write;
    write(const char *str) 函数底层是调用 write(const char *buffer, size_t size),通过strlen计算长度;

    size_t write(const char *str) {
    if(str == NULL)
    return 0;
    return write((const uint8_t *) str, strlen(str));
    }
    12345
    3.2.2 print - 发送数据到client连接的server
    函数说明:
    /**
    * 发送数据
    * @param FlashStringHelper 需要发送的字符串,字符串存在flash中(PROGMEM)
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t print(const __FlashStringHelper *);

    /**
    * 发送数据
    * @param String 需要发送的字符串,字符串存在内存中
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t print(const String &);

    /**
    * 发送数据
    * @param String 需要发送的字符数组,字符数组存在内存中
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t print(const char[]);

    /**
    * 发送数据
    * @param String 需要发送的字符
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t print(char);


    /**
    * 发送数据
    * @param String 需要发送的数据,多是数字,转成对应的进制,一般都是传输数字型数据
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    12345678910111213141516171819202122232425262728293031323334353637383940
    注意点:

    读者需要特别关注 print(const __FlashStringHelper *) 这个函数,以后代码内存优化需用用到;
    常见用法:

    //实例代码 非完整代码 不可直接使用 理解即可
    WiFiClient client;
    client.print( F("This is an flash string")); //字符串“This is an flash string”存在于flash
    123
    3.2.3 println - 发送数据到client连接的server
    函数说明:
    /**
    * 发送数据,并且加上换行符 " "
    * @param FlashStringHelper 需要发送的字符串,字符串存在flash中(PROGMEM)
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(const __FlashStringHelper *);

    /**
    * 发送数据,并且加上换行符 " "
    * @param String 需要发送的字符串,字符串存在内存中
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(const String &s);

    /**
    * 发送数据,并且加上换行符 " "
    * @param String 需要发送的字符数组,字符数组存在内存中
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(const char[]);

    /**
    * 发送数据,并且加上换行符 " "
    * @param String 需要发送的字符
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(char);

    /**
    * 发送数据,并且加上换行符 " "
    * @param String 需要发送的数据,多是数字,转成对应的进制,一般都是传输数字型数据
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);

    /**
    * 发送换行符 " "
    * @return size_t 成功写入发送缓冲区的字节数
    */
    size_t println(void);
    123456789101112131415161718192021222324252627282930313233343536373839404142434445
    注意点:

    println系列其实就是在print系列的基础上加上了换行符 “ ”;

    3.3 响应操作
    3.3.1 available() - 返回接收缓存区可读取字节数
    函数说明:
    /**
    * 返回接收缓存区可读取字节数
    * @return int 接收缓冲区可读取字节数
    */
    int available();
    12345
    注意点:

    通过此方法,我们可以判断发送出去的请求是否有响应信息;

    3.3.2 availableForWrite() - 返回发送缓冲区剩余可写字节数
    函数说明:
    /**
    * 返回发送缓冲区剩余可写字节数
    * @return int 发送缓冲区剩余可写字节数
    */
    size_t availableForWrite();
    12345
    注意点:

    一般来说,调用发送数据操作之后,并不会立刻发送出去,而是把数据放入发送缓冲区,通过机制不断读取发送缓冲区的数据不断发送出去;
    可以通过此函数判断请求是否发送完毕;

    3.3.3 read() - 读取接收缓冲区一个字节
    函数说明:
    /**
    * 读取接收缓冲区一个字节
    * @return int 一字节数据
    */
    int read();
    12345
    注意点:

    此函数读取完数据后,会把该数据从缓冲区清掉;

    3.3.4 read(buf,size) - 读取接收缓冲区size大小的字节数据
    函数说明:
    /**
    * 读取接收缓冲区size大小的字节数据
    * @param buf 数据存储到该buf
    * @param size 读取大小
    * @return int 成功读取的大小
    */
    int read(uint8_t *buf, size_t size);
    1234567
    注意点:

    此函数读取完数据后,会把该数据从缓冲区清掉;

    3.3.5 peek() - 读取接收缓冲区一个字节
    函数说明:
    /**
    * 读取接收缓冲区一个字节
    * @return int 一字节数据
    */
    int peek();
    12345
    注意点:

    此函数读取完数据后,不会把该数据从缓冲区清掉,所以需要特别关注这一点;

    3.3.6 peekBytes(buf,size) - 读取接收缓冲区size大小的字节数据
    函数说明:
    /**
    * 读取接收缓冲区length大小的字节数据
    * @param buffer 数据存储到该 buffer
    * @param length 读取大小
    * @return size_t 成功读取的大小
    */
    size_t peekBytes(uint8_t *buffer, size_t length);
    size_t peekBytes(char *buffer, size_t length);
    12345678
    注意点:

    此函数读取完数据后,不会把该数据从缓冲区清掉,所以需要特别关注这一点;

    3.3.7 readStringUntil - 读取响应数据直到某个字符串为止
    函数说明:
    /**
    * 读取响应数据直到某个字符串为止
    * @param end 结束字符
    * @return String 读取成功的字符串
    */
    String readStringUntil(char end);
    123456
    3.3.8 find - 查找某个字符串
    函数说明:
    /**
    * 判断是否存在某个目标字符串
    * @param buffer 目标字符串
    * @return bool 存在返回true
    */
    bool find(char *buffer);
    123456
    注意点:

    此函数会把数据从缓冲区清掉;

    3.3.9 flush - 清除接收缓冲区
    函数说明:
    /**
    * 清除缓冲区
    */
    void flush(void);
    1234
    注意点:

    新版本flush功能是等待缓冲区中的所有传出字符都已发送。所以做不了清除缓冲区的作用;
    可以有以下代替:

    while(client.read()>0);
    1
    方法要点

    博主建议大家尽量用批量处理的方法,比如 readStringUntil、read(buf,size)、peekBytes(buf,length),性能方面会好很多;
    博主通过查看源码,发现client的发送缓冲区的大小是256Bytes;

    3.4 普通设置
    3.4.1 setNoDelay - 是否禁用 Nagle 算法。
    函数说明:
    /**
    * 是否禁用 Nagle 算法。
    * @param nodelay true表示禁用 Nagle 算法
    */
    void setNoDelay(bool nodelay);
    12345
    底层源码:
    void setNoDelay(bool nodelay)
    {
    if(!_pcb) {
    return;
    }
    if(nodelay) {
    tcp_nagle_disable(_pcb);
    } else {
    tcp_nagle_enable(_pcb);
    }
    }
    1234567891011
    注意点:

    Nagle 算法的目的是通过合并一些小的发送消息,然后一次性发送所有的消息来减少通过网络发送的小数据包的tcp/ip流量。这种方法的缺点是延迟了单个消息的发送,直到一个足够大的包被组装。
    该方法出处:https://blog.csdn.net/dpjcn1990/article/details/92830087

    上面的方法看完之后,讲讲他大体的流程

    1   建立连接   2  get请求  3  获取数据  就这三步。如果你获取的是json数据,那就需要用的jsonobject这个库,如果是普通文本。那就需要一个char一个cha的单独读出来组文字。

    这里有一个就是建立连接的问题。这个问题一直困扰着我,也就是说我想获取两个地方的天气的时候,是无法获取到的,哪怕你发送了两个get请求,原文中代码是这样写的:

    // This will send the request to the server
    client.print(String("GET ") + GetUrl + " HTTP/1.1 " +
    "Host: " + host + " " +
    "Connection: close ");
    为什么获取不到呢,因为你在get之后,将Connection进行了close  。看最后一行。。。一开始我百思不得其解,为什么连接会自动关闭,直到看到了一篇文章,我才恍然大悟,原来在这里。相关链接 https://www.cnblogs.com/traxex/p/5803023.html

    大体贴一下内容:

    Connection头具体值

    1. 请求:
      1. close(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,断开连接,不要等待本次连接的后续请求了)。
      2. keepalive(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。
    2. 响应:
      1. close(连接已经关闭)。
      2. keep-alive(连接保持着,在等待本次连接的后续请求)。
      3. Keep-Alive: timeout。这个值能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。(见4)

    这就是保持该链接是长链接还是短连接的唯一的根源。你要短连接,那就请求之后close.你如果想要长链接,那就alive

    更改后代码如下

    DebugPrintln(client.print(String("GET ") + GetUrl + " HTTP/1.1 " +
    "Host: " + host + " " +
    "Connection: alive "));

    通过测试证明,该链接没有被关闭。而是保持畅通

    那我们该如何关闭该链接呢。这就是我们client提供的一个方法,client.stop()方法。来讲该链接关闭。所以我们可以再保持连接的前提下,多次请求多个城市的天气,而不必每次请求都打开一个链接。唯一的诀窍就在于Connection。

    OK,这个问题困扰我很久,今天终于解决掉了

    第二个问题就是多汉字输出的问题。

    你可以通过多维数组保存字模。你也放到一个一维数组中,用循环去处理。

    汉字的输出,一定要记得,16*16尺寸的汉字,他的字模就是32个。这就是for循环的基础。

    这里有个问题,就是位置问题。你一个汉字输出完,第二个汉字的位置,第三个汉字的位置,要做好处理,否则就会打到同一个位置了。

    贴一下代码

    /输出中文
    void printChinese(uint x,uint y,unsigned char *Hz_code,uint arraySize)
    {
    DebugPrintln(String("arraySize") + arraySize);
    short int temp_code=0;
    int i,j,k;

    for(i=0;i<arraySize/2;i++)
    for(j=0;j<2;j++)
    for(k=0;k<8;k++)
    if(((Hz_code[i*2+j]>>(7-k))&0x1)!=NULL)
    {
    if(i<16)
    {
    tft.drawPixel(x+8*j+k,y+i, ST7735_WHITE);

    }
    else
    {
    x=20;   //汉字的位置信息,x是横轴,y是纵轴。
    tft.drawPixel(x+8*j+k,y+i-16, ST7735_WHITE);
    }
    }
    }

    经过上述两个步骤,我们的天气终于顺利完成了。贴一下图。

  • 相关阅读:
    【Alpha】测试报告
    【Alpha】发布说明
    【Alpha】项目展示
    【Alpha】Scrum Meeting 10
    【Alpha】Scrum Meeting 9
    【Alpha】Scrum Meeting 8
    “北航Clubs”功能规格说明书
    后端技术规格说明书
    前端设计说明书
    Daily Scrumming* 2015.11.3(Day 15)
  • 原文地址:https://www.cnblogs.com/Lonelychampion/p/12133769.html
Copyright © 2020-2023  润新知