贪食蛇大作战
本帖最后由 sky007 于 2023-6-8 22:44 编辑贪食蛇大作战
很高兴参加Beetle ESP32-C3试用活动,这个小项目用到了DF的C3模组以及自己打了2812灯板PCB,与小欧机器人平台(http://www.ovorobot.com)结合,利用MQTT平台(www.emqx.com),实现了贪食蛇蛇对战的场景。之前,是考虑做激光对战小车的平台,但后来想,还是先做这个2812灯板的小项目来验证一下,一方面可以利用一下之前让ChartGPT写的贪食蛇程序,另一方面,也积累一下MQTT通信的学习。
这个项目的总体思路是:两个控制器的C3模运行并订阅MQTT主题,小欧机器人的APP作为裁判员,向MQTT主题发起游戏开启指令,控制器收到指令后分别进行各自的贪食蛇游戏,当控制器吃到果实后,会告诉小欧机器人APP,它会播报实况信息。如果有一方碰到边沿后,游戏失败,控制器会发消息到MQTT主题,小欧机器人APP会宣布比赛结果,播放图片和音效。
可以参考以下流程图,进行初步了解。
如果以上描述还是不够清晰,可以看看B站的这个视频——贪食蛇大作战
https://www.bilibili.com/video/BV1Ks4y1v71o?buvid=XX25F532A443BEBBE5990166D0CFF4DA69DA3&is_story_h5=false&mid=lJ9bKaN2CiRRy3cz%2BxrEKg%3D%3D&plat_id=240&share_from=ugc&share_medium=android&share_plat=android&share_source=WEIXIN&share_tag=s_i×tamp=1686188472&unique_k=xJq7RHr&up_id=142708284
(https://www.bilibili.com/video/BV1Ks4y1v71o?buvid=XX25F532A443BEBBE5990166D0CFF4DA69DA3&is_story_h5=false&mid=lJ9bKaN2CiRRy3cz%2BxrEKg%3D%3D&plat_id=240&share_from=ugc&share_medium=android&share_plat=android&share_source=WEIXIN&share_tag=s_i×tamp=1686188472&unique_k=xJq7RHr&up_id=142708284)
下面,我来介绍一下,这个项目是如何实现的,我将分Beetle ESP32-C3简介、小欧机器人平台简介、MQTT平台简介,PCB平台简介,代码解析这5个部分来说明,希望可以把我的想法描述清楚。
一、Beetle ESP32-C3简介 啥也不用多说,官方资料都在这里https://wiki.dfrobot.com.cn/_SKU_DFR0868_Beetle_ESP32_C3,特别小巧的一个模组,可以做特别小巧的作品。很不幸,由于操作不慎,被我烧了一块,怪心疼的。
二、小欧机器人平台简介
小欧是个很强大的在线编程平台http://www.ovorobot.com/index.html,我的理解,简单点来说,就是把手机作为单片机,利用手机的摄像头、散光灯、扬声器、显示屏、触摸屏、光线传感器、加速度传感器等等作为编程用的传感器,也可以调用网络图片、声音,也可以调用各种API,直接让小欧来使用,他甚至还是一台服务器,在线编程后可以实现各种你想要的功能。关键是开发者叶老师还对所有相关的内容,建立了知识树(小欧知识树:http://atom.ovorobot.com/index.php)可以方便的检索到要学习的内容和示例代码,而且在不断丰富,非常强大,非常方便,有兴趣可以试一下。
三、MQTT平台简介
这个平台https://www.emqx.com/用起来还是很方便的。不用关注服务器端的情况,公共账号也能用。只要在程序里嵌入代码就可以。只要有网络,无论在哪里,都可以方便的控制。
micropython这么设置:
小欧图形化这么设置
四、PCB平台简介
嘉立创EDAhttps://lceda.cn/,每月2张免费包邮优惠券,只要你会画简单的电路图,这个羊毛可以薅。
五、代码解析
1、控制器A
'''
实验名称:小欧MQTT贪食蛇大作战
版本:v1.0
日期:2023.6
改编:sky
说明:编程实现MQTT通信,小欧宣布贪食蛇大作战开始后,2个控制器分别开始自己的游戏,吃到果实,小欧就播报,若一方超出边界,即为失败,小欧宣布比赛结果
注意:上传程序时应对应相应的CLIENT_ID,控制器A是sky,控制器B是lucy
小欧程序地址:http://www.ovorobot.com/blockly/blockly.html#-7La73tLfUyNsZpZcgJJYFnRVDNPWIxYzE3K
'''
import network,time #导入网络和时间
from simple import MQTTClient #导入MQTT板块
from machine import Pin #导入引脚
import random #导入随机数
from neopixel import NeoPixel #导入2812彩灯
import _thread
# 定义输出管脚
pin = Pin(3, Pin.OUT)
# 定义灯带参数
pixels = NeoPixel(pin, 45)
#定义输入管脚
UP= Pin(1, Pin.IN, Pin.PULL_UP)
DOWN= Pin(2, Pin.IN, Pin.PULL_UP)
LEFT= Pin(21, Pin.IN, Pin.PULL_UP)
RIGHT= Pin(20, Pin.IN, Pin.PULL_UP)
# 定义蛇的初始位置、长度、方向和颜色
snake_pos = [(2,3), (2,2), (2,1)] #初始化蛇的位置,头向右【坐标点(y,x),坐标x=0~8,y=0~4】
snake_len = 3 #初始化蛇的长度,3
snake_direction = 'right' #初始化蛇的方向,向右
snake_color = (10,0,0) #设定蛇的颜色
# 定义食物的初始位置和颜色
food_pos = (random.randint(0, 4), random.randint(0, 8)) #设定果实的位置
food_color =(0,10,0) #设定果实的颜色
# 初始清除灯带
pixels.fill((0,0,0))
pixels.write()
#WIFI连接函数
def WIFI_Connect():
WIFI_LED=Pin(10, Pin.OUT) #初始化WIFI指示灯
wlan = network.WLAN(network.STA_IF) #STA模式
wlan.active(True) #激活接口
start_time=time.time() #记录时间做超时判断
if not wlan.isconnected():
print('connecting to network...')
wlan.connect('XXXXXX', 'YYYYYY') #输入WIFI账号密码
while not wlan.isconnected():
#LED闪烁提示
WIFI_LED.value(1)
time.sleep_ms(300)
WIFI_LED.value(0)
time.sleep_ms(300)
#超时判断,15秒没连接成功判定为超时
if time.time()-start_time > 15 :
print('WIFI Connected Timeout!')
wlan.active(False) #反激活WiFi
break
if wlan.isconnected():
#LED点亮
WIFI_LED.value(1)
#串口打印信息
print('network information:', wlan.ifconfig())
return True
else:
return False
#MQTT发布数据任务
def MQTT_Send(TOPIC2,msg):
client.publish(TOPIC2, CLIENT_ID+msg)
print(CLIENT_ID+msg)
#接收数据任务
def MQTT_Rev(TOPIC1, msg):
global START
print(TOPIC1, msg)
if msg==b'lucylose': #收到胜利的消息
win()
if msg==b'start': #收到开始比赛的消息
START=True
#执行WIFI连接函数并判断是否已经连接成功
if WIFI_Connect():
SERVER = 'broker.emqx.io' # MQTT服务器地址
PORT = 1883 # MQTT服务器端口
CLIENT_ID = 'sky' # 客户端ID
TOPIC1 = 'ovosend' # 小欧发送消息TOPIC名称
TOPIC2 = 'ovoreceive' # 小欧接收消息TOPIC名称
client = MQTTClient(CLIENT_ID, SERVER, PORT)# 实例化客户端
client.set_callback(MQTT_Rev) #配置回调函数,将反馈
client.connect() #链接网络
client.subscribe(TOPIC1) #订阅主题
#定义失败灯效,并发送失败消息
def lose():
pixels.fill((10,10,10))
pixels.write()
MQTT_Send(TOPIC2,'lose')
#定义胜利灯效
def win():
pixels.fill((10,0,0))
pixels.write()
START=False
#贪食蛇主程序
while True:
client.check_msg() #调用MQTT_Send()函数,接收信息,并返回msg的值
while START:
# 绘制蛇和食物
pixels.fill((0,0,0))
pixels.write()
for pos in snake_pos: #snake_pos蛇的位置
pixels*9+pos] = snake_color
pixels*9+food_pos] = food_color #food_pos食物的位置
pixels.write()
# 检测按键操作
if snake_direction == 'right':
next_pos = (snake_pos, snake_pos+1) #最右边的点,y不变,x+1
elif snake_direction == 'left':
next_pos = (snake_pos, snake_pos-1) #最右边的点,y不变,x-1
elif snake_direction == 'up':
next_pos = (snake_pos-1, snake_pos) #最右边的点,y-1,x不变
elif snake_direction == 'down':
next_pos = (snake_pos+1, snake_pos) #最右边的点,y+1,x不变
#按键操作后,碰到边沿
if next_pos<0 or next_pos>4 or next_pos<0 or next_pos>8 or next_pos in snake_pos:
#添加生成音效
lose()
START=False
continue
#按键操作后,未碰到边沿
snake_pos.insert(0, next_pos) #插入蛇的新坐标next_pos,位置插在index=0的前面
if next_pos == food_pos:
MQTT_Send(TOPIC2,'ate') #发送控制器CLIENT_ID吃到果实的消息
snake_len += 1 #吃到了食物,蛇长度加一,重新生成食物
food_pos = (random.randint(0, 4), random.randint(0, 8)) #随机生成新的果实
else:
snake_pos.pop() #没有吃到果实,蛇的身体就把最后一个元素删除。刚才在头部增加一个元素,现在减少一个元素,这样就实现了移动的效果。
time.sleep(0.5)
# 检测按键操作
if LEFT.value()==0:
snake_direction = 'left'
time.sleep(0.3)
elif RIGHT.value()==0:
snake_direction = 'right'
time.sleep(0.3)
elif UP.value()==0:
snake_direction = 'up'
time.sleep(0.3)
elif DOWN.value()==0:
snake_direction = 'down'
time.sleep(0.3)
print(snake_direction)
2、控制器B
'''
实验名称:小欧MQTT贪食蛇大作战
版本:v1.0
日期:2023.6
改编:sky
说明:编程实现MQTT通信,小欧宣布贪食蛇大作战开始后,2个控制器分别开始自己的游戏,吃到果实,小欧就播报,若一方超出边界,即为失败,小欧宣布比赛结果
注意:上传程序时应对应相应的CLIENT_ID,控制器A是sky,控制器B是lucy
小欧程序地址:http://www.ovorobot.com/blockly/blockly.html#-7La73tLfUyNsZpZcgJJYFnRVDNPWIxYzE3K
'''
import network,time #导入网络和时间
from simple import MQTTClient #导入MQTT板块
from machine import Pin #导入引脚
import random #导入随机数
from neopixel import NeoPixel #导入2812彩灯
import _thread
# 定义输出管脚
pin = Pin(3, Pin.OUT)
# 定义灯带参数
pixels = NeoPixel(pin, 45)
#定义输入管脚
UP= Pin(1, Pin.IN, Pin.PULL_UP)
DOWN= Pin(2, Pin.IN, Pin.PULL_UP)
LEFT= Pin(21, Pin.IN, Pin.PULL_UP)
RIGHT= Pin(20, Pin.IN, Pin.PULL_UP)
# 定义蛇的初始位置、长度、方向和颜色
snake_pos = [(2,3), (2,2), (2,1)] #初始化蛇的位置,头向右【坐标点(y,x),坐标x=0~8,y=0~4】
snake_len = 3 #初始化蛇的长度,3
snake_direction = 'right' #初始化蛇的方向,向右
snake_color = (10,0,0) #设定蛇的颜色
# 定义食物的初始位置和颜色
food_pos = (random.randint(0, 4), random.randint(0, 8)) #设定果实的位置
food_color =(0,10,0) #设定果实的颜色
# 初始清除灯带
pixels.fill((0,0,0))
pixels.write()
#WIFI连接函数
def WIFI_Connect():
WIFI_LED=Pin(10, Pin.OUT) #初始化WIFI指示灯
wlan = network.WLAN(network.STA_IF) #STA模式
wlan.active(True) #激活接口
start_time=time.time() #记录时间做超时判断
if not wlan.isconnected():
print('connecting to network...')
wlan.connect('XXXXXX', 'YYYYYY') #输入WIFI账号密码
while not wlan.isconnected():
#LED闪烁提示
WIFI_LED.value(1)
time.sleep_ms(300)
WIFI_LED.value(0)
time.sleep_ms(300)
#超时判断,15秒没连接成功判定为超时
if time.time()-start_time > 15 :
print('WIFI Connected Timeout!')
wlan.active(False) #反激活WiFi
break
if wlan.isconnected():
#LED点亮
WIFI_LED.value(1)
#串口打印信息
print('network information:', wlan.ifconfig())
return True
else:
return False
#MQTT发布数据任务
def MQTT_Send(TOPIC2,msg):
client.publish(TOPIC2, CLIENT_ID+msg)
print(CLIENT_ID+msg)
#接收数据任务
def MQTT_Rev(TOPIC1, msg):
global START
print(TOPIC1, msg)
if msg==b'skylose': #收到胜利的消息
win()
if msg==b'start': #收到开始比赛的消息
START=True
#执行WIFI连接函数并判断是否已经连接成功
if WIFI_Connect():
# 设置播放音效
SERVER = 'broker.emqx.io' # MQTT服务器地址
PORT = 1883 # MQTT服务器端口
CLIENT_ID = 'lucy' # 客户端ID
TOPIC1 = 'ovosend' # 小欧发送消息TOPIC名称
TOPIC2 = 'ovoreceive' # 小欧接收消息TOPIC名称
client = MQTTClient(CLIENT_ID, SERVER, PORT)# 实例化客户端
client.set_callback(MQTT_Rev) #配置回调函数,将反馈
client.connect()
client.subscribe(TOPIC1) #订阅主题
#定义失败灯效,并发送失败消息
def lose():
pixels.fill((10,10,10))
pixels.write()
MQTT_Send(TOPIC2,'lose')
#定义胜利灯效
def win():
pixels.fill((10,0,0))
pixels.write()
START=False
#贪食蛇主程序
while True:
client.check_msg() #调用MQTT_Send()函数,接收信息,并返回msg的值
while START:
# 绘制蛇和食物
pixels.fill((0,0,0))
pixels.write()
for pos in snake_pos: #snake_pos蛇的位置
pixels*9+pos] = snake_color
pixels*9+food_pos] = food_color #food_pos食物的位置
pixels.write()
# 检测按键操作
if snake_direction == 'right':
next_pos = (snake_pos, snake_pos+1) #最右边的点,y不变,x+1
elif snake_direction == 'left':
next_pos = (snake_pos, snake_pos-1) #最右边的点,y不变,x-1
elif snake_direction == 'up':
next_pos = (snake_pos-1, snake_pos) #最右边的点,y-1,x不变
elif snake_direction == 'down':
next_pos = (snake_pos+1, snake_pos) #最右边的点,y+1,x不变
#按键操作后,碰到边沿
if next_pos<0 or next_pos>4 or next_pos<0 or next_pos>8 or next_pos in snake_pos:
#添加生成音效
lose()
START=False
continue
#按键操作后,未碰到边沿
snake_pos.insert(0, next_pos) #插入蛇的新坐标next_pos,位置插在index=0的前面
if next_pos == food_pos:
MQTT_Send(TOPIC2,'ate') #发送控制器CLIENT_ID吃到果实的消息
snake_len += 1 #吃到了食物,蛇长度加一,重新生成食物
food_pos = (random.randint(0, 4), random.randint(0, 8)) #随机生成新的果实
else:
snake_pos.pop() #没有吃到果实,蛇的身体就把最后一个元素删除。刚才在头部增加一个元素,现在减少一个元素,这样就实现了移动的效果。
time.sleep(0.5)
# 检测按键操作
if LEFT.value()==0:
snake_direction = 'left'
time.sleep(0.3)
elif RIGHT.value()==0:
snake_direction = 'right'
time.sleep(0.3)
elif UP.value()==0:
snake_direction = 'up'
time.sleep(0.3)
elif DOWN.value()==0:
snake_direction = 'down'
time.sleep(0.3)
print(snake_direction)
3、小欧机器人
http://www.ovorobot.com/blockly/blockly.html#-lWuq8T4AVcrqW50Mtycgp2jFnvrgUEmJk8g
目前,这个项目还没有很完善,PCB还没最终完成,无源蜂鸣器需要加上去,那样联网成功、吃到果实都可以在本地发出提示音,后面再完善。另外,PCB是裸露的,需要打印个外壳,把板子包裹起来,手感会好很多。还有,利用这个控制器,还可以做其他的小项目,比如小欧MIDI,来作为乐器也是不错的,还可以做一个猫抓老鼠的互动游戏,好像也挺好玩……
但是,先告一段落,以后再折腾吧,先得把MQTT激光对战小车捣腾起来。
厉害厉害 贪吃蛇和MQTT的完美结合! 很棒的项目!赞!{:6_209:} 有MQTT好评!
页:
[1]