571|0

4

帖子

2

TA的资源

一粒金砂(中级)

楼主
 

FolowMe第四期任务提交 [复制链接]

 

内容一:3-5分钟短视频

FollowMe第四期视频

 

内容二:任务总结报告

■  入门务二:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld)

开发环境使用Arduino ID + C++E完成前置任务,用Thonny + MicroPython完成终极任务。

 

BLINK程序直接使用Arduinio IDE内置的例程即可,不需要任何修改,直接编译上传代码。

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000;
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

展示:

 

■ 基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。

本任务需要用Ethernet这个库,由于w5500和rp2040通过SPI连接,需要SPI库,以及进行域名解析的DNS库

#include <SPI.h>
#include <Ethernet.h>
#include <Dns.h>
#include <EthernetICMP.h>

IPAddress remoteip;
DNSClient dnClient;
char buffer[256];
SOCKET pingSocket = 0;
EthernetICMPPing ping(pingSocket, (uint16_t)random(0, 255));
byte mac[] = { 0x0E, 0xA0, 0xBE, 0xE4, 0xFE, 0x0D };

void pingTest() {
  EthernetICMPEchoReply echoReply = ping(remoteip, 4);
  if (echoReply.status == SUCCESS) {
    sprintf(buffer,
      "Reply[%d] from: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d",
    echoReply.data.seq,
    echoReply.addr[0],
    echoReply.addr[1],
    echoReply.addr[2],
    echoReply.addr[3],
    REQ_DATASIZE,
    millis() - echoReply.data.time,
    echoReply.ttl);
  } 
  else {
    sprintf(buffer, "Echo request failed; %d", echoReply.status);
  }
  Serial.println(buffer);
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ;  // 等待串口连接
  }

  IPAddress ip(192, 168, 10, 188);
  IPAddress myDns(192, 168, 10, 1);
  IPAddress gateway(192, 168, 10, 1);
  IPAddress subnet(255, 255, 255, 0);
  Serial.println("Ethernet Begin");
  Ethernet.init(17);
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  Serial.print("My Mac address: ");
  byte macBuffer[6];
  Ethernet.MACAddress(macBuffer);
  for (byte octet = 0; octet < 6; octet++) {
    Serial.print(macBuffer[octet], HEX);
    if (octet < 5) {
      Serial.print('-');
    }
  }

  dnClient.begin(Ethernet.dnsServerIP());
  const char domains[3][20] = { "www.eeworld.com.cn", "www.digikey.cn", "www.baidu.com" };
  for (int i = 0; i < 3; i++) {
    if (dnClient.getHostByName(domains[i], remoteip) == 1) {
      Serial.print(domains[i]);
      Serial.print(" = ");
      Serial.println(remoteip);
      if (i < 3)
        pingTest();
    } 
    else Serial.println(F("dns lookup failed"));
  }
}

void loop() {
  remoteip.fromString("192.168.10.188");
  pingTest();
  delay(1000);
}

展示:

 

■ 基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作

抓包结果: ping.pcapng (116.9 KB, 下载次数: 0)

开发板首先设置静态ip连接网络,然后使用TCP,同时另一台PC通过TCP向开发板发送信息。

#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE1, 0xF2, 0xE3 };
IPAddress ip(192, 168, 10, 188);
IPAddress gateway(192, 168, 10, 1);
IPAddress myDns(8, 8, 8, 8);
IPAddress subnet(255, 255, 255, 0);

IPAddress setNet() {
  Serial.println("Ethernet Begin");
  Ethernet.init(17);
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
  Serial.print("My Mac address: ");
  byte macBuffer[6];
  Ethernet.MACAddress(macBuffer);
  for (byte octet = 0; octet < 6; octet++) {
    Serial.print(macBuffer[octet], HEX);
    if (octet < 5) {
      Serial.print('-');
    }
  }
  Serial.println("");
  return Ethernet.localIP();
}

EthernetServer server(5000);

String server_loop() {
  String msg;
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        msg += String(c);
      }
    }
  }
  return msg;
}

void setup() {
  Serial.begin(115200);
  while (!Serial)
    ;//等待串口
  setNet();
  server.begin();
}

void loop() {
  String msg = server_loop();
  if (msg != "") {
    Serial.println(msg);
  }
}

PC端发送代码:

import socket
message = "Hello WIZNet-W5500-EVB-Pico, this is a message from vscode"
ip_addr=("192.168.10.188",5000)
client=socket.socket() 
client.connect(ip_addr) 
client.send(bytes(message,"utf-8"))
client.close()

展示:

 

■ 进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。

开发板设置静态IP后,访问国家Ntp授时网站cn.ntp.org.cn获取时间

#include <SPI.h>
#include <Ethernet.h>
#include <NTPClient.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 10, 188);
IPAddress myDns(192, 168, 10, 1);
IPAddress gateway(192, 168, 10, 1);
IPAddress subnet(255, 255, 255, 0);

EthernetUDP Udp;
NTPClient timeClient(Udp, "cn.ntp.org.cn");

void setup() {
  Serial.begin(115200);
  Serial.begin(9600);
  while(!Serial){
    ;
  }
  Ethernet.init(17);
  Ethernet.begin(mac, ip, myDns, gateway, subnet);
  timeClient.begin();
}

void loop() {
  timeClient.update();
  Serial.print("Current time: ");
  Serial.println(timeClient.getFormattedTime());
  char timeString[9]; // 用于保存格式化后的时.分.秒字符串
  sprintf(timeString, "%02d.%02d.%02d", timeClient.getHours(), timeClient.getMinutes(), timeClient.getSeconds());
  Serial1.println(timeString); // 输出时.分.秒格式的字符串
  delay(5000); // 每5秒更新一次时间到串口显示屏
}

显示效果:

     

 

■  终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。

此任务用arduino实现较为困难,故使用Thonny + MicroPython实现,代码如下:

 

import gc
import uos
import time
import socket
import network
from time import localtime
from machine import Pin, SPI
from micropython import const

_LED_PIN = const(25) # 绿色 LED 引脚
_SPI_SPEED = const(2_000_000) # SPI 速率
_MOSI_PIN = const(19) # SPI MOSI 引脚
_MISO_PIN = const(16) # SPI MISO 引脚
_SCK_PIN = const(18) # SPI SCK 引脚
_CS_PIN = const(17) # SPI CS 引脚
_RST_PIN = const(20) # SPI RESET 引脚
FTP_ROOT_PATH = const("/Ftp") # FTP 根目录

month_name = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

# SPI 定义
spi=SPI(0, _SPI_SPEED, mosi=Pin(_MOSI_PIN), miso=Pin(_MISO_PIN), sck=Pin(_SCK_PIN))
nic = None

""" W5500 初始化 """
def w5x00_init():
    global nic
    # 网口初始化
    nic = network.WIZNET5K(spi, Pin(_CS_PIN), Pin(_RST_PIN)) #spi,cs,reset pin
    nic.active(True)
    # 配置网络
    nic.ifconfig(('192.168.10.188','255.255.255.0','192.168.10.1','8.8.8.8'))
    while not nic.isconnected():
        time.sleep(1)
        print(nic.regs())
    
    print("IP地址:  %s" %nic.ifconfig()[0])
    print("子网掩码: %s" %nic.ifconfig()[1])
    print("网关:    %s" %nic.ifconfig()[2])
    print("DNS:     %s" %nic.ifconfig()[3])


""" 响应文件列表请求 """
def send_list_data(path, dataclient, full):
    try: # whether path is a directory name
        for fname in uos.listdir(path):
            dataclient.sendall(make_description(path, fname, full))
    except: # path may be a file name or pattern
        pattern = path.split("/")[-1]
        path = path[:-(len(pattern) + 1)]
        if path == "": path = "/"
        for fname in uos.listdir(path):
            if fncmp(fname, pattern) == True:
                dataclient.sendall(make_description(path, fname, full))

""" 列出目录详情 """
def make_description(path, fname, full):
    if full:
        stat = uos.stat(get_absolute_path(path,fname))
        file_permissions = "drwxr-xr-x" if (stat[0] & 0o170000 == 0o040000) else "-rw-r--r--"
        file_size = stat[6]
        tm = localtime(stat[7])
        if tm[0] != localtime()[0]:
            description = "{}    1 owner group {:>10} {} {:2} {:>5} {}\r\n".format(
                file_permissions, file_size, month_name[tm[1]], tm[2], tm[0], fname)
        else:
            description = "{}    1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".format(
                file_permissions, file_size, month_name[tm[1]], tm[2], tm[3], tm[4], fname)
    else:
        description = fname + "\r\n"
    return description

""" 发送文件数据 """
def send_file_data(path, dataclient):
    try:
        with open(path, "rb") as file:
            chunk = file.read(512)
            print("chunk 0: ", len(chunk))
            while len(chunk) > 0:
                print("chunk: ", len(chunk))
                dataclient.sendall(chunk)
                chunk = file.read(512)
    except Exception as err:
        print("error: ", err.args, err.value, err.errno)


""" 保存文件上传数据 """
def save_file_data(path, dataclient, mode):
    with open(path, mode) as file:
        chunk = dataclient.read(512)
        while len(chunk) > 0:
            file.write(chunk)
            chunk = dataclient.read(512)

""" 获取文件绝对路径 """
def get_absolute_path(cwd, payload):
    # Just a few special cases "..", "." and ""
    # If payload start's with /, set cwd to / 
    # and consider the remainder a relative path
    if payload.startswith('/'):
        cwd = "/"
    for token in payload.split("/"):
        if token == '..':
            if cwd != '/':
                cwd = '/'.join(cwd.split('/')[:-1])
                if cwd == '': 
                    cwd = '/'
        elif token != '.' and token != '':
            if cwd == '/':
                cwd += token
            else:
                cwd = cwd + '/' + token
    return cwd


""" 文件名比较 """
def fncmp(fname, pattern):
    pi = 0
    si = 0
    while pi < len(pattern) and si < len(fname):
        if (fname[si] == pattern[pi]) or (pattern[pi] == '?'):
            si += 1
            pi += 1
        else:
            if pattern[pi] == '*': # recurse
                if (pi + 1) == len(pattern):
                    return True
                while si < len(fname):
                    if fncmp(fname[si:], pattern[pi+1:]) == True:
                        return True
                    else:
                        si += 1
                return False
            else:
                return False
    if pi == len(pattern.rstrip("*"))  and si == len(fname):
        return True
    else:
        return False

""" 启动FTP服务 """
def ftpserver():
    DATA_PORT = 13333
    ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    ftpsocket.bind(socket.getaddrinfo("0.0.0.0", 21)[0][4])
    datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4])

    ftpsocket.listen(1)
    datasocket.listen(1)
    datasocket.settimeout(10)
    
    print("FTP服务启动成功!监听端口:21");

    msg_250_OK = '250 OK\r\n'
    msg_550_fail = '550 Failed\r\n'
    try:
        dataclient = None
        fromname = None
        while True:
            cl, remote_addr = ftpsocket.accept()
            cl.settimeout(300)
            cwd = FTP_ROOT_PATH
            try:
                print("新的FTP连接来自: %s:%s" %(remote_addr[0], remote_addr[1]))
                cl.sendall("220 Welcome! This is the W5500_EVB_PICO!\r\n")
                while True:
                    gc.collect()
                    data = cl.readline().decode("utf-8").rstrip("\r\n")
                    if len(data) <= 0:
                        print("Client disappeared")
                        break
                    
                    command = data.split(" ")[0].upper()
                    payload = data[len(command):].lstrip()

                    path = get_absolute_path(cwd, payload)
                    
                    print("命令={}, 参数={}, 路径={}".format(command, payload, path))
                    
                    if command == "USER":
                        cl.sendall("230 Logged in.\r\n")
                    elif command == "SYST":
                        cl.sendall("215 UNIX Type: L8\r\n")
                    elif command == "NOOP":
                        cl.sendall("200 OK\r\n")
                    elif command == "FEAT":
                        cl.sendall("211 no-features\r\n")
                    elif command == "PWD":
                        cl.sendall('257 "{}"\r\n'.format(cwd))
                    elif command == "CWD":
                        try:
                            files = uos.listdir(path)
                            cwd = path
                            cl.sendall(msg_250_OK)
                        except:
                            cl.sendall(msg_550_fail)
                    elif command == "CDUP":
                        cwd = get_absolute_path(cwd, "..")
                        cl.sendall(msg_250_OK)
                    elif command == "TYPE":
                        # probably should switch between binary and not
                        cl.sendall('200 Transfer mode set\r\n')
                    elif command == "SIZE":
                        try:
                            size = uos.stat(path)[6]
                            cl.sendall('213 {}\r\n'.format(size))
                        except:
                            cl.sendall(msg_550_fail)
                    elif command == "QUIT":
                        cl.sendall('221 Bye.\r\n')
                        break
                    elif command == "PASV":
                        addr = nic.ifconfig()[0]
                        cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format(
                            addr.replace('.',','), DATA_PORT>>8, DATA_PORT%256))
                        dataclient, data_addr = datasocket.accept()
                        print("新的FTP数据连接来自: %s:%s" %(data_addr[0], data_addr[1]))
                    elif command == "LIST" or command == "NLST":
                        if not payload.startswith("-"):
                            place = path
                        else: 
                            place = cwd
                        try:
                            send_list_data(place, dataclient, command == "LIST" or payload == "-l")
                            cl.sendall("150 Here comes the directory listing.\r\n")
                            cl.sendall("226 Listed.\r\n")
                        except:
                            cl.sendall(msg_550_fail)
                        if dataclient is not None:
                            dataclient.close()
                            dataclient = None
                    elif command == "RETR":
                        try:
                            send_file_data(path, dataclient)
                            cl.sendall("150 Opening data connection.\r\n")
                            cl.sendall("226 Transfer complete.\r\n")
                        except:
                            cl.sendall(msg_550_fail)
                        if dataclient is not None:
                            dataclient.close()
                            dataclient = None
                    elif command == "STOR":
                        try:
                            cl.sendall("150 Ok to send data.\r\n")
                            save_file_data(path, dataclient, "wb")
                            cl.sendall("226 Transfer complete.\r\n")
                        except:
                            cl.sendall(msg_550_fail)
                        if dataclient is not None:
                            dataclient.close()
                            dataclient = None
                    elif command == "APPE":
                        try:
                            cl.sendall("150 Ok to send data.\r\n")
                            save_file_data(path, dataclient, "a")
                            cl.sendall("226 Transfer complete.\r\n")
                        except:
                            cl.sendall(msg_550_fail)
                        if dataclient is not None:
                            dataclient.close()
                            dataclient = None
                    elif command == "DELE":
                        try:
                            uos.remove(path)
                            cl.sendall(msg_250_OK)
                        except:
                            cl.sendall(msg_550_fail)
                    elif command == "RMD":
                        try:
                            uos.rmdir(path)
                            cl.sendall(msg_250_OK)
                        except:
                            cl.sendall(msg_550_fail)
                    elif command == "MKD":
                        try:
                            uos.mkdir(path)
                            cl.sendall(msg_250_OK)
                        except:
                            cl.sendall(msg_550_fail)
                    elif command == "RNFR":
                            fromname = path
                            cl.sendall("350 Rename from\r\n")
                    elif command == "RNTO":
                            if fromname is not None: 
                                try:
                                    uos.rename(fromname, path)
                                    cl.sendall(msg_250_OK)
                                except:
                                    cl.sendall(msg_550_fail)
                            else:
                                cl.sendall(msg_550_fail)
                            fromname = None
                    else:
                        cl.sendall("502 Unsupported command.\r\n")
                        # print("Unsupported command {} with payload {}".format(command, payload))
            except Exception as err:
                print(err)  

            finally:          
                cl.close()
                cl = None
    finally:
        datasocket.close()
        ftpsocket.close()
        if dataclient is not None:
            dataclient.close()


if __name__ == "__main__":
    print("run in main")
    w5x00_init() # 初始化网络
    ftpserver()  # 运行 FTP Server

效果:

   

内容三:可编译下载的代码

 

代码链接:代码

 

心得体会
本次FollowMe活动涵盖了从搭建开发环境到实现网络通讯、时间同步和文件传输等多个方面的技术内容,是一项非常全面和实用的项目。完成这些任务需要对嵌入式系统、网络通信、协议解析等知识有较深入的理解和掌握。


从入门任务开始,了解硬件和软件的基本交互,逐步深入学习和实践,帮助我建立起完整的技术体系。在基础任务中,通过网络通信实现局域网与互联网的连接,抓包分析可以帮助我们更深入地了解数据交互过程,发现问题和优化方案。进阶任务则更进一步地提高了网络通信的要求,需要在数据的接收与显示过程中做更多的处理操作。而终极任务则突破了单一功能,结合外部存储器和FTP协议,实现了文件的传输管理,扩展了功能的同时也加深了对整个系统的理解和应用。


在完成这些任务的过程中,需要不断尝试和调试,遇到问题就分析解决问题。同时,及时查阅文档和资料,积极参与相关领域的社区和讨论,向他人请教和分享经验也是非常重要的。另外,保持学习的热情和对技术的好奇心,不断提升自己的技术水平和解决问题的能力也是很重要的。


总而言之,完成这些任务帮助我建立起扎实的技术基础,培养解决问题的能力和创新意识。同时,也拓展了我对技术领域的认识和理解,为未来的学习和发展打下坚实的基础。感谢Digikey得捷电子,感谢EEWorld。


建议:多多开展此类活动,培养更多全栈工程师

点赞 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表