- 2024-03-23
-
加入了学习《得捷Follow me第4期视频》,观看 得捷Follow me第4期视频
-
发表了主题帖:
【得捷电子Follow me第4期】补充--tcp网络显示屏
本帖最后由 eew_UPQWC7 于 2024-3-23 21:53 编辑
原汇总帖【得捷电子Follow me第4期】任务汇总提交 - DigiKey得捷技术专区 - 电子工程世界-论坛 (eeworld.com.cn)中介绍了使用Seeed的GROVE - OLED DISPLAY0.96" 显示屏和M5的ATOMS3U制作tcp协议传输的网络显示屏,当时考虑到 1.Seeed的GROVE - OLED DISPLAY0.96"为GROVE接口恰巧能与M5的ATOMS3U对接省去接线的麻烦;2.刚好趁着任务中学习了TCP网络传输。想着就利用起来。原帖中介绍的有限现在摘出来详细介绍下:
1.接线
因为都是GROVE接口直接对插即可非常方便。
2.ATOMS3U端代码:
需要调用U8g2lib来驱动lcd。
#include <M5AtomS3.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <U8g2lib.h>
// Set the name and password of the wifi to be connected.
// 配置所连接wifi的名称和密码
const char *ssid = "OpenWrt";
const char *password = "01010101";
const uint16_t servPort = 80;
WiFiServer server(servPort);
const char *servIp = "192.168.1.200";
WiFiClient client;
const uint16_t port = 23;
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/G1, /* data=*/G2, /* reset=*/U8X8_PIN_NONE); // All Boards without Reset of the Display
void lcdprint(String str)
{
const u16_t strL = 22;
u8g2.firstPage();
do
{
if (str.length() > strL)
{
u8g2.setFont(u8g2_font_ncenB08_tr);
const int row = str.length() / strL;
for (int i = 0; i < row+1; i++)
{
u8g2.drawStr(0, 10 * (i + 1), str.substring(i * strL, (i + 1) * strL).c_str());
}
}
else
{
u8g2.drawStr(0, 12, str.c_str());
}
} while (u8g2.nextPage());
}
void setup()
{
Serial.begin(115200);
u8g2.begin();
int sum = 0;
M5.begin(); // Init M5AtomS3. 初始化M5AtomS3
WiFi.config(IPAddress(192, 168, 1, 200), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 1, 1));
WiFi.begin(ssid, password);
String msgStr;
msgStr = "Waiting connect to WiFi: " + String(ssid);
printf(msgStr.c_str());
lcdprint(msgStr);
while (WiFi.status() != WL_CONNECTED)
{
printf(".");
lcdprint("...");
delay(1000);
sum += 1;
if (sum == 8)
printf("Conncet failed!\n");
lcdprint("Conncet failed!");
}
char buff[100];
sprintf(buff, "WiFi connected\nIP address: \n %s\n", WiFi.localIP().toString());
lcdprint(buff);
printf(buff); // The serial port outputs the IP address
// of the M5AtomS3. 串口输出M5AtomS3的IP地址
delay(500);
sum = 0;
server.begin();
sprintf(buff, "Server started @%s:%d\n", WiFi.localIP().toString(), servPort);
lcdprint(buff);
}
void loop()
{
WiFiClient client = server.available(); // 尝试建立客户对象
char buff[100];
if (client)
{
sprintf(buff, "%s connected", client.localIP().toString());
lcdprint(buff);
String readBuff; // 读取信息暂存
while (client.connected())
{
if (client.available()) // 如果有可读数据
{
String buff = client.readStringUntil('\r');
lcdprint(buff);
}
}
}
client.stop(); // 结束当前连接:
sprintf(buff, "remote lcd @ %s:%d has no client connected!", servIp,servPort);
lcdprint(buff);
delay(1000);
}
3.测试效果:
1)wifi连接好后即可打开tcp服务端进入待配对状态:
2)在w5500-evb-pico端建立一个tcp客户端用于向tcp显示服务端发送数据:
以任务二UDP传输为例,当w5500-evb-pico接受到udp数据就会转而发送至tcp显示服务端显示,代码如下:
from w5500 import w5500
import time
import board
import busio as io
import digitalio
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
print("Task-2 TCP UDP")
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
#初始化网卡
eth=w5500()
socket.set_interface(eth)
#创建远程LCD TCP socket
lcd_client = socket.socket()
lcd_server_ip='192.168.1.200'
lcd_server_port = 80
lcd_client.connect((lcd_server_ip,lcd_server_port))
print("remote lcd connected !")
def rmLcdPrint(s:str):
lcd_client.send(s+"\n")
#创建UDP socket
server_udp = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Allocate socket for the server
server_port_udp = 5000
server_udp.bind((eth.pretty_ip(eth.ip_address),server_port_udp)) # Bind to IP and Port
print("Udp listening")
def taskUdp():
data, addr = server_udp.recvfrom(50)
if data:
data=data.decode('utf-8')
echoStr=f"get udp data:{data} @ {addr[0]}:{addr[1]}\n"
print(echoStr)
rmLcdPrint(echoStr)
return echoStr
else:
return None
while True:
taskUdp()
3)效果:
tcp显示屏服务端客户端建立连接后:
向w5500-evb-pico发送udp数据。
w5500-evb-pico接受数据发送atomsU显示:
- 2024-02-26
-
发表了主题帖:
【得捷电子Follow me第4期】任务汇总提交
本帖最后由 eew_UPQWC7 于 2024-2-26 01:01 编辑
很幸运能够抽中本次得捷电子Follow me第4期活动的参与名额,也很惭愧这个点才提交任务,还请审核大人海谅。
一、本期任务硬件简介
1.本期主板:
2.本期采购的辅助硬件:
包括:Seeed的GROVE - OLED DISPLAY0.96"(原本想直接W5500-EVB-PICO上使用的,因为没有扩展版,接线不便,计划将其作为远程显示屏使用);
M5家的ATOMS3U(作为远程显示屏的控制板使用刚好有GROVE接口);ESP THREAD BORDER ROUTER/ZIGBEE(作为发送UDP数据的终端使用)。
来个合照:
二、任务实现与展示
1.入门任务:开发环境搭建,BLINK,驱动液晶显示器进行显示(没有则串口HelloWorld)
开发环境:
开发环境选择cpy简单方便,按住boot键插入usb,拖拽下好的固件至u盘即可,这里下载的adafruit-circuitpython-wiznet_w5500_evb_pico-en_US-8.2.9.uf2。
待重启后,打开thony即可轻松连接开发板编辑程序。
任务代码(显示屏接线麻烦,再TCP任务中使用网络远程调用):
import board
import busio as io
import adafruit_ssd1306
import digitalio
import time
#blink and hello world
# bgColor=0
# SCL=board.GP1
# SDA=board.GP0
# i2c = io.I2C(SCL,SDA)
# oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
#
# oled.fill(0)
# oled.text("Hello \nWorld!", 10, 10, not bgColor, font_name='font5x8.bin', size=3)
# oled.show()
print("hellow fm4!")
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
led.value = True
while True:
led.value = not led.value
time.sleep(1)
效果展示:
2.基础任务一:完成主控板W5500初始化(静态IP配置),并能使用局域网电脑ping通,同时W5500可以ping通互联网站点;通过抓包软件(Wireshark、Sniffer等)抓取本地PC的ping报文,展示并分析。
注意需要在lib目录下复制好所需调用的库文件:
核心代码
from w5500 import w5500
import time
import board
import digitalio
print("Task-1 Ping Test")
#通过DHCP自动获取IP
# eth=w5500(is_dhcp=True)
#静态IP
eth=w5500()
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = not led.value
time.sleep(1)
print("Done!")
效果展示:
抓包分析:
通过wireshark可以抓取到ping过程的报文,ping的过程采用ICMP协议,一组报文分为两个,首先是ping请求,然后是被ping端的回应。
3.基础任务二:主控板建立TCPIP或UDP服务器,局域网PC使用TCPIP或UDP客户端进行连接并发送数据,主控板接收到数据后,送液晶屏显示(没有则通过串口打印显示);通过抓包软件抓取交互报文,展示并分析。(TCP和UDP二选一,或者全都操作)
核心代码:
接收UDP数据,并将数据同过TCP传送并显示在ATOMS3U连接的LCD。
主板代码:
from w5500 import w5500
import time
import board
import busio as io
import digitalio
import adafruit_ssd1306
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
import asyncio
print("Task-2 TCP UDP")
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
#初始化网卡
eth=w5500()
socket.set_interface(eth)
#创建远程LCD TCP socket
lcd_client = socket.socket()
lcd_server_ip='192.168.1.200'
lcd_server_port = 80
lcd_client.connect((lcd_server_ip,lcd_server_port))
print("remote lcd connected !")
def rmLcdPrint(s:str):
lcd_client.send(s+"\n")
#创建UDP socket
server_udp = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Allocate socket for the server
server_port_udp = 5000
server_udp.bind((eth.pretty_ip(eth.ip_address),server_port_udp)) # Bind to IP and Port
print("Udp listening")
def taskUdp():
data, addr = server_udp.recvfrom(50)
if data:
data=data.decode('utf-8')
echoStr=f"get udp data:{data} @ {addr[0]}:{addr[1]}\n"
print(echoStr)
rmLcdPrint(echoStr)
return echoStr
else:
return None
while True:
taskUdp()
atoms3U代码:
#include <M5AtomS3.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <U8g2lib.h>
// Set the name and password of the wifi to be connected.
// 配置所连接wifi的名称和密码
const char *ssid = "OpenWrt";
const char *password = "01010101";
const uint16_t servPort = 80;
WiFiServer server(servPort);
const char *servIp = "192.168.1.200";
WiFiClient client;
const uint16_t port = 23;
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/G1, /* data=*/G2, /* reset=*/U8X8_PIN_NONE); // All Boards without Reset of the Display
void lcdprint(String str)
{
const u16_t strL = 22;
u8g2.firstPage();
do
{
if (str.length() > strL)
{
u8g2.setFont(u8g2_font_ncenB08_tr);
const int row = str.length() / strL;
for (int i = 0; i < row+1; i++)
{
u8g2.drawStr(0, 10 * (i + 1), str.substring(i * strL, (i + 1) * strL).c_str());
}
}
else
{
u8g2.drawStr(0, 12, str.c_str());
}
} while (u8g2.nextPage());
}
void setup()
{
Serial.begin(115200);
u8g2.begin();
int sum = 0;
M5.begin(); // Init M5AtomS3. 初始化M5AtomS3
WiFi.config(IPAddress(192, 168, 1, 200), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 1, 1));
WiFi.begin(ssid, password);
String msgStr;
msgStr = "Waiting connect to WiFi: " + String(ssid);
printf(msgStr.c_str());
lcdprint(msgStr);
while (WiFi.status() != WL_CONNECTED)
{
printf(".");
lcdprint("...");
delay(1000);
sum += 1;
if (sum == 8)
printf("Conncet failed!\n");
lcdprint("Conncet failed!");
}
char buff[100];
sprintf(buff, "WiFi connected\nIP address: \n %s\n", WiFi.localIP().toString());
lcdprint(buff);
printf(buff); // The serial port outputs the IP address
// of the M5AtomS3. 串口输出M5AtomS3的IP地址
delay(500);
sum = 0;
server.begin();
sprintf(buff, "Server started @%s:%d\n", WiFi.localIP().toString(), servPort);
lcdprint(buff);
// while (!client.connect(host, port))
// {
// printf(
// "Connection failed.\nWaiting 5 seconds before retrying...\n");
// lcdprintf("Connection failed.\nWaiting 5 seconds before retrying...\n");
// delay(5000);
// sum += 1;
// if (sum > 8)
// {
// printf(
// "Connection failed. Time out!\n");
// lcdprintf("Connection failed. Time out!\n");
// return;
// }
// }
// sprintf(buff,"Connected to server.\n Client:%s:%d\n",client.remoteIP().toString().c_str(),client.remotePort());
// printf(buff);
// lcdprintf(buff);
}
void loop()
{
WiFiClient client = server.available(); // 尝试建立客户对象
char buff[100];
if (client)
{
sprintf(buff, "%s connected", client.localIP().toString());
lcdprint(buff);
String readBuff; // 读取信息暂存
while (client.connected())
{
if (client.available()) // 如果有可读数据
{
String buff = client.readStringUntil('\r');
lcdprint(buff);
}
}
}
client.stop(); // 结束当前连接:
sprintf(buff, "remote lcd @ %s:%d has no client connected!", servIp,servPort);
lcdprint(buff);
delay(1000);
}
另外在ESP THREAD BORDER ROUTER/ZIGBEE循环发送udp数据,代码如下:
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiUdp.h>
const char *ssid = "OpenWrt";
const char *password = "01010101";
const char *host = "192.168.1.255";
const u16_t udpPort= 5000;
char buffer[100];
bool status = false;
bool led_logic = false;
int countNum=0;
WiFiUDP Udp;
void setup() {
Serial.begin(115200);
WiFi.config(IPAddress(192, 168, 1, 150), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0), IPAddress(192, 168, 1, 1));
WiFi.begin(ssid, password);
int sum = 0;
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(1000);
sum += 1;
if (sum == 8)
printf("Conncet failed!\n");
}
Udp.begin(udpPort);
sprintf( buffer,"Udp begined @\n IP address:%s Port:%d\n", WiFi.localIP().toString(),udpPort);
Serial.print(buffer);
}
void loop() {
// int packetSize = Udp.parsePacket();
Udp.beginPacket(host, udpPort); //准备发送数据
char buff[100];
sprintf(buff,"udp data @%s:%d, countNum=%d\n",WiFi.localIP().toString(),udpPort,countNum++);
Udp.print(buff); //复制数据到发送缓存
Serial.println(buff);
Udp.endPacket(); //发送数据
delay(2000);
}
效果展示:
可以看到远程显示屏分别显示了,PC发送至PICO及ESP THREAD BORDER ROUTER/ZIGBEE发送至PICO的UDP数据:
抓包分析:
可以看到UDP通信是不需求回应的,发送过程只有一帧报文。
4.进阶任务:从NTP服务器(注意数据交互格式的解析)同步时间,获取时间送显示屏(串口)显示
核心代码
# SPDX-FileCopyrightText: 2022 Scott Shawcroft for Adafruit Industries
# SPDX-License-Identifier: MIT
"""Print out time based on NTP."""
import rtc
import time
import board
import busio as io
import digitalio
import adafruit_ssd1306
import adafruit_ntp
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
from w5500 import w5500
led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
#初始化屏幕
bgColor=0
SCL=board.GP1
SDA=board.GP0
i2c = io.I2C(SCL,SDA)
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
#初始化网卡
eth=w5500()
socket.set_interface(eth)
#创建socket
# socket= socket.socket() # Allocate socket for the server
ntp = adafruit_ntp.NTP(socket, server="ntp.aliyun.com",tz_offset=8)
rtc.RTC().datetime = ntp.datetime
while True:
datetime=time.localtime()
timeStr=f"{datetime.tm_year}/{datetime.tm_mon}/{datetime.tm_mday}\n{datetime.tm_hour}:{datetime.tm_min}:{datetime.tm_sec}"
# print(timeStr)
oled.fill(0)
oled.text(f'{timeStr}', 5, 5, not bgColor, font_name='font5x8.bin', size=2)
oled.show()
time.sleep(0.2)
led.value = not led.value
效果展示:
5.终极任务二:使用外部存储器,组建简易FTP文件服务器,并能正常上传下载文件。
核心代码:
from ftp_server import ftp
from sys import exit
import time
import board
import busio as io
import digitalio
import adafruit_ssd1306
import adafruit_ntp
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
from w5500 import w5500
#初始化网卡
eth=w5500()
socket.set_interface(eth)
# my_ftp_server = ftp(socket, eth.ip,authlist={'usr1':123,'usr2':'abc'},verbose=True)
my_ftp_server = ftp(socket, eth.ip,path="/ftp/",verbose=True)
# my_ftp_server = ftp(socket, eth.ip,verbose=True)
print(my_ftp_server)
while True: # Customise your condition.
print(f"polling:{my_ftp_server.poll()}")
# my_ftp_server.serve() # This will run forever
效果展示:
cpy环境下ftp服务器的被动模式一直未能成功,但是命令模式可以运行并传输文件,默认会下载至windows用户名文件夹:
三、任务源码
https://download.eeworld.com.cn/detail/eew_UPQWC7/631393
补充内容 (2024-3-2 18:04):
四、对本活动的心得体会
通过本次活动学习了circuit python、树莓派pico的基本使用,了解了tcp/udp网络通信的基本原理和使用方法,收获满满!感谢主办方与赞助方共同提供的活动机会,期待eeword与得捷的下次活动。
-
上传了资料:
Follow me 第4期任务代码