很高兴能参加得捷电子Follow me第4期活动,这次活动我使用MicroPython进行开发,相关的软件和源代码附在最后。
一、入门任务
1、固件下载
首先去MicroPython官网下载W5500-EVB-Pico开发板固件,固件下载网址如下:
可以看到有很多版本固件可供使用,这里我选择v1.22.1版本。
2、IDE下载安装
W5500-EVB-Pico开发板MicroPython Getting Started网址如下:
根据此开发板Getting Started的推荐,开发IDE选择Thonny,Thonny同时支持Windows、Mac和Linux,因为我用的是Windows系统,所以我下载thonny-4.1.4.exe (21 MB)版本。
Thonny下载网址如下:
Thonny安装完成后会有和技术不相关的东西,可以按照此链接操作对此进行屏蔽,链接如下:
3、固件烧录
开发板通过micro USB线接电脑,按住BOOTSEL键后再短按一下RUN键,最后松开BOOTSEL键,电脑会将开发板识别成U盘。
将前面下载好的固件复制到此U盘,复制完成后开发板会自动重启,至此固件烧录完成。
4、BLINK
打开Thonny,通过软件右下角可以选择要开发的设备和端口,这里我的是COM8。
Shell窗口会显示固件版本。
在编辑区写入BLINK代码,从原理图能看出LED控制引脚是25号引脚,所以代码如下:
from machine import Pin
import network
import time
led = Pin(25, Pin.OUT)
def main():
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
if __name__ == "__main__":
main()
运行效果如下:
5、驱动液晶显示器
这是Adafruit Sharp Memory Display Breakout使用说明链接如下:
根据此链接用W5500-EVB-Pico开发板使用CircuitPython一直没有驱动起来此屏幕,报GP10已经使用了的错误,网上找了一圈没有找到解决办法,想到自己手里还有一块Arduino DUE开发板,所以根据屏幕的使用说明用Arduino进行驱动,接线如下:
Microcontroller GND to LCD Gnd
Microcontroller 5V to LCD Vin
Microcontroller D13 to LCD Clk
Microcontroller D11 to LCD DI
Microcontroller D10 to LCD CS
Arduino IDE下载链接如下:
这里根据自己的平台下载相应的版本,这里我下载Windows版
安装DUE开发板包:
安装Sharp Memory Display Library库:
弹出选项选INSTALL ALL:
打开Arduino显示屏对应的显示例程:
编译下载,对应的显示效果如下
/*********************************************************************
This is an example sketch for our Monochrome SHARP Memory Displays
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/1393
These displays use SPI to communicate, 3 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above, must be included in any redistribution
*********************************************************************/
#include <Adafruit_GFX.h>
#include <Adafruit_SharpMem.h>
// any pins can be used
#define SHARP_SCK 13
#define SHARP_MOSI 11
#define SHARP_SS 10
// Set the size of the display here, e.g. 144x168!
Adafruit_SharpMem display(SHARP_SCK, SHARP_MOSI, SHARP_SS, 144, 168);
// The currently-available SHARP Memory Display (144x168 pixels)
// requires > 4K of microcontroller RAM; it WILL NOT WORK on Arduino Uno
// or other <4K "classic" devices! The original display (96x96 pixels)
// does work there, but is no longer produced.
#define BLACK 0
#define WHITE 1
int minorHalfSize; // 1/2 of lesser of display width or height
void setup(void)
{
Serial.begin(9600);
Serial.println("Hello!");
// start & clear the display
display.begin();
display.clearDisplay();
// Several shapes are drawn centered on the screen. Calculate 1/2 of
// lesser of display width or height, this is used repeatedly later.
minorHalfSize = min(display.width(), display.height()) / 2;
// draw a single pixel
display.drawPixel(10, 10, BLACK);
display.refresh();
delay(500);
display.clearDisplay();
// draw many lines
testdrawline();
delay(500);
display.clearDisplay();
// draw rectangles
testdrawrect();
delay(500);
display.clearDisplay();
// draw multiple rectangles
testfillrect();
display.refresh();
delay(500);
display.clearDisplay();
// draw a circle, 10 pixel radius
display.fillCircle(display.width()/2, display.height()/2, 10, BLACK);
display.refresh();
delay(500);
display.clearDisplay();
testdrawroundrect();
display.refresh();
delay(500);
display.clearDisplay();
testfillroundrect();
display.refresh();
delay(500);
display.clearDisplay();
testdrawtriangle();
display.refresh();
delay(500);
display.clearDisplay();
testfilltriangle();
display.refresh();
delay(500);
display.clearDisplay();
testdrawchar();
display.refresh();
for(int i=0; i<4; i++) {
display.refresh();
delay(500);
}
}
void loop(void)
{
for(int i=0; i<4; i++) {
display.setRotation(i);
display.clearDisplay();
// text display tests
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(0,0);
display.println("Hello, world!");
display.setTextColor(WHITE, BLACK); // inverted text
display.println(3.141592);
display.setTextSize(2);
display.setTextColor(BLACK);
display.print("0x"); display.println(0xDEADBEEF, HEX);
// Screen must be refreshed at least once per second
for(int j=0; j<4; j++) {
display.refresh();
delay(500); // 1/2 sec delay
} // x4 = 2 second pause between rotations
}
}
///
void testdrawline() {
for (int i=0; i<display.width(); i+=4) {
display.drawLine(0, 0, i, display.height()-1, BLACK);
display.refresh();
}
for (int i=0; i<display.height(); i+=4) {
display.drawLine(0, 0, display.width()-1, i, BLACK);
display.refresh();
}
delay(250);
display.clearDisplay();
for (int i=0; i<display.width(); i+=4) {
display.drawLine(0, display.height()-1, i, 0, BLACK);
display.refresh();
}
for (int i=display.height()-1; i>=0; i-=4) {
display.drawLine(0, display.height()-1, display.width()-1, i, BLACK);
display.refresh();
}
delay(250);
display.clearDisplay();
for (int i=display.width()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, i, 0, BLACK);
display.refresh();
}
for (int i=display.height()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, 0, i, BLACK);
display.refresh();
}
delay(250);
display.clearDisplay();
for (int i=0; i<display.height(); i+=4) {
display.drawLine(display.width()-1, 0, 0, i, BLACK);
display.refresh();
}
for (int i=0; i<display.width(); i+=4) {
display.drawLine(display.width()-1, 0, i, display.height()-1, BLACK);
display.refresh();
}
delay(250);
}
void testdrawrect(void) {
for (int i=0; i<minorHalfSize; i+=2) {
display.drawRect(i, i, display.width()-2*i, display.height()-2*i, BLACK);
display.refresh();
}
}
void testfillrect(void) {
uint8_t color = BLACK;
for (int i=0; i<minorHalfSize; i+=3) {
// alternate colors
display.fillRect(i, i, display.width()-i*2, display.height()-i*2, color&1);
display.refresh();
color++;
}
}
void testdrawroundrect(void) {
for (int i=0; i<minorHalfSize/2; i+=2) {
display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i, minorHalfSize/2, BLACK);
display.refresh();
}
}
void testfillroundrect(void) {
uint8_t color = BLACK;
for (int i=0; i<minorHalfSize/2; i+=2) {
display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i, minorHalfSize/2, color&1);
display.refresh();
color++;
}
}
void testdrawtriangle(void) {
for (int i=0; i<minorHalfSize; i+=5) {
display.drawTriangle(display.width()/2, display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, BLACK);
display.refresh();
}
}
void testfilltriangle(void) {
uint8_t color = BLACK;
for (int i=minorHalfSize; i>0; i-=5) {
display.fillTriangle(display.width()/2 , display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, color & 1);
display.refresh();
color++;
}
}
void testdrawchar(void) {
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(0,0);
display.cp437(true);
for (int i=0; i < 256; i++) {
if (i == '\n') continue;
display.write(i);
}
display.refresh();
}
显示效果如下:
Arduino sharpmemtest
二、基础任务一
1、W5500使用
参考W5500-EVB-Pico开发板MicroPython Getting Started例程对W5500初始化(静态IP配置),网址如下:
from machine import Pin,SPI
import network
import time
import uping
led = Pin(25, Pin.OUT)
#W5x00 chip init
def w5x00_init():
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)
nic.ifconfig(('192.168.31.120','255.255.255.0','192.168.31.1','8.8.8.8'))
while not nic.isconnected():
time.sleep(1)
print(nic.regs())
print(nic.ifconfig())
def main():
w5x00_init()
uping.ping('baidu.com')
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
if __name__ == "__main__":
main()
运行结果如图
使用局域网电脑ping通开发板结果如图:
开发板想要ping通互联网站点,需要添加uping.py文件,此文件下载网址如下:
下载好文件后将文件保存进开发板即可。
W5500 ping通互联网站点(这里选择ping百度)结果如图:
2、Wireshark下载安装使用
下载Wireshark并安装,软件下载网址如下:
根据自己的平台选择相应的版本,这里我用的是Windows x64 Installer版本。
使用Wireshark抓取PC网卡数据,结果如图:
数据太多了,对关心的数据进行筛选,筛选条件为ip.addr==192.168.31.120(开发板IP),结果如图,ping的数据包就比较清晰了。
可以看到,跟开发板相关的报文有8个,4个请求报文和4个应答报文,报文第一项是对整个报文的描述,第二项为报文前14个字节,分别为本机的MAC地址和路由器的MAC地址:
接下来的20个字节为IP报文:
关于IP报文的格式如下:
最后是IP报文的数据段,也就是ICMP报文:
三、基础任务二
1、开发板建立TCPIP服务器,并接收数据
参考W5500-EVB-Pico开发板MicroPython Getting Started例程用开发板创建TCP服务器,用网络调试助手创建TCP客户端连接开发板并发送数据
from usocket import socket
from machine import Pin,SPI
import network
import time
#W5x00 chip init
def w5x00_init():
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)
#None DHCP
nic.ifconfig(('192.168.31.120','255.255.255.0','192.168.31.1','8.8.8.8'))
#DHCP
#nic.ifconfig('dhcp')
print('IP address :', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs())
def server_loop():
s = socket()
s.bind(('192.168.31.120', 5000)) #Source IP Address
s.listen(5)
print("TEST server")
conn, addr = s.accept()
print("Connect to:", conn, "address:", addr)
print("Loopback server Open!")
while True:
data = conn.recv(2048)
print(data.decode('utf-8'))
if data != 'NULL':
conn.send(data)
def client_loop():
s = socket()
s.connect(('192.168.31.2', 5000)) #Destination IP Address
print("Loopback client Connect!")
while True:
data = s.recv(2048)
print(data.decode('utf-8'))
if data != 'NULL' :
s.send(data)
def main():
w5x00_init()
###TCP SERVER###
while True:
server_loop()
###TCP CLIENT###
#client_loop()
if __name__ == "__main__":
main()
运行结果如下:
2、将接收数据送显示屏模块显示
通过串口将W5500-EVB-Pico和Arduino DUE相连,W5500将要显示的内容通过串口发给DUE,DUE驱动屏幕显示。
W5500-EVB-Pico代码:
from usocket import socket
from machine import Pin,SPI,UART
import network
import time
uart = UART(0, baudrate=115200, bits=8, stop=1)
#W5x00 chip init
def w5x00_init():
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)
#None DHCP
nic.ifconfig(('192.168.31.120','255.255.255.0','192.168.31.1','8.8.8.8'))
#DHCP
#nic.ifconfig('dhcp')
print('IP address :', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs())
def server_loop():
s = socket()
s.bind(('192.168.31.120', 5000)) #Source IP Address
s.listen(5)
print("TEST server")
conn, addr = s.accept()
print("Connect to:", conn, "address:", addr)
print("Loopback server Open!")
while True:
data = conn.recv(2048)
print(data.decode('utf-8'))
if data != 'NULL':
uart.write(data)
conn.send(data)
def client_loop():
s = socket()
s.connect(('192.168.31.2', 5000)) #Destination IP Address
print("Loopback client Connect!")
while True:
data = s.recv(2048)
print(data.decode('utf-8'))
if data != 'NULL' :
s.send(data)
def main():
w5x00_init()
#uart.write('hello-world')
###TCP SERVER###
while True:
server_loop()
###TCP CLIENT###
#client_loop()
if __name__ == "__main__":
main()
Arduino DUE代码:
/*********************************************************************
This is an example sketch for our Monochrome SHARP Memory Displays
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/1393
These displays use SPI to communicate, 3 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above, must be included in any redistribution
*********************************************************************/
#include <Adafruit_GFX.h>
#include <Adafruit_SharpMem.h>
// any pins can be used
#define SHARP_SCK 13
#define SHARP_MOSI 11
#define SHARP_SS 10
// Set the size of the display here, e.g. 144x168!
Adafruit_SharpMem display(SHARP_SCK, SHARP_MOSI, SHARP_SS, 144, 168);
// The currently-available SHARP Memory Display (144x168 pixels)
// requires > 4K of microcontroller RAM; it WILL NOT WORK on Arduino Uno
// or other <4K "classic" devices! The original display (96x96 pixels)
// does work there, but is no longer produced.
#define BLACK 0
#define WHITE 1
int minorHalfSize; // 1/2 of lesser of display width or height
String inString[5]="";
int i = 0;
void setup(void)
{
Serial.begin(115200);
// Serial.println("Hello!");
// start & clear the display
display.begin();
display.clearDisplay();
// testdrawchar();
display.refresh();
for(int i=0; i<4; i++) {
display.refresh();
delay(500);
}
}
void loop(void)
{
while(Serial.available()>0)
{
inString[i] += char(Serial.read());
delay(10); // 延时函数用于等待字符完全进入缓冲区,可以尝试没有延时,输出结果会是什么
}
// 检查是否接收到数据,如果接收到数据,则输出该数据
if(inString[i]!="")
{
i++;
if(i == 5)
{
inString[0] = inString[1];
inString[1] = inString[2];
inString[2] = inString[3];
inString[3] = inString[4];
inString[4] = "";
i = 4;
}
display.setRotation(1);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(BLACK);
for(int y = 0; y < 4; y++)
{
display.setCursor(4,4 + 15 * y);
display.println(inString[y]);
}
display.refresh();
}
}
3、报文抓取
通过Wirshark抓取的数据包如下图,可以看到发送的数据EEworld在抓取的TCP数据包中。
四、进阶任务
进阶任务比较简单,注意最好选国内ntp服务器,比较稳定,运行结果如图:
W5500-EVB-Pico开发板代码如下:
from usocket import socket
from machine import Pin,SPI,UART,RTC
import network
import time
import ntptime
uart = UART(0, baudrate=115200, bits=8, stop=1)
#W5x00 chip init
def w5x00_init():
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)
#None DHCP
nic.ifconfig(('192.168.31.120','255.255.255.0','192.168.31.1','8.8.8.8'))
#DHCP
#nic.ifconfig('dhcp')
print('IP address :', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs())
def show_local_time(timezone=8):
rtc = RTC()
now = time.time()
now += timezone * 3600
t = time.localtime(now)
print(f'{t[0]} - {t[1]:02d}-{t[2]:02d} {t[3]:02d}:{t[4]:02d}:{t[5]:02d}')
uart.write(f'{t[0]} - {t[1]:02d}-{t[2]:02d} {t[3]:02d}:{t[4]:02d}:{t[5]:02d}')
def main():
w5x00_init()
#先手动设置一个错误时间,模拟系统时间不准
rtc = RTC()
rtc.datetime((2020, 1, 1, 3, 9, 0, 0, 0)) #年、月、日、星期、时、分、秒、亚秒
print('校时前系统时间:')
show_local_time()
#NTP校时
print('开始NTP校时...')
ntptime.host = 'ntp1.aliyun.com'
ntptime.settime()
print(f'校时后系统时间:')
show_local_time()
while True:
show_local_time()
time.sleep(1)
if __name__ == "__main__":
main()
Arduino DUE代码如下:
/*********************************************************************
This is an example sketch for our Monochrome SHARP Memory Displays
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/1393
These displays use SPI to communicate, 3 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above, must be included in any redistribution
*********************************************************************/
#include <Adafruit_GFX.h>
#include <Adafruit_SharpMem.h>
// any pins can be used
#define SHARP_SCK 13
#define SHARP_MOSI 11
#define SHARP_SS 10
// Set the size of the display here, e.g. 144x168!
Adafruit_SharpMem display(SHARP_SCK, SHARP_MOSI, SHARP_SS, 144, 168);
// The currently-available SHARP Memory Display (144x168 pixels)
// requires > 4K of microcontroller RAM; it WILL NOT WORK on Arduino Uno
// or other <4K "classic" devices! The original display (96x96 pixels)
// does work there, but is no longer produced.
#define BLACK 0
#define WHITE 1
int minorHalfSize; // 1/2 of lesser of display width or height
String inString ="";
int i = 0;
void setup(void)
{
Serial.begin(115200);
// Serial.println("Hello!");
// start & clear the display
display.begin();
display.clearDisplay();
// testdrawchar();
display.refresh();
for(int i=0; i<4; i++) {
display.refresh();
delay(500);
}
}
void loop(void)
{
while(Serial.available()>0)
{
inString += char(Serial.read());
delay(10); // 延时函数用于等待字符完全进入缓冲区,可以尝试没有延时,输出结果会是什么
}
// 检查是否接收到数据,如果接收到数据,则输出该数据
if(inString!="")
{
display.setRotation(1);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(BLACK);
display.setCursor(4,4);
display.println(inString);
display.refresh();
inString = "";
}
}
Task_A
四、终极任务二
1、驱动SD卡
首先下载sd卡类,下载网址如下:
将下载的库文件内容复制到Thonny,再存到开发板。
# Filename: tfcard_test.py
import uos # os/uos
import machine
import sdcard
from machine import SPI, Pin
spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)
# 挂载文件到sd
uos.mount(sd,"/sd")
# 列出MicroSD/TF卡中的目录文件
print(uos.listdir('/sd'))
# 写文件测试
f = open('/sd/test.txt','w',encoding='utf-8')
f.write('MicroSD/TF存储卡访问测试!')
f.close()
# 读文件测试
f = open('/sd/test.txt','r')
print(f.read())
f.close()
运行结果如下:
可以看到开发板挂载了sd卡,并且有了test.txt文件
2、实现简易FTP服务
参考网上的使用MicroPython实现ftp的例子,参考网址如下:
由于参考程序是使用的ESP32来实现的,需要对程序进行一定的修改,主要修改网络初始化,修改完成后运行报超时
最后发现是原例程第186行需要修改addr获取方式,改后程序运行正常,ftp文件服务器也能正常上传和下载文件。
from usocket import socket
from machine import Pin,SPI
import network
import time
import socket
import uos
import gc
import sdcard
from time import localtime
spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)
# 挂载文件到sd
uos.mount(sd,"/sd")
# 列出MicroSD/TF卡中的目录文件
print(uos.listdir('/sd'))
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)
#None DHCP
nic.ifconfig(('172.22.51.220','255.255.255.0','172.22.51.1','8.8.8.8'))
print('IP address :', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs())
print(nic.ifconfig())
#W5x00 chip init
def w5x00_init():
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)
#None DHCP
nic.ifconfig(('172.22.51.220','255.255.255.0','172.22.51.1','8.8.8.8'))
print('IP address :', nic.ifconfig())
while not nic.isconnected():
time.sleep(1)
#print(nic.regs())
print(nic.ifconfig())
return nic
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()
def main():
#w5x00_init()
ftpserver() #直接跑这个就成功建立服务器了
if __name__ == "__main__":
main()
五、可编译下载的代码
六、用到的相关库和固件
八、心得体会
感谢EEWorld和得捷举办这次活动,让我们有机会感受到RP2040和W5500组合起来能产生多大的魅力,但也在动手过程中发现MicroPython如果遇到某些问题很难在很快在网上找到答案,希望大家以后能一起壮大MicroPython生态。
|