- 2024-03-20
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico platform IO下使用Ethernet3库设置静态IP
lugl4313820 发表于 2024-3-9 16:22
第四步:在setup初始化函数中设置CS和rst引脚,并调用begin()函数/方法初始化W5500的网络参数。
初始化 ...
是的
主要是有人造好了轮子
- 2024-03-10
-
发表了主题帖:
【得捷Follow me第4期】项目总结
因起:rp2040早两年就面世了,并且在不同的开发平台都有对其的支持,本身也是一款性能比较强劲的芯片,这次W5500-EVB-Pico在官方pico基础上增加了W5500这颗 集成全硬件TCPIP协议栈网络芯片,是一个学习通过嵌入式平台接入以太网的,以及各种网络协议栈学习的绝好机会。
**在每个帖子中,均有安装过程,代码和最后运行的结果(静态图片和动画的形式展现),硬件相对简单,没有录制视频,后边有时间再补充。
发帖地址汇总:
入门任务: 第一帖:【得捷Follow me第4期】W5500-EVB-Pico arduino离线HFS方式搭建第三方库开发环境 帖子中有完整的离线安装过程、库文件链接和github加速下载...
第二帖:【得捷Follow me第4期】W5500-EVB-Pico platform io方式开发环境踩坑记录
基础任务一:第三帖: 【得捷Follow me第4期】W5500-EVB-Pico platform IO下使用Ethernet3库设置静态IP
基础任务二:第四帖: 【得捷Follow me第4期】W5500-EVB-Pico PIO UDP收发测试
第五帖: 【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
进阶任务: 第六帖: 【得捷Follow me第4期】W5500-EVB-Pico PIO通过NTP服务器获取网络时间
终极任务二:第七帖:【得捷Follow me第4期】W5500-EVB-Pico PIO 挂载SD卡并实现文件读写访问
第八帖: 未完待续......
入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld)
① arduino
第一个任务主要是开发环境搭建,这里没有使用其他小伙伴使用的***Python环境,而是使用了Arduino环境和vscode + platformIO环境,原因,第一平时用python不多,第二主要是借助本次测试正好进一步了解一下vscode + platformIO环境下的开发板添加、各种库的使用方法、以及开发环境中的各种配置。
任务一分两步进行,首先在Arduino环境中进行了rp2040 pico开发环境的安装,因为中rp2040仅支持arduino环境。 所以想着先通过Arduino中搭建开发环境并测试开发板,以验证rp2040底层,跑通“helloworld”第一步,这样,方便在vscode + platformIO中万一遇到各种问题,好排查问题所在。
过程比较曲折,开始尝试通过在线安装,两次没成功。
后来网上搜索,使用离线方式安装,同时发现了一个非官方的库,大佬就是大佬(感谢开源路上各个无私奉献的大佬们!!!),arduino官方中的库应该也是来自于这位大佬,不过更新/同步不及时。于是乎使用HFS离线方式在arduino搭建了rp2040 pico 的库以及工具链等,中vscode + platformIO间过程也还好,不算太绕。
arduino库提供了丰富的示例库,无论是通用的arduino开发板,还是rp2040 pico 。但是弊端是没有编程过程中的代码提示功能和调试功能。(后来发现在visualStudio中已经有支持arduino工程的插件),于是,打算通过测评,测试另一目标比较流行的开发方式:vscode + platformIO。
结论:通过例程BLINK,arduino的库,是最快入门嵌入式学习的途径,不要太好用。
② vscode + platformIO
首先是vscode + platformIO的安装。
安装完成后,便可以通过添加新工程的方式,让platformIO自动将rp2040 pico的库和工具链添加到系统环境。
这一步在线安装比在arduino中快很多,一次成功。
如果某硬件arduino和platformIO中是同步的,那么arduino中的例程是可以直接拷贝过来使用的,仅需要在mian.cpp中增加“#include <arduino.h>”,添加对arduino.h的应用即可。也就是platformIO可以借鉴arduino丰富的生态,并且绝大多数arduino的库都有使用例程。
platformIO也提供了在线搜索library的功能,并且查询到的库,可以通过链接打开库的网址。并且,如果直接使用这些库,不需要修改的话,仅需在PIO HOME中查询库,打开库之后单击 即可。
下图演示过程:
下载的库文件也会添加到lib文件夹下:
另一种添加第三方库的方式是,手动查找对应的库,下载后,将库手动拷贝至工程目录下对应的lib文件夹内即可。platformIO中提供ldf功能,会自动搜索lib文件下下的源文件,在编译时进行编译。
这里遇到的一个坑是,rp2040 pico通过usb下载一键下载程序使用驱动问题,在帖子中也有详细说明。这一步,在arduino中工具菜单下通过一些选项配置即可完成,没有遇到问题。
结论:过程有一些曲折,但是代码编辑自动提示、可以直接借鉴arduino生态,还是有新引力的(另外,platformIO对esp32、esp8266、stm32多个系列均有支持)。
基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
因为此前对platformIO的不熟悉,因此,在本任务中,主要首先网络搜索,发现有Ethernet3库,这个库比较新,并且提供了对W5500的支持。
后来发现,pico库中自带Ethernet库,不过不支持CS引脚和RST引脚,需要在此基础上进行二次修改,同时Ethernet是否原生支持W5500,也是个问题。
因为,在后边的UDP和TCP/IP中都需要以本任务做基础,另,当时写本任务和后面基础任务二、进阶任务时,当时pico不在身边,没法测试。所以,本任务无最终测试,但是通过基础任务二、进阶任务,可知,使用Ethernet3可以完成本任务基本要求。
同时,在计算机端ping PICO的ip地址,可以返回数据(在其他任务中测试)。
结论:Ethernet3“是”pico原生Ethernet库的扩展,提供了CS引脚和RST引脚以及对应的io操作函数,以进行片选和硬件复位,同时通了udp、tcp/ip等协议栈的接口操作。
基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作)
① Udp协议收发测试
这里使用了ETHERNET3库中的示例代码,pico建立udp服务端,pc端使用net assist软件模拟udp客户端,进行数据收发。
PC端:
pico端(下图开始时有一段时间静止(在操作pc端)):
② TCP/IP协议 Webclient的实现
依然使用Ethernet3库,使用其web client例程
首先创建web client客户端,然后向服务端发起连接请求,当接收到数据后,打印接收数据。
结论:成功建立web client ,访问baidu.com,并成功返回连接请求数据。
③ TCP/IP协议 Web server 的实现
这里继续使用Ethernet3库,使用pico创建web 服务器,在pc端,使用net assist软件,建立web 客户端。客户端发送数据给pico,pico进行应答。
pico 服务端:
pc 客户端:
结论:作为web server可以成功接收客户端发送的请求数据,(写总结时)发现,pico作为服务端没有成功返回给客户端数据,这个后边查卡壳原因。
进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示。
从NTP服务器获取网络时间,还是在UDP协议上的一个延伸,只要udp协议可以正常收发数据。
接下来就是按照NTP要求向NTP服务器发送请求数据报文即可获取时间数据,接下来就是对返回时间戳进行计算,根据需要计算得到不同格式的时间数据。
在例子中分别得到了1900年起到现在的秒数、1970年起到现在的秒数、和当前的日期和时间。
结论:在rp2040 pico中通过w5500可以方便实现有线连接获取网络时间,从而实现本地校时功能。
终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。
这一步需要将之前的Ethernet功能基础上增加ftp服务,同时需要在原来硬件基础上,扩展SD卡。需要使用多个库才能实现最终功能。
① 首先实现pico 硬件SPI1 挂载SD卡
这里使用了RP2040_SD 库,同时在RP2040_SD 库的基础上进行了部分修改(RP2040_SD 库,以及 pico的库文件),使得支持硬件SPI1访问SD卡(原生仅支持SPI0)。原贴中有详细的修改步骤。
运行结果:
硬件连接:
未实现部分:
最后一个任务需要使用Ethernet库、FTP server库和SD库,因为原生pico库中没有很好的提供硬件支持(或者没找到),在实现以上任务中使用到的库文件在最后一个任务中,出现了不兼容的情况,因此FTP server和SD库,总有部分编译时出现各种重定义、错误等警告。
业余时间又断断续续折腾了一周,还是不见效果,但是折腾的过程也逐渐学习了各种库的实现、platformio库文件管理、不通库如何相互协作(引用)、C++语法等相关知识内容。
同时在折腾中,也发现了一些非常好的库文件,后边有时间再继续折腾。
-
发表了日志:
【得捷Follow me第4期】项目总结
-
加入了学习《【得捷电子Follow me第4期】任务视频》,观看 【得捷电子Follow me第4期】任务视频
- 2024-03-05
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO 挂载SD卡并实现文件读写访问
外部SD卡挂载成功,并检测到SD卡类型,读写文件。
待解决:打印目录及其内容执行有异常...
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO UDP收发测试
udp rp2040串口打印信息:
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO UDP收发测试
UDP 测试pc端口
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
web server 服务器端串口打印信息:
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
web server客户端截屏:
- 2024-03-04
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
在pio中,rp2040的库没有ethernet或者有点乱,没找到。这里选用了外部第三方库,ethernet3为arduino生态中最新的库,并且适配了w5500,支持了rp2040(只需要“借用”一下硬件spi即可),通过测试,webclient成功从百度获取数据,说明移植没有问题。Webserver例程应该也没有问题,限于时间关系,今天没有测试成功,明天测试一下…
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO 挂载SD卡并实现文件读写访问
eew_9XVJps 发表于 2024-3-4 16:10
学习学习,修改这个SPI还挺复杂,之前研究过,没改过来,楼主这个介绍的很详细呀
希望对你有帮助其实核心就是sd卡各个class能够使用spi1和对应setting类中的参数访问到硬件spi1上就可以了。库本身应该都是能用的说起来简单,确实别人的东西改起来不顺利的话,会把自己整晕进去,然后重来一遍。rp2040说实话,pio的库比起8266和esp32起来,差的有点远,尤其是外部扩展应用上折腾这几天,收获很大
-
回复了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
成功从百度获取数据
- 2024-03-03
-
发表了日志:
【得捷Follow me第4期】W5500-EVB-Pico PIO FTP服务器测试
-
发表了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO 挂载SD卡并实现文件读写访问
本节实现功能:通过使用第三方SD库,实现PICO 访问外扩SD卡,并实现读取卡的信息、显示SD卡目录列表、文件读写访问...
1. 准备 下载SD卡扩展库
地址:https://github.com/khoih-prog/RP2040_SD
概述:This library enables you to use SPI SD cards with RP2040-based boards such as Nano_RP2040_Connect, RASPBERRY_PI_PICO using either RP2040 Arduino-mbed or arduino-pico core. This SD-Fat v2 can support FAT16, FAT32, exFAT file systems. exFAT supports files larger than 4GB by using uint64_t as file offset.
该库主要为RP2040而来,支持FAT16, FAT32, exFAT,同时支持容量大于4GB。
*但是该库内部使用了PICO的spi0,由于W5500连接了SPI0,这里需要做一些改动,使得外扩SD卡连接到SPI1。
2. 创建基础工程,并将下载的库复制到工程的lib文件夹中
/*
SD card connection
This example shows how to read an SD Card's CardInfo
shows how to read and write data to and from an SD card file
The circuit:
SD card attached to SPI bus as follows:
// Arduino-pico core
** MISO - pin 12
** MOSI - pin 11
** CS - pin 13
** SCK - pin 10
*/
#include <Arduino.h>
#include <SPI.h>
#include <RP2040_SD.h>
#define _RP2040_SD_LOGLEVEL_ 1
#define PIN_SD_SS PIN_SPI1_SS
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
File root;
#define fileName "example.txt"
File myFile;
uint32_t writeData = 0xDEADBEEF;
uint32_t readData = 0;
void printDirectory(File dir, int numTabs)
{
while (true)
{
File entry = dir.openNextFile();
if (! entry)
{
// no more files
break;
}
for (uint8_t i = 0; i < numTabs; i++)
{
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory())
{
Serial.println("/");
printDirectory(entry, numTabs + 1);
}
else
{
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}
bool checkFileExist(const char * fileNameInput)
{
// Check to see if the file exists now
if (SD.exists(fileNameInput) )
{
Serial.print(fileNameInput);
Serial.println(" exists.");
return true;
}
else
{
Serial.print(fileNameInput);
Serial.println(" doesn't exist.");
return false;
}
}
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial);
delay(1000);
#if defined(ARDUINO_ARCH_MBED)
Serial.print("Starting SD Card CardInfo on MBED ");
#else
Serial.print("Starting SD Card CardInfo on ");
#endif
Serial.println(BOARD_NAME);
Serial.println(RP2040_SD_VERSION);
Serial.print("Initializing SD card with SS = ");
Serial.println(PIN_SPI1_SS);
Serial.print("SCK = ");
Serial.println(SPI1_SCK);
Serial.print("MOSI = ");
Serial.println(SPI1_MOSI);
Serial.print("MISO = ");
Serial.println(SPI1_MISO);
// we'll use the initialization code from the utility libraries
// since we're just testing if the card is working!
if (!card.init(SPI_HALF_SPEED, PIN_SPI1_SS))
{
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
while (1);
}
else
{
Serial.println("Wiring is correct and a card is present.");
}
// print the type of card
Serial.println();
Serial.print("Card type: ");
switch (card.type())
{
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card))
{
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
while (1);
}
Serial.print("Clusters: ");
Serial.println(volume.clusterCount());
Serial.print("Blocks x Cluster: ");
Serial.println(volume.blocksPerCluster());
Serial.print("Total Blocks: ");
Serial.println(volume.blocksPerCluster() * volume.clusterCount());
Serial.println();
// print the type and size of the first FAT-type volume
uint32_t volumesize;
Serial.print("Volume type is: FAT");
Serial.println(volume.fatType(), DEC);
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
volumesize /= 2; // SD card blocks are always 512 bytes (2 blocks are 1KB)
Serial.print("Volume size (Kb): ");
Serial.println(volumesize);
Serial.print("Volume size (Mb): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Gb): ");
Serial.println((float)volumesize / 1024.0);
if (!SD.begin(PIN_SD_SS))
{
Serial.println("Initialization failed!");
return;
}
Serial.println("Initialization done.");
root = SD.open("/");
printDirectory(root, 0);
Serial.println("read cardinfo done.");
checkFileExist(fileName);
// open a new file and immediately close it:
Serial.print("Creating ");
Serial.println(fileName);
myFile = SD.open(fileName, FILE_WRITE);
if (myFile)
{
myFile.write((uint8_t *) &writeData, sizeof(writeData));
Serial.print("writeData = 0x");
Serial.println(writeData, HEX);
myFile.close();
}
else
{
Serial.print("Error open for writing ");
Serial.println(fileName);
}
myFile = SD.open(fileName, FILE_READ);
if (myFile)
{
myFile.read((uint8_t *) &readData, sizeof(readData));
Serial.print("readData = 0x");
Serial.println(readData, HEX);
myFile.close();
}
else
{
Serial.print("Error open for reading ");
Serial.println(fileName);
}
checkFileExist(fileName);
// delete the file:
Serial.println("Removing example.txt...");
SD.remove(fileName);
checkFileExist(fileName);
}
void loop()
{
// nothing happens after setup finishes.
}
第一步:定义全局对象
Sd2Card card;
SdVolume volume;
File root;
第二步:使用card.init()进行SD卡初始化,内部使用了SPI1。
第三步:根据card.type() 输出SD卡类型。
第四步:volume.init(card) SD卡卷的初始化,并打印显示SD卡卷信息。
第五步:再次SD.begin()初始化SD卡,SD.open("/")打开SD卡根目录。
第六步:printDirectory(root, 0);遍历SD卡目录并通过串口打印显示。
第七步:checkFileExist()检测文件是否存在SD卡中,并通过函数SD.open、File.write、File.close、File.read、SD.remove等实现文件打开、增删改查。
3. 需要修改的地方
3.1. \.platformio\packages\framework-arduino-mbed\variants\RASPBERRY_PI_PICO\pins_arduino.h
增加:
// SPI1
#define PIN_SPI1_MISO (12u)
#define PIN_SPI1_MOSI (11u)
#define PIN_SPI1_SCK (10u)
#define PIN_SPI1_SS (13u)
修改:#define SPI_HOWMANY (1) (MBED的库默认仅设置了SPI0 引脚的相关宏定义)
为:
#define SPI_HOWMANY (2)
增加:
#define SPI1_MISO (digitalPinToPinName(PIN_SPI1_MISO))
#define SPI1_MOSI (digitalPinToPinName(PIN_SPI1_MOSI))
#define SPI1_SCK (digitalPinToPinName(PIN_SPI1_SCK))
spi.h和spi.c文件相关内容如下:
spi1在定义的时候已经绑定了对应的引脚
3.2 RP2040_SD库下 RP2040_SD\src\utility\Sd2Card.cpp
修改 SDCARD_SPI SPI为:
#define SDCARD_SPI SPI1
-
发表了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO下TCP协议实现Webclient和WebServer
本帖最后由 wo4fisher 于 2024-3-2 16:25 编辑
1. 准备工作
新建基础工程,并添加Ethernet3库到工程中
2. WebClient测试
使用Tcp协议创建WebClient,连接远端服务器,并发送http请求,并打印返回数据
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet3.h>
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS)
char server[] = "www.baidu.com"; // name address for Google (using DNS)
// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 2, 177);
IPAddress myDns(192, 168, 2, 1);
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;
// Variables to measure the speed
unsigned long beginMicros, endMicros;
unsigned long byteCount = 0;
bool printWebData = true; // set to false for better speed measurement
uint8_t svrIp[4]= {0};
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Ethernet.setCsPin(17);
Ethernet.setRstPin(20);
// start the Ethernet connection:
Serial.println("Initialize Ethernet with DHCP:");
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware link status
if (Ethernet.link() == 0) {
Serial.println("Ethernet cable is not connected.");
}
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip, myDns);
} else {
Serial.print(" DHCP assigned IP ");
Serial.println(Ethernet.localIP());
}
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.print("connecting to ");
Serial.print(server);
Serial.println("...");
// if you get a connection, report back via serial:
if (client.connect(server, 80)) {
Serial.print("connected to ");
client.remoteIP(svrIp);
Serial.print(svrIp[0]);Serial.print(',');
Serial.print(svrIp[1]);Serial.print(',');
Serial.print(svrIp[2]);Serial.print(',');
Serial.print(svrIp[3]);Serial.println();
// Make a HTTP request:
client.println("GET /search?q=arduino HTTP/1.1");
client.println("Host: www.baidu.com");
client.println("Connection: close");
client.println();
} else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
beginMicros = micros();
}
void loop() {
// if there are incoming bytes available
// from the server, read them and print them:
int len = client.available();
if (len > 0) {
byte buffer[80];
if (len > 80) len = 80;
client.read(buffer, len);
if (printWebData) {
Serial.write(buffer, len); // show in the serial monitor (slows some boards)
}
byteCount = byteCount + len;
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
endMicros = micros();
Serial.println();
Serial.println("disconnecting.");
client.stop();
Serial.print("Received ");
Serial.print(byteCount);
Serial.print(" bytes in ");
float seconds = (float)(endMicros - beginMicros) / 1000000.0;
Serial.print(seconds, 4);
float rate = (float)byteCount / seconds / 1000.0;
Serial.print(", rate = ");
Serial.print(rate);
Serial.print(" kbytes/second");
Serial.println();
// do nothing forevermore:
while (true) {
delay(1);
}
}
}
实现过程:
第一步:定义mac地址、W5500 静态ip地址、DNS地址和web服务器主机ip名称。
第二步:定义EthernetClient。
第三步:在初始化函数setup中完成cs引脚和rst引脚的配置,调用begin函数完成W5500网络参数初始化(首先尝试动态分配,动态分配失败的话,使用静态分配方式进行初始化),接下来调用connect方法连接远端服务器。
第四步:在循环函数Loop中,当接收到返回数据,通过串口打印;断开连接的话,停止。
3. WebServer测试
创建WebServer服务器,如果客户端连接访问,返回pico ADC模数转换结果
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet3.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 2, 177);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
uint8_t ClientIp[4]= {0};
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Ethernet.setCsPin(17);
Ethernet.setRstPin(20);
Serial.println("Ethernet WebServer Example");
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
// start the server
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank)
{
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
int sensorReading = analogRead(A3);
client.print("pico Voltage");
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
实现过程:
第一步:定义mac地址、W5500 静态ip地址。
第二步:定义EthernetServer,并使用默认80端口。
第三步:在初始化函数setup中完成cs引脚和rst引脚的配置,调用server.begin函数完成W5500网络参数初始化并启动Web服务器。
第四步:在循环函数Loop中,当有客户端连接请求,打印请求内容,之后,读取电源电压ADC转换值,并返回HTTP请求给客户端。
-
发表了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO通过NTP服务器获取网络时间
本帖最后由 wo4fisher 于 2024-3-5 13:42 编辑
1. 可用的NTP服务器测试
使用如下的命令可以测试NTP授时服务器是否可用
w32tm /stripchart /computer:ntp_server_address
ntp_server_address 可以是服务器主机名称,也可以是IP地址。
可用的NTP服务器地址:
国家授时中心 NTP 服务器 ntp.ntsc.ac.cn 114.118.7.161 114.118.7.163
中国 NTP 快速授时服务 cn.ntp.org.cn 223.113.97.98 119.29.26.206
教育网 edu.ntp.org.cn 202.118.1.130 202.118.1.81
2. 新建基本工程并添加Ethernet3库文件到工程lib目录
3. 代码编辑
/*
Udp NTP Client
Get the time from a Network Time Protocol (NTP) time server
Demonstrates use of UDP sendPacket and ReceivePacket
For more on NTP time servers and the messages needed to communicate with them,
see http://en.wikipedia.org/wiki/Network_Time_Protocol
created 4 Sep 2010
by Michael Margolis
modified 9 Apr 2012
by Tom Igoe
modified 02 Sept 2015
by Arturo Guadalupi
This code is in the public domain.
*/
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet3.h>
#include <EthernetUdp3.h>
#define FOURYEARDAY (365 + 365 + 365 + 366) // 4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化)
#define TIMEZONE (8) // 北京时区调整
typedef struct rtc_time_struct
{
uint16_t ui8Year; // 1970~2038
uint8_t ui8Month; // 1~12
uint8_t ui8DayOfMonth; // 1~31
uint8_t ui8Week;
uint8_t ui8Hour; // 0~23
uint8_t ui8Minute; // 0~59
uint8_t ui8Second; // 0~59
} rtc_time_t;
static uint8_t month_day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 平年
static uint8_t Leap_month_day[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 闰年
// static char weekCHS[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
unsigned int localPort = 8888; // local port to listen for UDP packets
const char timeServer[] = "ntp.ntsc.ac.cn"; // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// 判断是否是闰年 return:1:闰年, 0: 平年
uint8_t isLeapYear(uint16_t year);
// 将Unix时间戳转换为北京时间
void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing);
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial)
{
; // wait for serial port to connect. Needed for native USB port only
}
Ethernet.setCsPin(17);
Ethernet.setRstPin(20);
// start Ethernet and UDP
if (Ethernet.begin(mac) == 0)
{
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
for (;;)
;
}
Udp.begin(localPort);
}
void sendNTPpacket(const char *address);
void loop()
{
sendNTPpacket(timeServer); // 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 date & Time
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
rtc_time_t tempBeijing2;
char dtBuf[30] = {0};
covUnixTimeStp2Beijing(epoch, &tempBeijing2);
sprintf(dtBuf, "%d/%02d/%02d-%02d:%02d:%02d",
tempBeijing2.ui8Year, tempBeijing2.ui8Month, tempBeijing2.ui8DayOfMonth,
tempBeijing2.ui8Hour, tempBeijing2.ui8Minute, tempBeijing2.ui8Second);
Serial.println(dtBuf);
}
// wait ten seconds before asking for the time again
delay(6000);
Ethernet.maintain();
}
// send an NTP request to the time server at the given address
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();
}
// 判断是否是闰年
// year: 需要判断的年
// return:1:闰年
// 0: 平年
uint8_t isLeapYear(uint16_t year)
{
uint8_t res = 0;
if (year % 4 == 0) // 能够被4整除
{
if ((year % 100 == 0) && (year % 400 != 0)) // 能够被100整除,但是不能够被400整除
{
res = 0;
}
else
{
res = 1;
}
}
return res;
}
// 将Unix时间戳转换为北京时间
// unixTime: 需要判断的Unix时间戳
// *tempBeijing:返回的北京时间
// return:none
// note:没对输入参数正确性做判断
void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing)
{
uint32_t totleDayNum = 0, totleSecNum = 0;
uint16_t remainDayofYear = 0, tempYear = 0;
uint8_t *pr = NULL;
totleDayNum = unixTime / (24 * 60 * 60); // 总天数(注意加括号)
totleSecNum = unixTime % (24 * 60 * 60); // 当天剩余的秒速
memset(tempBeijing, 0x00, sizeof(rtc_time_t));
// 1.先计算时间 HH:MM:SS
tempBeijing->ui8Hour = totleSecNum / 3600;
tempBeijing->ui8Minute = (totleSecNum % 3600) / 60; // error:变量搞错
tempBeijing->ui8Second = (totleSecNum % 3600) % 60;
// 2.对时间进行时区调整(注意:这里可能造成日期 +1)
tempBeijing->ui8Hour += TIMEZONE;
if (tempBeijing->ui8Hour > 23)
{
// printf("modify day..\n");
tempBeijing->ui8Hour -= 24;
remainDayofYear++; // 日期+1
}
// 3.计算哪一年
tempBeijing->ui8Year = 1970 + (totleDayNum / FOURYEARDAY) * 4; // 4年为一个周期
remainDayofYear += totleDayNum % FOURYEARDAY;
// printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
tempYear = isLeapYear(tempBeijing->ui8Year) ? 366 : 365;
while (remainDayofYear >= tempYear) // 计算4年整数倍外的年。
{
tempBeijing->ui8Year++;
remainDayofYear -= tempYear;
tempYear = isLeapYear(tempBeijing->ui8Year) ? 366 : 365;
}
// 4.计算哪一月的哪一天
pr = isLeapYear(tempBeijing->ui8Year) ? Leap_month_day : month_day;
remainDayofYear++; // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号,所以这里要 +1
while (remainDayofYear > *(pr + tempBeijing->ui8Month))
{
remainDayofYear -= *(pr + tempBeijing->ui8Month);
tempBeijing->ui8Month++;
}
// printf("year:%d, day:%d.\n", tempBeijing->ui8Year, remainDayofYear);
tempBeijing->ui8Month++; // month
tempBeijing->ui8DayOfMonth = remainDayofYear; // day
// printf("year:%d, day:%d.\n", tempBeijing->ui8Year, tempBeijing->ui8DayOfMonth);
}
主要步骤:
第一步:定义mac地址、本地端口、ntp服务器,并且定义UDP客户端EthernetUDP实例。
第二步:在初始化函数setup中使用Ethernet.begin函数初始化W5500网络参数,然后启动udp客户端: Udp.begin(localPort)。
第三步:在Loop函数中 首先向NTP服务器发送请求包sendNTPpacket(),然后解析接收到的应答数据包,并且分别显示自1900年开始的秒数、UNIX时间(即从1970年1月1日开始的秒数)和NTC时间的时分秒。
备注:中间有增加unix时间戳计算当前日期和时间的函数,没有计算星期几。
结果,NTP时间和右下角显示时间一致
-
发表了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico PIO UDP收发测试
本帖最后由 wo4fisher 于 2024-3-2 17:58 编辑
1. 新建基础工程并添加Ethernet3库文件到工程目录
2. UDP协议收发代码编辑
Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。
即UDP基于无连接,通过IP地址和端口即可以收发数据。
上代码:
#include <Arduino.h>
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet3.h>
#include <EthernetUdp3.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 2, 177);
unsigned int localPort = 8888; // local port to listen on
// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged"; // a string to send back
// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
void setup() {
Ethernet.setCsPin(17);
Ethernet.setRstPin(20);
// start the Ethernet and UDP:
Ethernet.begin(mac, ip);
Udp.begin(localPort);
Serial.begin(115200);
}
void loop() {
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize)
{
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remote = Udp.remoteIP();
for (int i = 0; i < 4; i++)
{
Serial.print(remote[i], DEC);
if (i < 3)
{
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(Udp.remotePort());
// read the packet into packetBufffer
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
Serial.println("Contents:");
Serial.println(packetBuffer);
// send a reply, to the IP address and port that sent us the packet we received
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
}
delay(10);
}
主要步骤:
第一步:定义mac地址、本机ip地址、端口号和EthernetUDP 实例。
第二步:在setup中
初始化/定义cs引脚和rst引脚,
Ethernet.begin(mac, ip);初始化网络参数
Udp.begin(localPort);启动UDP服务器
第三步:在loop中
监听Udp.parsePacket(),如果有数据,进行解析
Udp.read()读取数据并通过串口输出后,再通过Udp.write()函数进行应答。
other:
UDP客户端实例就会以上例子反过来,因为UDP为无连接协议。因此
大概思路就是:
实例化UDP
封装发送数据
通过
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
进行发送
然后解析、读取应答数据
Udp.parsePacket();
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
需要的话增加超时判断(可能没有应答)即可。
-
发表了主题帖:
【得捷Follow me第4期】W5500-EVB-Pico platform IO下使用Ethernet3库设置静态IP
本篇完成任务:使用arduino Ethernet3库,设置静态IP,连接远端服务器测试
1. Ethernet3库的获取
https://github.com/sstaub/Ethernet3
打开以上网址,克隆仓库文件到本地。
2. 新建项目
打开vs code,打开左侧的platform IO插件图标,打开home页。
在home页中,选择新建工程
输入关键字“pico”,然后在下边的列表中选择“Raspberry Pi Pico”,然后在name一栏中输入项目/工程名称:(如:pico_telnet_test)。
等一小会,待工程创建完毕。
3. 添加第三方库Ethernet3到工程中
工程创建完毕后,复制Ethernet3库文件夹至项目文件夹下的lib子文件夹下(在项目列表中,在任一文件上鼠标右键,选择在文件资源管理器中显示)。
添加完成后,可见code中资源管理器该工程下的lib子项下显示Ethernet3目录,说明添加成功。
4. 代码编写
在工程main.cpp中编辑如下代码:
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet3.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 2, 186);
// Enter the IP address of the server you're connecting to:
IPAddress server(180,101,50,188); //www.baidu.com
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 23 is default for telnet;
// if you're using Processing's ChatServer, use port 10002):
EthernetClient client;
void setup() {
// start the Ethernet connection:
Ethernet.setCsPin(17);
Ethernet.setRstPin(20);
Ethernet.begin(mac, ip);
Serial.begin(115200);
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");
// if you get a connection, report back via serial:
if (client.connect(server, 80)) {
Serial.println("connected");
}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
}
void loop() {
/* *********** Telnet client test*********** */
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
char c = client.read();
Serial.print(c);
}
// as long as there are bytes in the serial queue,
// read them and send them out the socket if it's open:
while (Serial.available() > 0) {
char inChar = Serial.read();
if (client.connected()) {
client.print(inChar);
}
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
// do nothing:
while (true);
}
}
说明:
第一步:引用头文件
第二步:定义mac地址(w5500)、IP静态地址、要连接的服务器IP地址(这里使用百度的ip)、
第三步:新建一个EthernetClient类型对象
第四步:在setup初始化函数中设置CS和rst引脚,并调用begin()函数/方法初始化W5500的网络参数。
初始化之后,使用connect函数连接远端服务器的指定端口,并输出连接情况、如果连接成功,输出服务器返回内容。
- 2024-03-02
-
发表了日志:
【得捷Follow me第4期】W5500-EVB-Pico PIO 挂载SD卡并实现文件读写访问
-
发表了日志:
【得捷Follow me第4期】W5500-EVB-Pico PIO UDP收发测试