我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
背景
这是我们家的主子可爱粘人
每天不是吃就是睡日子过的好不自在。
但是每当我想进步的时候它就会躺在我键盘上
猫子果然是人类进步的绊脚石!
为了解决这个问题我决定给小猫咪做个下玩具可以让它来挤我键盘的时候让它自己玩去。
分析
小猫咪喜欢什么?
- 盒子 对盒子绝对是真爱有盒必进
- 好奇心 对一些声音很敏感,听到后会比较兴奋要去一探究竟
- 窗外的小鸟那也是真爱要是有小鸟那必须蹲着看
- 它平时还经常玩一些毛绒小球
还有就是它对任何玩具都只有一定的新鲜度,如果比较机械一般几天就不爱玩了 基于分析大致设计了这么一个玩具 在一个盒子里 放上一些毛绒小球,在盒子里开一些孔方便它用爪子伸进来抓取,同时盒子里面需要有一个发声装置比如可以播放小鸟的叫声,这声音可以更换,当它对当前声音不敏感后可以更换些新的声音,同时我们可以监听猫子的爪子伸入然后触发调大音量或者更尖锐的声音模拟出遭遇攻击😄
玩具的名字就叫 BirdBox 吧
设计调研
盒子 先随便找个快递盒子随便开几个口子完成。
毛绒小球 刚好我有之前买的毛绒小球
¸
放在一起
接下去是要开发的部分了 发声装置
我们可以使用一个mcu控制一个小喇叭来完成发声
硬件部分
esp8266 和小喇叭
采用github.com/earlephilho… 库即可播放声音 文档里用NPN三极管即可驱动,我这个带有8002B功放芯片也也没有问题
服务端
准备采用 Nestjs nestjs.com/
MQTT 服务器 emqx github.com/emqx/emqx
手机端
采用微信小程序开发
整体架构
设备端通过MQTT和云端保持通信,App端则通过HTTP和服务端保持通信
原型Demo开发
服务端实现
框架安装参考官方文档吧nestjs.com/
脚手架创建工程
nest new server
选用TypeOrm github.com/typeorm/typ… 作为数据库框架
在 app.module.ts 的 module中配置使用 sqlite 作为数据库,没多多少数据 sqlite 正合适~
TypeOrmModule.forRoot({
type: 'sqlite',
database: 'sqlite.db',
autoLoadEntities: true,
synchronize: true,
})
实现一个简单的curd 这里一个设备为例子
定义数据实体相当于表结构 device.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Device {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
serial: string;
@Column()
desc: string;
}
然后在service 进行 curd 操作, 首先是在构造函数中注入 deviceRespository 然后使用它做相应的操作啦
@Injectable()
export class DeviceService {
constructor(
@InjectRepository(Device) private deviceRespository: Repository<Device>,
) {}
create(createDeviceDto: CreateDeviceDto) {
return this.deviceRespository.save(createDeviceDto);
}
findOne(id: number) {
return this.deviceRespository.findOneBy({ id });
}
update(id: number, updateDeviceDto: UpdateDeviceDto) {
return this.deviceRespository.update(id, updateDeviceDto);
}
remove(id: number) {
// return this.deviceRespository.remove();
}
}
具体还是看官网吧
MQTT服务
基于 emqx 开源版本 github.com/emqx/emqx
直接 docker 部署服务
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:latest
服务端口部署在 1883, 控制台端口在 18083 默认账号 admin 默认密码 public
nestjs 连上 Mqtt 服务
安装mqtt module
npm install nest-mqtt mqtt --save
在 app.module.ts 中配置好mqtt服务
MqttModule.forRoot({
host: 'localhost',
port: 1883,
queue: true,
})
],
监听和发布消息 在构造中注入 mqttService 可以用它完成publish操作,监听则可以使用 @Subscribe 装饰器完成
@Injectable()
export class AppService {
constructor(@Inject(MqttService) private readonly mqttService: MqttService) {}
getHello(): string {
return 'Hello World!';
}
@Subscribe('test')
test(@Payload() payload) {
console.log("subscribe")
console.log(payload)
}
publish() : string {
this.mqttService.publish('play', {play: "on"});
return 'publish'
}
}
至此我们已经可以实现通过发Http请求来触发一个mqtt消息了,也就可以实现通过手机端发送请求来达到控制设备端了
硬件
基于Arduino开发
配网
直接使用 WiFiManager github.com/tzapu/WiFiM… 即可完成配网,不过作为demo原型 直接暂时写死 ssid & password 即可
在setup 函数中
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
MQTT 实现
基于 AsyncMqttClient github.com/marvinroger…
//初始化
void initMqtt(const char* clientId, OnMqttConnect connect, OnMqttMessage onMsg) {
onConnect = connect;
onMessage = onMsg;
mqttClient.setClientId(clientId);
mqttClient.onConnect(_onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onSubscribe(onMqttSubscribe);
mqttClient.onUnsubscribe(onMqttUnsubscribe);
//注册消息回调
mqttClient.onMessage(_onMqttMessage);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, PORT);
mqttClient.setMaxTopicLength(512);
mqttClient.setKeepAlive(60);
mqttClient.connect();
}
// 收到消息
void _onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
Serial.println("Publish received.");
Serial.print(" topic: ");
Serial.println(topic);
Serial.print(" qos: ");
Serial.println(properties.qos);
Serial.print(" dup: ");
Serial.println(properties.dup);
Serial.print(" retain: ");
Serial.println(properties.retain);
Serial.print(" len: ");
Serial.println(len);
Serial.print(" index: ");
Serial.println(index);
Serial.print(" total: ");
Serial.println(total);
onMessage(topic, payload, properties, len, index, total);
}
//使用来注册监听
mqttClient.subscribe(topic)
至此我们就可以让使用通过MQTT来控制 我们的模块啦
播放声音
首先是接线地和电源没什么好说的数据线要和RX接也就是D8边上的那个 如图接好~
根据github.com/earlephilho… 该库文档它不仅支持 SPIFFS 通过文件播放 还支持 http 流播放 这里我们暂时采用文件播放 后续考虑是否要切成用http流播放,直接根据它的样例代码即可实现音乐播放
file = new AudioFileSourceSPIFFS("/bird.mp3");
id3 = new AudioFileSourceID3(file);
id3->RegisterMetadataCB(MDCallback, (void*)"ID3TAG");
out = new AudioOutputI2SNoDAC();
mp3 = new AudioGeneratorMP3();
mp3->begin(id3, out);
bird.mp3文件只要放到工程文件的data文件夹下上传时就会上传了
至此我们已经完成了所有基础功能。
测试
终于到了激动人心的测试环节,测试喵也已经准备好了,由于不能插入视频这里只能看看图片了,效果挺好!成功的引起了两个测试的好奇~
测试视频可以移步B站查看 www.bilibili.com/video/BV1wd…
后续
完善小程序功能可以选择声音列表,可以定时触发等功能
当前只有部分后端片段代码放在这里 github.com/6a209/birdb… 后续会更新上设备端和小程序端相关代码。