793浏览
查看: 793|回复: 4

贪食蛇大作战

[复制链接]
本帖最后由 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会宣布比赛结果,播放图片和音效。

      可以参考以下流程图,进行初步了解。
贪食蛇大作战图1


      如果以上描述还是不够清晰,可以看看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)

      下面,我来介绍一下,这个项目是如何实现的,我将分Beetle ESP32-C3简介、小欧机器人平台简介、MQTT平台简介,PCB平台简介,代码解析这5个部分来说明,希望可以把我的想法描述清楚。

一、Beetle ESP32-C3简介       啥也不用多说,官方资料都在这里https://wiki.dfrobot.com.cn/_SKU_DFR0868_Beetle_ESP32_C3,特别小巧的一个模组,可以做特别小巧的作品。很不幸,由于操作不慎,被我烧了一块,怪心疼的。

贪食蛇大作战图4


贪食蛇大作战图3

二、小欧机器人平台简介

      小欧是个很强大的在线编程平台http://www.ovorobot.com/index.html,我的理解,简单点来说,就是把手机作为单片机,利用手机的摄像头、散光灯、扬声器、显示屏、触摸屏、光线传感器、加速度传感器等等作为编程用的传感器,也可以调用网络图片、声音,也可以调用各种API,直接让小欧来使用,他甚至还是一台服务器,在线编程后可以实现各种你想要的功能。关键是开发者叶老师还对所有相关的内容,建立了知识树(小欧知识树:http://atom.ovorobot.com/index.php)可以方便的检索到要学习的内容和示例代码,而且在不断丰富,非常强大,非常方便,有兴趣可以试一下。

贪食蛇大作战图2


三、MQTT平台简介
      这个平台https://www.emqx.com/用起来还是很方便的。不用关注服务器端的情况,公共账号也能用。只要在程序里嵌入代码就可以。只要有网络,无论在哪里,都可以方便的控制。

micropython这么设置:
贪食蛇大作战图5

小欧图形化这么设置
贪食蛇大作战图6



四、PCB平台简介
      嘉立创EDAhttps://lceda.cn/,每月2张免费包邮优惠券,只要你会画简单的电路图,这个羊毛可以薅。
贪食蛇大作战图7贪食蛇大作战图8

五、代码解析
      
      1、控制器A
  1. '''
  2. 实验名称:小欧MQTT贪食蛇大作战
  3. 版本:v1.0
  4. 日期:2023.6
  5. 改编:sky
  6. 说明:编程实现MQTT通信,小欧宣布贪食蛇大作战开始后,2个控制器分别开始自己的游戏,吃到果实,小欧就播报,若一方超出边界,即为失败,小欧宣布比赛结果
  7. 注意:上传程序时应对应相应的CLIENT_ID,控制器A是sky,控制器B是lucy
  8. 小欧程序地址:http://www.ovorobot.com/blockly/blockly.html#-7La73tLfUyNsZpZcgJJYFnRVDNPWIxYzE3K
  9. '''
  10. import network,time                   #导入网络和时间
  11. from simple import MQTTClient         #导入MQTT板块
  12. from machine import Pin               #导入引脚
  13. import random                         #导入随机数            
  14. from neopixel import NeoPixel         #导入2812彩灯
  15. import _thread
  16. # 定义输出管脚
  17. pin = Pin(3, Pin.OUT)
  18. # 定义灯带参数
  19. pixels = NeoPixel(pin, 45)
  20. #定义输入管脚
  21. UP= Pin(1, Pin.IN, Pin.PULL_UP)
  22. DOWN= Pin(2, Pin.IN, Pin.PULL_UP)
  23. LEFT= Pin(21, Pin.IN, Pin.PULL_UP)
  24. RIGHT= Pin(20, Pin.IN, Pin.PULL_UP)
  25. # 定义蛇的初始位置、长度、方向和颜色
  26. snake_pos = [(2,3), (2,2), (2,1)]           #初始化蛇的位置,头向右【坐标点(y,x),坐标x=0~8,y=0~4】
  27. snake_len = 3                               #初始化蛇的长度,3
  28. snake_direction = 'right'                   #初始化蛇的方向,向右
  29. snake_color = (10,0,0)                      #设定蛇的颜色
  30. # 定义食物的初始位置和颜色
  31. food_pos = (random.randint(0, 4), random.randint(0, 8))            #设定果实的位置
  32. food_color =  (0,10,0)                                             #设定果实的颜色
  33. # 初始清除灯带
  34. pixels.fill((0,0,0))
  35. pixels.write()
  36. #WIFI连接函数
  37. def WIFI_Connect():
  38.     WIFI_LED=Pin(10, Pin.OUT) #初始化WIFI指示灯
  39.     wlan = network.WLAN(network.STA_IF) #STA模式
  40.     wlan.active(True)                   #激活接口
  41.     start_time=time.time()              #记录时间做超时判断
  42.     if not wlan.isconnected():
  43.         print('connecting to network...')
  44.         wlan.connect('XXXXXX', 'YYYYYY') #输入WIFI账号密码
  45.         while not wlan.isconnected():
  46.             #LED闪烁提示
  47.             WIFI_LED.value(1)
  48.             time.sleep_ms(300)
  49.             WIFI_LED.value(0)
  50.             time.sleep_ms(300)
  51.             #超时判断,15秒没连接成功判定为超时
  52.             if time.time()-start_time > 15 :
  53.                 print('WIFI Connected Timeout!')
  54.                 wlan.active(False) #反激活WiFi
  55.                 break
  56.     if wlan.isconnected():
  57.         #LED点亮
  58.         WIFI_LED.value(1)
  59.         #串口打印信息
  60.         print('network information:', wlan.ifconfig())
  61.         return True
  62.     else:
  63.         return False
  64.    
  65. #MQTT发布数据任务
  66. def MQTT_Send(TOPIC2,msg):
  67.     client.publish(TOPIC2, CLIENT_ID+msg)
  68.     print(CLIENT_ID+msg)
  69.    
  70. #接收数据任务
  71. def MQTT_Rev(TOPIC1, msg):
  72.     global START
  73.     print(TOPIC1, msg)
  74.     if msg==b'lucylose':                                             #收到胜利的消息                             
  75.         win()                                             
  76.     if msg==b'start':                                              #收到开始比赛的消息
  77.         START=True
  78.         
  79.             
  80. #执行WIFI连接函数并判断是否已经连接成功
  81. if WIFI_Connect():
  82.     SERVER = 'broker.emqx.io'                     # MQTT服务器地址
  83.     PORT = 1883                                          # MQTT服务器端口
  84.     CLIENT_ID = 'sky'                                   # 客户端ID
  85.     TOPIC1 = 'ovosend'                               # 小欧发送消息TOPIC名称
  86.     TOPIC2 = 'ovoreceive'                            # 小欧接收消息TOPIC名称
  87.     client = MQTTClient(CLIENT_ID, SERVER, PORT)  # 实例化客户端
  88.     client.set_callback(MQTT_Rev)                 #配置回调函数,将反馈
  89.     client.connect()                                        #链接网络
  90.     client.subscribe(TOPIC1)                          #订阅主题
  91.                           
  92.    
  93. #定义失败灯效,并发送失败消息
  94. def lose():
  95.     pixels.fill((10,10,10))
  96.     pixels.write()
  97.     MQTT_Send(TOPIC2,'lose')
  98. #定义胜利灯效
  99. def win():
  100.     pixels.fill((10,0,0))
  101.     pixels.write()
  102. START=False
  103. #贪食蛇主程序
  104. while True:
  105.    
  106.       client.check_msg()                                               #调用MQTT_Send()函数,接收信息,并返回msg的值
  107.       
  108.       while START:
  109.             # 绘制蛇和食物
  110.             pixels.fill((0,0,0))
  111.             pixels.write()
  112.             for pos in snake_pos:                                    #snake_pos蛇的位置
  113.                 pixels[pos[0]*9+pos[1]] = snake_color
  114.             pixels[food_pos[0]*9+food_pos[1]] = food_color           #food_pos食物的位置
  115.             pixels.write()
  116.             # 检测按键操作
  117.             if snake_direction == 'right':
  118.                 next_pos = (snake_pos[0][0], snake_pos[0][1]+1)          #最右边的点,y不变,x+1
  119.             elif snake_direction == 'left':
  120.                 next_pos = (snake_pos[0][0], snake_pos[0][1]-1)          #最右边的点,y不变,x-1
  121.             elif snake_direction == 'up':
  122.                 next_pos = (snake_pos[0][0]-1, snake_pos[0][1])          #最右边的点,y-1,x不变
  123.             elif snake_direction == 'down':
  124.                 next_pos = (snake_pos[0][0]+1, snake_pos[0][1])          #最右边的点,y+1,x不变
  125.                
  126.             #按键操作后,碰到边沿
  127.             if next_pos[0]<0 or next_pos[0]>4 or next_pos[1]<0 or next_pos[1]>8 or next_pos in snake_pos:
  128.                 #添加生成音效
  129.                 lose()
  130.                 START=False
  131.                 continue
  132.             
  133.             #按键操作后,未碰到边沿
  134.             snake_pos.insert(0, next_pos)                                            #插入蛇的新坐标next_pos,位置插在index=0的前面
  135.             if next_pos == food_pos:
  136.                 MQTT_Send(TOPIC2,'ate')                                           #发送控制器CLIENT_ID吃到果实的消息
  137.                
  138.                 snake_len += 1                                                       #吃到了食物,蛇长度加一,重新生成食物               
  139.                 food_pos = (random.randint(0, 4), random.randint(0, 8))              #随机生成新的果实   
  140.             else:
  141.                 snake_pos.pop()                                                      #没有吃到果实,蛇的身体就把最后一个元素删除。刚才在头部增加一个元素,现在减少一个元素,这样就实现了移动的效果。
  142.             time.sleep(0.5)
  143.             # 检测按键操作
  144.             if LEFT.value()==0:
  145.                 snake_direction = 'left'
  146.                 time.sleep(0.3)
  147.             elif RIGHT.value()==0:
  148.                 snake_direction = 'right'
  149.                 time.sleep(0.3)
  150.             elif UP.value()==0:
  151.                 snake_direction = 'up'
  152.                 time.sleep(0.3)
  153.             elif DOWN.value()==0:
  154.                 snake_direction = 'down'
  155.                 time.sleep(0.3)
  156.             print(snake_direction)
  157.    
复制代码


    2、控制器B
  1. '''
  2. 实验名称:小欧MQTT贪食蛇大作战
  3. 版本:v1.0
  4. 日期:2023.6
  5. 改编:sky
  6. 说明:编程实现MQTT通信,小欧宣布贪食蛇大作战开始后,2个控制器分别开始自己的游戏,吃到果实,小欧就播报,若一方超出边界,即为失败,小欧宣布比赛结果
  7. 注意:上传程序时应对应相应的CLIENT_ID,控制器A是sky,控制器B是lucy
  8. 小欧程序地址:http://www.ovorobot.com/blockly/blockly.html#-7La73tLfUyNsZpZcgJJYFnRVDNPWIxYzE3K
  9. '''
  10. import network,time                   #导入网络和时间
  11. from simple import MQTTClient         #导入MQTT板块
  12. from machine import Pin               #导入引脚
  13. import random                         #导入随机数            
  14. from neopixel import NeoPixel         #导入2812彩灯
  15. import _thread
  16. # 定义输出管脚
  17. pin = Pin(3, Pin.OUT)
  18. # 定义灯带参数
  19. pixels = NeoPixel(pin, 45)
  20. #定义输入管脚
  21. UP= Pin(1, Pin.IN, Pin.PULL_UP)
  22. DOWN= Pin(2, Pin.IN, Pin.PULL_UP)
  23. LEFT= Pin(21, Pin.IN, Pin.PULL_UP)
  24. RIGHT= Pin(20, Pin.IN, Pin.PULL_UP)
  25. # 定义蛇的初始位置、长度、方向和颜色
  26. snake_pos = [(2,3), (2,2), (2,1)]           #初始化蛇的位置,头向右【坐标点(y,x),坐标x=0~8,y=0~4】
  27. snake_len = 3                               #初始化蛇的长度,3
  28. snake_direction = 'right'                   #初始化蛇的方向,向右
  29. snake_color = (10,0,0)                      #设定蛇的颜色
  30. # 定义食物的初始位置和颜色
  31. food_pos = (random.randint(0, 4), random.randint(0, 8))            #设定果实的位置
  32. food_color =  (0,10,0)                                             #设定果实的颜色
  33. # 初始清除灯带
  34. pixels.fill((0,0,0))
  35. pixels.write()
  36. #WIFI连接函数
  37. def WIFI_Connect():
  38.     WIFI_LED=Pin(10, Pin.OUT) #初始化WIFI指示灯
  39.     wlan = network.WLAN(network.STA_IF) #STA模式
  40.     wlan.active(True)                   #激活接口
  41.     start_time=time.time()              #记录时间做超时判断
  42.     if not wlan.isconnected():
  43.         print('connecting to network...')
  44.         wlan.connect('XXXXXX', 'YYYYYY') #输入WIFI账号密码
  45.         while not wlan.isconnected():
  46.             #LED闪烁提示
  47.             WIFI_LED.value(1)
  48.             time.sleep_ms(300)
  49.             WIFI_LED.value(0)
  50.             time.sleep_ms(300)
  51.             #超时判断,15秒没连接成功判定为超时
  52.             if time.time()-start_time > 15 :
  53.                 print('WIFI Connected Timeout!')
  54.                 wlan.active(False) #反激活WiFi
  55.                 break
  56.     if wlan.isconnected():
  57.         #LED点亮
  58.         WIFI_LED.value(1)
  59.         #串口打印信息
  60.         print('network information:', wlan.ifconfig())
  61.         return True
  62.     else:
  63.         return False
  64.    
  65. #MQTT发布数据任务
  66. def MQTT_Send(TOPIC2,msg):
  67.     client.publish(TOPIC2, CLIENT_ID+msg)
  68.     print(CLIENT_ID+msg)
  69.    
  70. #接收数据任务
  71. def MQTT_Rev(TOPIC1, msg):
  72.     global START
  73.     print(TOPIC1, msg)
  74.     if msg==b'skylose':                                             #收到胜利的消息                             
  75.         win()                                             
  76.     if msg==b'start':                                              #收到开始比赛的消息
  77.         START=True
  78.         
  79.             
  80. #执行WIFI连接函数并判断是否已经连接成功
  81. if WIFI_Connect():
  82.     # 设置播放音效
  83.     SERVER = 'broker.emqx.io'                     # MQTT服务器地址
  84.     PORT = 1883                                   # MQTT服务器端口
  85.     CLIENT_ID = 'lucy'                             # 客户端ID
  86.     TOPIC1 = 'ovosend'                           # 小欧发送消息TOPIC名称
  87.     TOPIC2 = 'ovoreceive'                         # 小欧接收消息TOPIC名称
  88.     client = MQTTClient(CLIENT_ID, SERVER, PORT)  # 实例化客户端
  89.     client.set_callback(MQTT_Rev)                 #配置回调函数,将反馈
  90.     client.connect()   
  91.     client.subscribe(TOPIC1)                      #订阅主题
  92.                           
  93.    
  94. #定义失败灯效,并发送失败消息
  95. def lose():
  96.     pixels.fill((10,10,10))
  97.     pixels.write()
  98.     MQTT_Send(TOPIC2,'lose')
  99. #定义胜利灯效
  100. def win():
  101.     pixels.fill((10,0,0))
  102.     pixels.write()
  103. START=False
  104. #贪食蛇主程序
  105. while True:
  106.    
  107.       client.check_msg()                                               #调用MQTT_Send()函数,接收信息,并返回msg的值
  108.       
  109.       while START:
  110.             # 绘制蛇和食物
  111.             pixels.fill((0,0,0))
  112.             pixels.write()
  113.             for pos in snake_pos:                                    #snake_pos蛇的位置
  114.                 pixels[pos[0]*9+pos[1]] = snake_color
  115.             pixels[food_pos[0]*9+food_pos[1]] = food_color           #food_pos食物的位置
  116.             pixels.write()
  117.             # 检测按键操作
  118.             if snake_direction == 'right':
  119.                 next_pos = (snake_pos[0][0], snake_pos[0][1]+1)          #最右边的点,y不变,x+1
  120.             elif snake_direction == 'left':
  121.                 next_pos = (snake_pos[0][0], snake_pos[0][1]-1)          #最右边的点,y不变,x-1
  122.             elif snake_direction == 'up':
  123.                 next_pos = (snake_pos[0][0]-1, snake_pos[0][1])          #最右边的点,y-1,x不变
  124.             elif snake_direction == 'down':
  125.                 next_pos = (snake_pos[0][0]+1, snake_pos[0][1])          #最右边的点,y+1,x不变
  126.                
  127.             #按键操作后,碰到边沿
  128.             if next_pos[0]<0 or next_pos[0]>4 or next_pos[1]<0 or next_pos[1]>8 or next_pos in snake_pos:
  129.                 #添加生成音效
  130.                 lose()
  131.                 START=False
  132.                 continue
  133.             
  134.             #按键操作后,未碰到边沿
  135.             snake_pos.insert(0, next_pos)                                            #插入蛇的新坐标next_pos,位置插在index=0的前面
  136.             if next_pos == food_pos:
  137.                 MQTT_Send(TOPIC2,'ate')                                           #发送控制器CLIENT_ID吃到果实的消息
  138.                
  139.                 snake_len += 1                                                       #吃到了食物,蛇长度加一,重新生成食物               
  140.                 food_pos = (random.randint(0, 4), random.randint(0, 8))              #随机生成新的果实   
  141.             else:
  142.                 snake_pos.pop()                                                      #没有吃到果实,蛇的身体就把最后一个元素删除。刚才在头部增加一个元素,现在减少一个元素,这样就实现了移动的效果。
  143.             time.sleep(0.5)
  144.             # 检测按键操作
  145.             if LEFT.value()==0:
  146.                 snake_direction = 'left'
  147.                 time.sleep(0.3)
  148.             elif RIGHT.value()==0:
  149.                 snake_direction = 'right'
  150.                 time.sleep(0.3)
  151.             elif UP.value()==0:
  152.                 snake_direction = 'up'
  153.                 time.sleep(0.3)
  154.             elif DOWN.value()==0:
  155.                 snake_direction = 'down'
  156.                 time.sleep(0.3)
  157.             print(snake_direction)
  158.    
复制代码


      3、小欧机器人
  1. 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

很棒的项目!赞!
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-7-18 21:45:44

有MQTT好评!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail