sky007 发表于 2023-6-7 16:52:28

贪食蛇大作战

本帖最后由 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激光对战小车捣腾起来。






三春牛-创客 发表于 2023-6-9 14:57:44

厉害厉害

三春牛-创客 发表于 2023-6-9 14:59:11

贪吃蛇和MQTT的完美结合!

花生编程 发表于 2023-7-18 21:44:14

很棒的项目!赞!{:6_209:}

花生编程 发表于 2023-7-18 21:45:44

有MQTT好评!
页: [1]
查看完整版本: 贪食蛇大作战