江华210

  • 2024-03-03
  • 加入了学习《【得捷Follow me第4期】项目任务提交2》,观看 【得捷Follow me第4期】项目任务提交

  • 加入了学习《【得捷Follow me第4期】项目任务提交》,观看 视频

  • 2024-02-21
  • 上传了资料: 【得捷Follow me第4期】项目任务提交

  • 加入了学习《【得捷电子Follow me第4期】全部任务合集》,观看 【得捷电子Follow me第4期】全部任务合集

  • 2024-02-20
  • 加入了学习《【得捷电子Follow me第4期】使用micropython完成全部任务》,观看 【得捷电子Follow me第4期】使用micropython完成全部任务

  • 发表了主题帖: 【得捷Follow me第4期】项目任务提交

    入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示。 W5500-RPI-PICO开发板对Arduino有很好的支持,安扎官方文档,我们先在arduino IDE中添加开发板地址,由于我购买了一块cardputer作为项目中的协处理器,负责屏幕显示,因此也一并添加相关支持。: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json     添加完成后,在开发板管理器中搜索pico并安装。   blink完成非常简单,直接使用arduino官方的basic example blink,不需要任何修改,直接上传。   上传完成后就可以看到绿灯闪起来。   液晶屏是在cardputer上进行驱动的,我先在cardputer上初始化好液晶屏,然后开启一个硬件串口作为输入,当接收到信息时将它显示在屏幕上。这样就可以实现一个简单的串口显示器。W5500-RPI-PICO只需要将需要显示的内容用串口发送出去就可以。   在使用cardputer之前,我们需要安装一下对应的库。   安装好后使用起来就非常简单了。 #include "M5Cardputer.h" #include <HardwareSerial.h> HardwareSerial mySerial(2); void setup() { auto cfg = M5.config(); M5Cardputer.begin(cfg, true); M5Cardputer.Display.setRotation(1); M5Cardputer.Display.setTextColor(GREEN); M5Cardputer.Display.setTextDatum(middle_center); M5Cardputer.Display.setTextFont(&fonts::Orbitron_Light_24); M5Cardputer.Display.setTextSize(1); M5Cardputer.Display.drawString("Ready", M5Cardputer.Display.width() / 2, M5Cardputer.Display.height() / 2); Serial.begin(115200); mySerial.begin(115200, SERIAL_8N1, 2, 1); } void loop() { if (mySerial.available()) { String receivedString = mySerial.readString(); Serial.println(receivedString); if (receivedString != "") { M5Cardputer.Display.clear(); M5Cardputer.Display.drawString(receivedString, M5Cardputer.Display.width() / 2, M5Cardputer.Display.height() / 2); } } }   这里我们用cardputer上的GPIO1和GPIO2作为TX和RX。接下来我们用pico 使用serial1打印一个hello,就可以显示在cardputer上。注意由于pico在连接上串口监视器后并不会重置,因此要先等待串口连接后,才可以继续执行串口打印任务,否则无法在串口看到输出。 void setup() { Serial.begin(115200); Serial1.begin(115200); while (!Serial) ; Serial.println("Now send Hello"); Serial1.println("Hello"); } void loop() { }   基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。   同样的,先安装一下需要的库。这个库需要自己去github上下载下来zip,然后在arduino ide中导入zip库。 https://github.com/masterx1981/Ethernet.git   静态配置好ip,MAC等参数,就可以联网,然后分别解析dns并ping一下三个网站。 #include <Ethernet.h> #include <Dns.h> #include <EthernetICMP.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE1, 0xF2, 0xE3 }; IPAddress ip(192, 168, 50, 188); IPAddress gateway(192, 168, 50, 1); IPAddress myDns(8, 8, 8, 8); IPAddress subnet(255, 255, 255, 0); IPAddress setNet() { Serial.println("Ethernet Begin"); Ethernet.init(17); // DHCP // Ethernet.begin(mac); // 静态IP设置 Ethernet.begin(mac, ip, myDns, gateway, subnet); // 打印INFO Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); Serial.print("My subnet: "); Serial.println(Ethernet.subnetMask()); Serial.print("My DNS address: "); Serial.println(Ethernet.dnsServerIP()); Serial.print("My GateWay address: "); Serial.println(Ethernet.gatewayIP()); 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(); } DNSClient dnClient; SOCKET pingSocket = 4; EthernetICMPPing ping(pingSocket, (uint8_t)random(0, 255)); void pingTest(const IPAddress &dstip) { EthernetICMPEchoReply echoReply = ping(dstip, 4); char buffer[256]; 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 ping_site() { dnClient.begin(Ethernet.dnsServerIP()); const char domains[3][20] = { "www.eeworld.com.cn", "www.digikey.cn", "www.digikey.com" }; IPAddress dstip; for (int i = 0; i < 3; i++) { if (dnClient.getHostByName(domains[i], dstip) == 1) { Serial.print(domains[i]); Serial.print(" = "); Serial.println(dstip); pingTest(dstip); } else Serial.println(F("dns lookup failed")); } } void setup() { Serial.begin(115200); while (!Serial) ; setNet(); ping_site(); } void loop() { }   打开wireshark开始抓包,然后用电脑ping一下上面串口打印出的W5500-RPI-PICO开发板,接着在wireshark中用ip地址筛选,就可以看到捕获到的icmp包。       基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。 w5500-rpi-pico连接上网络后,建立一个tcp服务器并监听5000端口。如果有客户连入,则循环读取内容,并将内容添加至字符串。等客户结束连接后,用串口监视器和cardputer同时打印接收的内容。 #include <Ethernet.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE1, 0xF2, 0xE3 }; IPAddress ip(192, 168, 50, 188); IPAddress gateway(192, 168, 50, 1); IPAddress myDns(8, 8, 8, 8); IPAddress subnet(255, 255, 255, 0); IPAddress setNet() { Serial.println("Ethernet Begin"); Ethernet.init(17); // DHCP // Ethernet.begin(mac); // 静态IP设置 Ethernet.begin(mac, ip, myDns, gateway, subnet); // 打印INFO Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); Serial.print("My subnet: "); Serial.println(Ethernet.subnetMask()); Serial.print("My DNS address: "); Serial.println(Ethernet.dnsServerIP()); Serial.print("My GateWay address: "); Serial.println(Ethernet.gatewayIP()); 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); Serial1.begin(115200); while (!Serial) ; setNet(); server.begin(); } void loop() { String msg = server_loop(); if (msg != "") { Serial.println(msg); Serial1.println(msg); } } 编译代码时会有一定概率报错Compilation error: cannot declare variable 'server' to be of abstract type 'EthernetServer'。如果出现这种情况,需要修改一下两个库文件: Ethernet.h // virtual void begin(); virtual void begin(uint16_t port=0); EthernetServer.cpp //void EthernetServer::begin() void EthernetServer::begin(uint16_t port)   电脑上发送tcp访问请求我使用的是python,用python写了个非常简单的代码,发送Hello!!!后关闭连接。 import socket msg = "Hello!!!" ip_addr=("192.168.50.188",5000) client=socket.socket() client.connect(ip_addr) client.send(bytes(msg,"utf-8")) client.close() 运行一下python代码,就可以看到效果。 打开wireshark,也可以抓到我们发送过去的信息。   进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。  通过NTP服务器获取时间的方式是基于UDP的,UDP是只管发送,不管对方收不收的到,因此单次请求可能会失败,所以需要设定一定的重试次数。每次重试之间需要有间隔,以免被对方服务器当作网络攻击直接屏蔽。代码中先把请求的UDP报文写好,再用该方法像服务器发送请求。成功获取到时间后,发送到串口和屏幕。 #include <Ethernet.h> #include <EthernetUdp.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE1, 0xF2, 0xE3 }; IPAddress ip(192, 168, 50, 188); IPAddress gateway(192, 168, 50, 1); IPAddress myDns(8, 8, 8, 8); IPAddress subnet(255, 255, 255, 0); IPAddress setNet() { Serial.println("Ethernet Begin"); Ethernet.init(17); // DHCP // Ethernet.begin(mac); // 静态IP设置 Ethernet.begin(mac, ip, myDns, gateway, subnet); // 打印INFO Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); Serial.print("My subnet: "); Serial.println(Ethernet.subnetMask()); Serial.print("My DNS address: "); Serial.println(Ethernet.dnsServerIP()); Serial.print("My GateWay address: "); Serial.println(Ethernet.gatewayIP()); 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(); } EthernetUDP Udp; const uint8_t NTP_PACKET_SIZE = 48; byte packetBuffer[NTP_PACKET_SIZE]; char bjtime[256]; void sendNTPpacket(const char *address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); // NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); } void ntp() { while (true) { sendNTPpacket("time.nist.gov"); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); if (Udp.parsePacket()) { // We've received a packet, read the data from it Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer // the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, extract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; Serial.print("Seconds since Jan 1 1900 = "); Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: Serial.println(epoch); // print the hour, minute and second: sprintf(bjtime, "%d : %d : %d", (epoch % 86400L) / 3600 + 8, (epoch % 3600) / 60, epoch % 60); return; } // wait 3 seconds before asking for the time again Serial.println("Retry in 3 seconds..."); delay(3000); } } void setup() { Serial.begin(115200); Serial1.begin(115200); while (!Serial) ; setNet(); Udp.begin(8888); ntp(); Serial.println(bjtime); Serial1.println(bjtime); } void loop() { }  终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。 这个任务我本来打算继续在arduino平台完成的,虽然ftp部分没有很大的问题,但是储存这块出了问题。不管是littlefs,还是SD库,我尝试了各种主流的sd卡驱动库,都无法在修改默认SPI的情况下成功读取并写入SD卡。因此只好转战micropython,因为micropython可以直接在内存中虚拟出一块区域作为储存使用。 github中8266的uftp代码可以直接使用,唯一需要更改的一个小地方,就是将我们的IP地址传入即可。 import socket import network import uos import gc from time import localtime from machine import Pin,SPI import time spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18)) nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin nic.active(True)#network active nic.ifconfig(('192.168.50.188','255.255.255.0','192.168.50.1','8.8.8.8'))#Set static network address information while not nic.isconnected(): time.sleep(1) print(nic.regs())#Print register information #Print network address information print("IP Address:",nic.ifconfig()[0]) print("Subnet Mask:",nic.ifconfig()[1]) print("Gateway:",nic.ifconfig()[2]) print("DNS:",nic.ifconfig()[3]) month_name = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 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): with open(path, "r") as file: chunk = file.read(512) while len(chunk) > 0: dataclient.sendall(chunk) chunk = file.read(512) 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 # compare fname against pattern. Pattern may contain # wildcards ? and *. 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 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) 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 = '/' try: # print("FTP connection from:", remote_addr) cl.sendall("220 Hello, this is the ESP8266.\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("Command={}, Payload={}, Path={}".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 Data connection from:", data_addr) 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, "w") 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() ftpserver()     项目视频 项目源码 https://download.eeworld.com.cn/detail/%E6%B1%9F%E5%8D%8E210/631263   心得 通过followme活动让我学到了很多,在活动中可以尝试很多之前没有尝试过的平台,希望活动越办越好。

最近访客

< 1/1 >

统计信息

已有3人来访过

  • 芯积分:26
  • 好友:--
  • 主题:1
  • 回复:0

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言