- 2024-11-14
-
加入了学习《PX4固件二次开发课程》,观看 飞控软硬体系介绍1
- 2024-10-08
-
发表了主题帖:
【Follow me第二季第2期】+项目总结
本帖最后由 superw 于 2024-11-6 02:00 编辑
文章中各任务均由图片中物料实现
物料清单:
厂商
厂商料号
名称
得捷链接
Arduino
ABX00087
Arduino UNO R4 WiFi
https://www.digikey.cn/zh/products/detail/arduino/ABX00087/20371539?s=N4IgTCBcDaIIICEAaAGNAOA7AApAXQF8g
Adafruit
4991
Adafruit I2C STEMMA QT
Rotary Encoder Breakout with
Neopixel
https://www.digikey.cn/zh/products/detail/adafruit-industries-llc/4991/14302511
Adafruit
5591
Adafruit LTR-329 Light Sensor
https://www.digikey.cn/zh/products/detail/adafruit-industries-llc/5591/16733167
一、入门任务(必做):搭建环境并开启第一步Blink / 串口打印Hello EEWorld!
Arduino官方的板子,当然要用Arduino IDE来开发,在Arduino官网根据自己的操作系统下载对应的安装程序,一路next即可。当然电脑硬盘不够的也可以选择使用便携版IDE或者浏览器开发。
安装完成后,搭建环境。按照官方指南https://docs.arduino.cc/tutorials/uno-r4-wifi/r4-wifi-getting-started/,安装Board Package后,即可进行编译下载代码。
硬件分析:
板载DL4通过MOS管驱动,当P102输出高电平点亮DL4,当P102输出低电平熄灭DL4。
软件实现:
通过在loop函数中循环翻转P102电平,并附加相应延时,即可实现板载LED闪烁。Arduino官方开发板默认串口使用Serial.begin进行初始化,其他通信串口使用Serialx.begin初始化(x=1,2,...),串口初始化成功后,可通过print,println函数进行打印。
代码实现:
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("Hello EEWorld!");
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
上面代码实现任务一,控制板载LED Blink,同时通过串口打印Hello EEWorld!
实现效果:
二、基础任务(必做):驱动12x8点阵LED;用DAC生成正弦波;用OPAMP放大DAC信号;用ADC采集并且打印数据到串口等其他接口可上传到上位机显示曲线
对于Arduino UNO R4 WiFi板上资源,官方提供了详细的指南。
对于点阵LED,可参考https://docs.arduino.cc/tutorials/uno-r4-wifi/led-matrix/
对于片上DAC,可参考https://docs.arduino.cc/tutorials/uno-r4-wifi/dac/
对于片上OPAMP,可参考https://docs.arduino.cc/tutorials/uno-r4-wifi/opamp/
对于片上ADC,可参考https://docs.arduino.cc/tutorials/uno-r4-wifi/adc-resolution/
综合根据上面指南,可以完成任务二。
硬件分析:本任务涉及到的硬件资源,开发板上均已具备,且对于DAC、OPAMP、ADC均为MCU片上资源,直接通过对应函数操作硬件即可。
对于OPAMP放大DAC信号,需要使用面包板,通过片上OPAMP运放单元搭建一个简易的运算放大器。原理图如下:
上面的实现是一个电压跟随器,但电压跟随器可以理解为一个特殊的运算放大器,增益为1,输出电压与输入电压同幅值,同频率。电压跟随器在电路中经常起到缓冲和隔离的作用。
将A0(DAC输出)接入A1(OPAMP的正向输入端),A2(OPAMP的反向输入端)和A3短接(OPAMP的输出端),此时便可在A3用示波器观察到与A0的输出波形一致。将A3(OPAMP的输出端)与A4(ADC采集)短接,便可以获取到ADC采集到的信号电压。
代码实现:
#include "analogWave.h"
#include "OPAMP.h"
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
analogWave wave(DAC);
ArduinoLEDMatrix matrix;
int freq = 10;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
wave.sine(freq);
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
analogReadResolution(14);
matrix.begin();
}
void loop() {
// put your main code here, to run repeatedly:
int adc_value = analogRead(A4);
Serial.println("current adc value is " + String(adc_value));
// Make it scroll!
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(50);
// add the text
const char text[] = " EEWorld & DigiKey ";
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
}
实现效果:
对于12x8点阵LED,上电后会看到EEWorld & DigiKey的字样。
手机相机拍的不是很清,人眼视觉效果会相对好点。
用DAC生成正弦波,并经过OPAMP放大DAC信号,现象可通过示波器观察得到。
由于手里没有插件电阻,将片上的OPAMP运放配置为电压跟随器模式,即输入为什么信号,输出就是什么信号。图中黄色为原始DAC正弦波信号,绿色为经过电压跟随器后输出的正弦波信号,二者幅值频率均相等。
用ADC采集生成的正弦波信号,并打印到串口,借助Arduino IDE的串口上位机可将波形大致呈现出来。
三、进阶任务(必做):通过Wi-Fi,利用MQTT协议接入到开源的智能家居平台HA(HomeAssistant)
四、扩展任务一:通过外部LTR-329 环境光传感器,上传光照度到HA,通过HA面板显示数据
首先,非常感谢这期老师给大家带来的直播任务讲解,里面清楚的介绍了如何在windows平台上搭建HomeAssistant环境。当然,我自己也尝试了在泰山派上通过docker安装HomeAssistant,但貌似是由于Docker Hub不稳定的问题,总是访问超时,在ubuntu上也不会魔法上网,只能放弃。后来又尝试安装CasaOS(系统本身已具有docker),但最终还是需要通过docker访问HomeAssistant的Docker Hub,也未能完整实现。最终选择了直播中老师讲解的方法,在windows上安装Docker Desktop,之后拉取HomeAssistant镜像,并创建容器。
具体搭建过程可参考直播讲解https://training.eeworld.com.cn/video/40793
HomeAssistant界面
EMQX(MQTT)服务器界面
硬件分析:
上图为任务推荐的LTR329光照传感器模块,板载Qwiic接口。对应Adafruit采购型号为5591。
上图为任务之外选购的编码器模块,板载Qwiic接口。对应Adafruit采购型号为4991。板载一个内置固件的MCU,采集板载的编码器信息,并且支持外部IIC总线访问来获取。
上图为Arduino开发板的J2接口,硬件形式为Qwiic,可通过SH1.0排线将上述两个模块串联到一块,共同挂载在IIC总线上,通过IIC主机寻址不同的设备地址,访问设备。
注意:LTR329的IIC地址为0x29,编码器模块的IIC地址为0x36。
软件实现:
打开Library Manager,搜索框输入LTR329,安装对应的Library。详细介绍及驱动代码可见https://learn.adafruit.com/adafruit-ltr-329-ltr-303/overview
本次活动选择的另一个器件是I2C QT ROTARY ENCODER,片上集成了一个编码器和一个WS2812,单片机下载seesaw固件,通过I2C接口获取命令,进一步通过单片机读取编码器的值,控制WS2812。搜索框输入seesaw,安装对应的Library。详细介绍及驱动代码可见https://learn.adafruit.com/adafruit-i2c-qt-rotary-encoder
当然,要通过WiFi接入HA平台,最重要的还需要一个home-assistant-integration库,通过这个库,可以创建各种设备类型,包括灯、按键、传感器、锁等等。具体库介绍可参考https://dawidchyrzynski.github.io/arduino-home-assistant/index.html
环境搭建和各种library安装好之后,就可以进行代码编写。
代码实现:
#include "WiFiS3.h"
#include "arduino_secrets.h"
#include "Adafruit_LTR329_LTR303.h"
#include <ArduinoHA.h>
#include <seesaw_neopixel.h>
#define MQTT_OBJECT_ID "followme"
#define BROKER_ADDR IPAddress(192,168,10,246)
#define MQTT_PORT 1883
#define MQTT_USERNAME "admin"
#define MQTT_PASSWORD "123"
#define SS_NEOPIX 6
#define SEESAW_ADDR 0x36
seesaw_NeoPixel sspixel = seesaw_NeoPixel(1, SS_NEOPIX, NEO_GRB + NEO_KHZ800, &Wire1);
Adafruit_LTR329 ltr = Adafruit_LTR329();
WiFiClient client;
HADevice device(MQTT_OBJECT_ID);
HAMqtt mqtt(client, device);
HAButton buttonR("myButtonR");
HAButton buttonG("myButtonG");
HAButton buttonB("myButtonB");
HASensorNumber lightSensor("mylight");
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS;
unsigned long lastUpdateAt = 0;
void onButtonCommand(HAButton* sender)
{
if (sender == &buttonR) {
// button A was clicked, do your logic here
sspixel.setPixelColor(0, sspixel.Color(255, 0, 0));
} else if (sender == &buttonG) {
// button B was clicked, do your logic here
sspixel.setPixelColor(0, sspixel.Color(0, 255, 0));
} else if (sender == &buttonB) {
sspixel.setPixelColor(0, sspixel.Color(0, 0, 255));
}
sspixel.show();
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
wifi_init();
ltr329_init();
pixel_init();
device.setName("Arduino");
device.setSoftwareVersion("1.0.0");
buttonR.setIcon("mdi:fire");
buttonR.setName("RED");
buttonG.setIcon("mdi:home");
buttonG.setName("GREEN");
buttonB.setIcon("mdi:water");
buttonB.setName("BLUE");
buttonR.onCommand(onButtonCommand);
buttonG.onCommand(onButtonCommand);
buttonB.onCommand(onButtonCommand);
lightSensor.setIcon("mdi:home");
lightSensor.setName("light value");
mqtt.begin(BROKER_ADDR, MQTT_PORT, MQTT_USERNAME, MQTT_PASSWORD);
}
void loop() {
// put your main code here, to run repeatedly:
bool valid;
uint16_t visible_plus_ir, infrared;
mqtt.loop();
if ((millis() - lastUpdateAt) > 1000)
{ // 1000ms debounce time
if (ltr.newDataAvailable())
{
valid = ltr.readBothChannels(visible_plus_ir, infrared);
if (valid)
{
Serial.print("CH0 Visible + IR: ");
Serial.print(visible_plus_ir);
Serial.print("\t\tCH1 Infrared: ");
Serial.println(infrared);
lightSensor.setValue(visible_plus_ir);
}
}
lastUpdateAt = millis();
}
}
void wifi_init()
{
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
printWifiStatus();
}
void ltr329_init()
{
Serial.println("Adafruit LTR-329 advanced test");
if ( ! ltr.begin(&Wire1) ) {
Serial.println("Couldn't find LTR sensor!");
while (1) delay(10);
}
Serial.println("Found LTR sensor!");
ltr.setGain(LTR3XX_GAIN_2);
ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
ltr.setMeasurementRate(LTR3XX_MEASRATE_200);
}
void pixel_init()
{
Serial.println("Looking for seesaw!");
if (!sspixel.begin(SEESAW_ADDR)) {
Serial.println("ERROR! seesaw not found");
while(1) delay(1);
}
Serial.println("seesaw started");
// set not so bright!
sspixel.setBrightness(20);
sspixel.show();
}
/* -------------------------------------------------------------------------- */
void printWifiStatus() {
/* -------------------------------------------------------------------------- */
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
上面代码实现了两个功能,一个是将LTR-329获取到的光照强度上传到HA平台,另一个是通过HA平台创建三个按键,分别用来控制WS2812的红、绿、蓝三种颜色。
硬件连接如图
实现效果
在HA平台上可看见上传的光照强度值,具体现象可见视频。
五、心得体会
非常感谢官方提供的Arduino板卡,这块板子22年发布的,在技术创新和功能上都非常有特色,最感兴趣的还是上面12x8点阵LED了,可以生成表情、动物、字符等信息。另外,这期活动还跟着教程学会了home assistant的搭建和使用,后面也要继续尝试在泰山派上成功搭建起来。最后希望follow me活动越办越好,给大家带来更多更优秀的大厂开发板。
六、可编译下载的代码
-
加入了学习《【Follow me第二季第2期】+视频介绍》,观看 【Follow me第二季第2期】
-
加入了学习《FollowMe 第二季:2 - Arduino UNO R4 Wi-Fi 及任务讲解》,观看 Arduino UNO R4 Wi-Fi 及任务讲解
- 2024-09-12
-
加入了学习《Digi-Key: Follow Me 系列(2) 直播回放》,观看 Adafruit ESP32-S3 TFT Feather开发板使用入门
- 2024-03-02
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B强大的测试和分析功能
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B多通道同步频谱分析
-
加入了学习《泰克MSO6B探索营》,观看 如何完成AI芯片电源噪声测试
- 2024-03-01
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B系列低噪声演示
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B技术介绍
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B-360度介绍
- 2024-02-26
-
加入了学习《 得捷电子Follow me第4期》,观看 得捷电子Follow me第4期
-
发表了主题帖:
【得捷电子Follow me第4期】+项目总结
本帖最后由 superw 于 2024-2-26 11:28 编辑
内容一、演示视频
内容二、 项目总结报告
入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld)
# 在这里写上你的代码 :-)
import time
import board
import busio
import digitalio
import adafruit_sharpmemorydisplay
def display_init():
#SPI1 Display
SPI1_SCK = board.GP10
SPI1_MOSI = board.GP11
SPI1_CSn = board.GP13
#配置display的SPI
display_spi = busio.SPI(SPI1_SCK, SPI1_MOSI)
display_scs = digitalio.DigitalInOut(SPI1_CSn)
display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(display_spi, display_scs, 144, 168, baudrate=8000000)
return display
def led_init():
#配置LED
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
return led
led=led_init()
display=display_init()
print("DigiKey & eeworld")
print("Follow me")
print("W5500-EVB-Pico with rp2040")
display.fill(0)
display.text(" DigiKey & eeworld", 0, 10, 1)
display.text(" Follow me", 0, 20, 1)
display.text(" W5500-EVB-Pico ", 0, 30, 1)
display.text(" with rp2040 ", 0, 40, 1)
display.show()
while True:
led.value = not led.value
time.sleep(0.5)
通过使用adafruit_sharpmemorydisplay库完成液晶显示器的驱动,同时点亮板载LED。
基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
# 在这里写上你的代码 :-)
import board
import busio
import digitalio
import time
import adafruit_sharpmemorydisplay
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
def led_init():
#配置LED
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
return led
def display_init():
#SPI1 Display
SPI1_SCK = board.GP10
SPI1_MOSI = board.GP11
SPI1_CSn = board.GP13
#配置display的SPI
display_spi = busio.SPI(SPI1_SCK, SPI1_MOSI)
display_scs = digitalio.DigitalInOut(SPI1_CSn)
display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(display_spi, display_scs, 144, 168, baudrate=8000000)
return display
def w5500_init():
#SPI0 Ethernet
SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
#Ethernet reset
W5500_RSTn = board.GP20
ethernetRst = digitalio.DigitalInOut(W5500_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
# Setup your network configuration below
# random MAC, later should change this value on your vendor ID
MY_MAC = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05)
IP_ADDRESS = (192, 168, 1, 100)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
# For Adafruit Ethernet FeatherWing
eth_cs = digitalio.DigitalInOut(SPI0_CSn)
# For Particle Ethernet FeatherWing
# cs = digitalio.DigitalInOut(board.D5)
eth_spi = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)
# Reset W5500 first
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
# Initialize ethernet interface with DHCP
# eth = WIZNET5K(spi_bus, cs)
# Initialize ethernet interface without DHCP
eth = WIZNET5K(eth_spi, eth_cs, is_dhcp=False, mac=MY_MAC)
# Set network configuration
eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
display.text("Chip Version:"+eth.chip,0,20,0)
display.text("MAC Address:",0,30,0)
display.text(eth.pretty_mac(eth.mac_address),0,40,0)
display.text("My IP address is:",0,50,0)
display.text(eth.pretty_ip(eth.ip_address),0,60,0)
display.show()
return eth
led = led_init()
display = display_init()
print("Wiznet5k Ping Test (no DHCP)")
display.fill(1)
display.text("Wiznet5k Ping Test ",0,10,0)
display.show()
eth = w5500_init()
while True:
if eth.link_status == False:
print("PHY link is down...")
display.text("PHY link is down...",0,70,0)
display.show()
time.sleep(0.1)
else:
print("PHY link is up")
print("W5500 Connect Ethernet")
display.text("PHY link is up",0,80,0)
display.text("W5500 Connect Ethernet",0,90,0)
display.show()
break
while True:
led.value = not led.value
time.sleep(0.5)
circuitpython下由于水平有限,目前只实现了PC端主动ping设备,设备主动实现ping电脑暂未实现,后面继续学习。
抓包如下:
基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作)
import board
import busio
import digitalio
import time
import adafruit_sharpmemorydisplay
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
def led_init():
#配置LED
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
return led
def display_init():
#SPI1 Display
SPI1_SCK = board.GP10
SPI1_MOSI = board.GP11
SPI1_CSn = board.GP13
#配置display的SPI
display_spi = busio.SPI(SPI1_SCK, SPI1_MOSI)
display_scs = digitalio.DigitalInOut(SPI1_CSn)
display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(display_spi, display_scs, 144, 168, baudrate=8000000)
return display
def w5500_init():
#SPI0 Ethernet
SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
#Ethernet reset
W5500_RSTn = board.GP20
ethernetRst = digitalio.DigitalInOut(W5500_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
# Setup your network configuration below
# random MAC, later should change this value on your vendor ID
MY_MAC = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05)
IP_ADDRESS = (192, 168, 1, 100)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
# For Adafruit Ethernet FeatherWing
eth_cs = digitalio.DigitalInOut(SPI0_CSn)
# For Particle Ethernet FeatherWing
# cs = digitalio.DigitalInOut(board.D5)
eth_spi = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)
# Reset W5500 first
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
# Initialize ethernet interface with DHCP
# eth = WIZNET5K(spi_bus, cs)
# Initialize ethernet interface without DHCP
eth = WIZNET5K(eth_spi, eth_cs, is_dhcp=False, mac=MY_MAC)
# Set network configuration
eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
display.text("Chip Version:"+eth.chip,0,20,0)
display.text("MAC Address:",0,30,0)
display.text(eth.pretty_mac(eth.mac_address),0,40,0)
display.text("My IP address is:",0,50,0)
display.text(eth.pretty_ip(eth.ip_address),0,60,0)
display.show()
return eth
def create_tcp_socket(mode):
if mode == 1:
#创建tcp server
# Initialize a socket for our server
socket.set_interface(eth)
tcp_server = socket.socket() # Allocate socket for the server
server_ip = None # IP address of server
server_port = 5000 # Port to listen on
tcp_server.bind((server_ip, server_port)) # Bind to IP and Port
tcp_server.listen() # Begin listening for incoming clients
conn, addr = tcp_server.accept() # Wait for a connection from a client.
print("socket connected")
return conn
else:
#创建tcp client
print("create tcp client")
def create_udp_socket(mode):
if mode == 1:
#创建udp server
# Initialize a socket for our server
socket.set_interface(eth)
udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Allocate socket for the server
server_ip = '192.168.1.59' # IP address of server
server_port = 5000 # Port to listen on
port = 5000
udp_server.bind((eth.pretty_ip(eth.ip_address),port))
print("socket connected")
else:
#创建tcp client
print("open socket")
print("Wiznet5k SimpleServer Test ")
led = led_init()
display = display_init()
display.fill(1)
display.text("Wiznet5k SimpleServer ",0,10,0)
display.show()
eth = w5500_init()
conn=create_tcp_socket(1)
display.text("client connected ",0,80,0)
display.text("recv data: ",0,90,0)
display.show()
while True:
led.value = not led.value
time.sleep(0.5)
with conn:
# data = conn.recv()
# print(data)
# conn.send(data) # Echo message back to client
while True:
data = conn.recv(10)
if data:
print(data)
display.text(hex(i) for i in data,0,100,0)
display.show()
conn.send(data) # Echo message back to client
break
print("Done!")
通过在板端构建TCP服务器,抓包可以明显看到其三次握手、四次挥手的过程,以及每次数据传输中需要频繁ACK,证实了TCP属于一种可靠且稳定的网络通信协议。
现象如图:
进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示
import board
import busio
import digitalio
import time
import adafruit_requests as requests
from adafruit_wiznet5k.adafruit_wiznet5k import *
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
from adafruit_wiznet5k.adafruit_wiznet5k_ntp import NTP
import adafruit_wiznet5k.adafruit_wiznet5k_dns as dns
days = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
def led_init():
#配置LED
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
return led
def display_init():
#SPI1 Display
SPI1_SCK = board.GP10
SPI1_MOSI = board.GP11
SPI1_CSn = board.GP13
#配置display的SPI
display_spi = busio.SPI(SPI1_SCK, SPI1_MOSI)
display_scs = digitalio.DigitalInOut(SPI1_CSn)
display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(display_spi, display_scs, 144, 168, baudrate=8000000)
return display
def w5500_init():
#SPI0 Ethernet
SPI0_SCK = board.GP18
SPI0_TX = board.GP19
SPI0_RX = board.GP16
SPI0_CSn = board.GP17
#Ethernet reset
W5500_RSTn = board.GP20
ethernetRst = digitalio.DigitalInOut(W5500_RSTn)
ethernetRst.direction = digitalio.Direction.OUTPUT
# Setup your network configuration below
# random MAC, later should change this value on your vendor ID
MY_MAC = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05)
IP_ADDRESS = (192, 168, 1, 100)
SUBNET_MASK = (255, 255, 255, 0)
GATEWAY_ADDRESS = (192, 168, 1, 1)
DNS_SERVER = (8, 8, 8, 8)
# For Adafruit Ethernet FeatherWing
eth_cs = digitalio.DigitalInOut(SPI0_CSn)
# For Particle Ethernet FeatherWing
# cs = digitalio.DigitalInOut(board.D5)
eth_spi = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX)
# Reset W5500 first
ethernetRst.value = False
time.sleep(1)
ethernetRst.value = True
# Initialize ethernet interface with DHCP
# eth = WIZNET5K(spi_bus, cs)
# Initialize ethernet interface without DHCP
eth = WIZNET5K(eth_spi, eth_cs, is_dhcp=False, mac=MY_MAC)
# Set network configuration
eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
display.text("Chip Version:"+eth.chip,0,20,0)
display.text("MAC Address:",0,30,0)
display.text(eth.pretty_mac(eth.mac_address),0,40,0)
display.text("My IP address is:",0,50,0)
display.text(eth.pretty_ip(eth.ip_address),0,60,0)
display.show()
return eth
print("Wiznet5k ntp Test ")
led = led_init()
display = display_init()
display.fill(1)
display.text("Wiznet5k ntp Test",0,10,0)
display.show()
eth = w5500_init()
# Initialize a socket for our server
#socket.set_interface(eth)
# Set network configuration
#eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER)
#NTP
ntpserver_ip = eth.pretty_ip(eth.get_host_by_name("time.google.com"))
print("NTP : %s" % ntpserver_ip) #DNS Domain
ntp = NTP(iface = eth, ntp_address =ntpserver_ip ,utc=9)
cal = ntp.get_time()
print("The date is %s %d/%d/%d" %(days[cal.tm_wday], cal.tm_mday,cal.tm_mon,cal.tm_year))
print("The time is %d:%02d:%02d" %(cal.tm_hour,cal.tm_min,cal.tm_sec))
该任务官方已经提供了示例程序,基于此又重新编写学习一下,但是不知道为什么,跑官方还有自己重新编写的程序都总是报错,提示少传入一个参数,后来又跟论坛上坛友发的贴子实现过程对照了一下,没什么太大出入,copy坛友的程序也跑不下来,不清楚什么原因。
现象如下:
DNS服务器已经根据域名解析出了目标NTP服务器的IP地址,证明网络应该是没问题的。
终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。
使用一个SD NAND卡+一个TF卡的转接板,将SD NAND卡的信号引脚引出,用杜邦线通过SPI1接到SD NAND上。
根据坛友分享的帖子,大致了解了FTP的实现流程,分别需要两个socket,一个用来做控制,包括登录认证之类的,另一个用来做文件传输,并且FTP是基于TCP传输层协议的,另外还知道了TFTP是另一种基于UDP传输层协议的应用。
下面根据坛友分享的进行学习
#
# Small ftp server for ESP8266 ans ESP32 Micropython
#
# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky
#
# The server accepts passive mode only.
# It runs in foreground and quits, when it receives a quit command
# Start the server with:
#
# import ftp
#
# Copyright (c) 2016 Christopher Popp (initial ftp server framework)
# Copyright (c) 2016 Robert Hammelrath (putting the pieces together
# and a few extensions)
# Distributed under MIT License
#
import socket
import network
import uos
import gc
#from usocket import socket
from machine import Pin,SPI,UART,PWM
import time, network,framebuf
''' static netinfo
'''
ip = '192.168.1.20'
sn = '255.255.255.0'
gw = '192.168.1.1'
dns= '8.8.8.8'
BL = 13
DC = 8
RST = 12
MOSI = 11
SCK = 10
CS = 9
netinfo=(ip, sn, gw, dns)
localip = ''
localport = 8000
listen_info = (localip, localport)
conn_flag = False
def w5x00_init():
global localip
spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
nic = network.WIZNET5K(spi,Pin(17),Pin(20))
nic.active(True)
# use dhcp, if fail use static netinfo
#try:
# nic.ifconfig('dhcp')
#except:
nic.ifconfig(netinfo)
localip = nic.ifconfig()[0]
print('ip :', nic.ifconfig()[0])
print('sn :', nic.ifconfig()[1])
print('gw :', nic.ifconfig()[2])
print('dns:', nic.ifconfig()[3])
while not nic.isconnected():
time.sleep(1)
# print(nic.regs())
print('no link')
return nic
def send_list_data(path, dataclient, full):
try: # whether path is a directory name
for fname in sorted(uos.listdir(path), key=str.lower):
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 sorted(uos.listdir(path), key=str.lower):
if fncmp(fname, pattern):
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]
description = "{} 1 owner group {:>10} Jan 1 2000 {}\r\n".format(
file_permissions, file_size, fname)
else:
description = fname + "\r\n"
return description
def send_file_data(path, dataclient):
with open(path, "rb") as file:
chunk = file.read(512)
while len(chunk) > 0:
dataclient.sendall(chunk)
chunk = file.read(512)
def save_file_data(path, dataclient):
with open(path, "wb") as file:
chunk = dataclient.recv(512)
while len(chunk) > 0:
file.write(chunk)
chunk = dataclient.recv(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:]):
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(net, port=21, timeout=None):
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", port)[0][4])
datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4])
ftpsocket.listen(1)
ftpsocket.settimeout(timeout)
datasocket.listen(1)
datasocket.settimeout(None)
msg_250_OK = '250 OK\r\n'
msg_550_fail = '550 Failed\r\n'
addr = net.ifconfig()[0]
print("FTP Server started on ", addr)
try:
dataclient = None
fromname = None
do_run = True
while do_run:
cl, remote_addr = ftpsocket.accept()
cl.settimeout(300)
cwd = '/'
try:
# print("FTP connection from:", remote_addr)
cl.sendall("220 Hello, this is Micropython.\r\n")
while True:
gc.collect()
data = cl.readline().decode("utf-8").rstrip("\r\n")
if len(data) <= 0:
print("Client disappeared")
do_run = False
break
command = data.split(" ")[0].upper()
payload = data[len(command):].lstrip()
path = get_absolute_path(cwd, payload)
print("Command={}, Payload={}".format(command, payload))
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" or command == "XPWD":
cl.sendall('257 "{}"\r\n'.format(cwd))
elif command == "CWD" or command == "XCWD":
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')
do_run = False
break
elif command == "PASV":
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)
DATA_PORT = 13333
active = False
elif command == "PORT":
items = payload.split(",")
if len(items) >= 6:
data_addr = '.'.join(items[:4])
# replace by command session addr
if data_addr == "127.0.1.1":
data_addr = remote_addr
DATA_PORT = int(items[4]) * 256 + int(items[5])
dataclient = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
dataclient.settimeout(10)
dataclient.connect((data_addr, DATA_PORT))
print("FTP Data connection with:", data_addr)
cl.sendall('200 OK\r\n')
active = True
else:
cl.sendall('504 Fail\r\n')
elif command == "LIST" or command == "NLST":
if not payload.startswith("-"):
place = path
else:
place = cwd
try:
cl.sendall("150 Here comes the directory listing.\r\n")
send_list_data(place, dataclient,
command == "LIST" or payload == "-l")
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:
cl.sendall("150 Opening data connection.\r\n")
send_file_data(path, dataclient)
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)
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" or command == "XRMD":
try:
uos.rmdir(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "MKD" or command == "XMKD":
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
elif command == "MDTM":
try:
tm=localtime(uos.stat(path)[8])
cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6]))
except:
cl.sendall('550 Fail\r\n')
elif command == "STAT":
if payload == "":
cl.sendall("211-Connected to ({})\r\n"
" Data address ({})\r\n"
"211 TYPE: Binary STRU: File MODE:"
" Stream\r\n".format(
remote_addr[0], addr))
else:
cl.sendall("213-Directory listing:\r\n")
send_list_data(path, cl, True)
cl.sendall("213 Done.\r\n")
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
except Exception as e:
print(e)
finally:
datasocket.close()
ftpsocket.close()
if dataclient is not None:
dataclient.close()
if __name__ == "__main__":
nic = w5x00_init()
ftpserver(nic)
运行之后,SD NAND挂载正常,但是不知道为什么,在进行socket初始化的时候,又一个提示参数无效的报错,已经成功刷成micropython的固件,并且该导入的库我也导入了。
心得体会
本期是23年的第四期follow me活动,确实对得起这个第四期,个人感觉确实有些难度,刚开始不太懂网络协议栈确实有些吃力,后面跟着坛友们的分享,一步步走下去,虽然坑还是没少趟,但是收获也是有的,2024继续加油!
内容三、可编译下载的代码
-
加入了学习《 【得捷电子Follow me第4期】》,观看 【得捷Follow me第4期】简易FTP文件服务器
- 2024-02-04
-
回复了主题帖:
共读入围名单:《原子嵌入式Linux驱动开发详解与实战(ARM Linux驱动)》
个人信息无误,确认可以完成
- 2024-02-02
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门
- 2023-12-21
-
加入了学习《【DigiKey“智造万物,快乐不停”创意大赛】 钢铁侠的智能助手贾维斯》,观看 【DigiKey“智造万物,快乐不停”创意大赛】 钢铁侠的智能助手贾维斯