目录
CatYou,一款针对宠物照顾问题的物联网解决方案。有助于主人不在家无法远程喂养猫的困扰,而且可以及时收集猫的身体状况数据,经过专业社区内宠物医生的分析以便给主人更好的宠物健康报告,让养宠更简单、更舒心。
问题陈述
爱猫人士可能经历的最糟糕的事情之一就是失去他们心爱的猫。
当我们考虑猫的疾病和死亡时,一个重要的事情是猫非常善于隐藏自己的疾病作为生存措施,这让猫在任何人意识到之前就生病了很长时间。对于那些白天和猫在一起并且没有注意到体重减轻、脱落、睡得更多或毛发暗淡等细微变化的人来说尤其如此。随着我们的猫变老,我们可能认为体重减轻、活动减少和/或嗜睡等症状是由于它们随着年龄的增长而减慢,而不是由于疾病。
此外,饮食失调也可能对猫的健康产生不良影响。宠物猫的肥胖是一个日益严重的问题。宠物猫的肥胖不仅会影响它们的外表和动物福利,而且额外的体重和多余的脂肪也会导致许多健康风险,如肝病、糖尿病、呼吸障碍、体力消耗引起的疼痛,甚至缩短宠物的寿命。而且,近年来家庭宠物猫的饲养越来越精细,主人也越来越意识到猫的饮食习惯和体重之间的关系。另一种饮食失调,即食欲不振,可能来自药物、糖尿病、肾脏疾病、胰腺炎、肠胃问题、免疫疾病、癌症、接触有毒物质、压力或环境或食物的变化等。
猫迫切需要有健康监测来追踪它们的雕像,以确保它们的安全。
市场同类产品对比与分析
这些年来,人们发明了猫喂食器以拯救无法每天在家喂猫的主人。 上料机实际上主要有3种,包括自动重力机、自动上料机和智能上料机。 但他们到目前为止做了什么?
虽然重力装置很容易添加猫粮,但食物会不断落入托盘供猫吃,这会导致肥胖。 暴露出来的食物也会发霉,吃起来不健康。 另一件事是它没有准确记录猫吃的食物。
自动机器比以前的机器工作得更好,因为它可以固定数量和固定时间喂猫,但是它不会提供其他功能来满足铲屎官额外关于检测的需求。
我们的CatYou完美的解决这些问题,比如记录摄入量,允许远程控制,而且我们的解决方案还将涵盖其他独特的健康检测功能!

主人在工作的同时,难免会关心家中宠物的安全和健康,每天给猫咪量体温和体重很麻烦。
所以一些宠物主人经常无法告诉猫的健康状况,以及它是否过度喂食,喂食不足甚至感冒,直到为时已晚,因为宠物无法为自己说话。 但在目前的状态下,检测猫的健康状态几乎没有物联网解决方案。
考虑到所有这些因素,我们决定构建一个物联网设备来“拯救”这些“留守”的猫。
针对用户
该产品的针对用户是猫主人。
正如我们之前提到的,随着社会和工作压力的增加,越来越多的独居年轻人选择养宠物陪伴,这大大缓解了他们的孤独感。
他们需要我们的设备的原因是他们关心被遗忘的猫的健康。
使用我们的物联网设备,猫主人可以每天获取温度和体重数据。 此外,喂食过程是远程控制的并记录准确的食物摄入量,供业主参考。 然后可以参考这些记录来设置未来的喂食量。
只要产品定价合理,猫主人都会欣然接受。
解决方案
验收标准
- 系统必须能够检测到猫在 50 厘米内的接近
- 系统必须能够在 5 秒内测量 10 厘米距离内的猫的温度
- 系统必须能够在 10 秒内测量出猫的重量
- 供料系统必须能够在用户操作后 10 秒内做出反应
成功目标
- 该产品上线后1个月内必须能够获得10,000+新用户
- 使用社区必须有 1,000+ 日活跃用户
- 预计每季末用户群增加 3% – 5%
- 自动生成的报告的平均准确率必须至少为 70%
前提假设
- 宠物饲养员将携带他们的手机并保持互联网连接
- 系统不会断电
- 红外温度传感器能够在 5 秒内给出温度读数
- 红外传感器能够以灵敏的水平检测移动
- 压力传感器与数据转换模块连接良好,可传输重量读数
约束条件
- 物联网原型的总成本不能超过 300 SGD(约合1400RMB)
- 远程喂猫护理系统不会影响猫的正常生活
- 整个系统应该能够使用电池运行至少 24 小时
- 安装屋内所有设备所需的电源插头数不能超过 5 个
技术依赖
- 必须先建立数据库,然后才能将传感器数据传输到后端
- 在获取传感器数据并将其发送到后端之前,必须设置物联网网关并连接到 Wi-Fi
- 应开发微信小程序并建立用户社区,然后用户才能在该平台共享报告
系统架构

在设备端,我们有红外运动传感器来检测猫的接近,红外温度传感器来测量猫的温度,压力传感器来测量猫的重量,喂食伺服来控制喂食过程和喂食量。 所有这些传感器都通过电线连接到 Arduino 主芯片。 Arduino UNO 就像一个中央处理器来监控所有传感器。 之后,连接 Arduino 的 Wi-Fi 模块会将采集到的数据上传到腾讯云。 处理后数据会发送到我们的前端微信小程序。 对于MVP,我们只有微信小程序平台,可以简单的报告猫的温度和体重读数。 对于成熟的商业产品,我们会加入一些高级功能,比如报表自动分析、报表可视化和用户社区。
模块选择
主芯片选择
在我们的项目中,我们选择了 Arduino UNO 作为主芯片,而不是其他的,例如 Micro:bit、Raspberry PI 等,因为以下优点:
- 功能性:Arduino产品非常友好,广泛用于入门级学习者。 它有很多pin端口,可以连接各种模块。
- 可行性:虽然 Arduino UNO 比较大,但是如果我们想在某个有限的空间里把所有的部分组合起来,使其成为一个成熟的产品,我们可以将整个代码转移到一个更小的芯片上,比如 Arduino Micro、Arduino Nano。
- 可编程性:Arduino 更容易编码,它还提供了一些支持软件,比如 BLYNK。
- 社区环境:Arduino拥有成熟的用户社区,提供丰富的支持信息和问题解决方案,初学者更容易学习和调试。
- 负担能力:考虑到成本,Arduino不贵,但计算能力也不错。
投食模块选择
对于投食模块驱动的电机,在项目初期我们有两种选择:马达,以及舵机。最终我们选择了舵机而不是马达。首先,因为在Arduino芯片中使用马达通常需要在电路中添加继电器来防止电路过载,会徒增项目成本; 其次,通过考虑项目化需求,我们需要能控制每次喂猫的食物量,舵机可以控制旋角度以实现对投食量的精确把控。

程序代码
首先要做的是设置 Arduino 环境和定义所有引脚端口。
#include //Use Wire library
#include //Use Adafruit_GFX.h library
#include //Use Adafruit_SSD1306.h library
#include "dht11.h"
#include //Time-clock Library
#include //Arduino IDE Servo library
//define Servo inout
#Define Servo_Pin 5
//press value pin
int fsrPin = 14; // A0 port
int fsrReading;
//display value pin
const uint8_t OLED_CS = 9;
//Define OLED 12864 CS port to arduino 9
const uint8_t OLED_DC = 10;
//Define OLED 12864 DC port to arduino 10
const uint8_t OLED_RESET = 11;
//Define OLED 12864 RES port to arduino 11
const uint8_t OLED_MOSI = 12;
//Define OLED 12864 D1(MOST) port to arduino 12
const uint8_t OLED_CLK = 13;
//Define OLED 12864 D0(CLK) port to arduino 13
Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
//movement test
int Move_Sensor_pin = 8;
//Temperature
int incomingByte = 0;
// Receive data byte
String inputString = "";
// Store data byte
boolean newLineReceived = false;
// Last data end flag
boolean startBit = false;
//protocol Start sign
String returntemp = "";
//Store return value
char temp[100] = {0};
//motuo
Servo myservo;
//Define the servo object myservo
/*printfFormat string initialization*/
int serial_putc( char c, struct __file * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
以下代码块是主要部分,每个子函数的注释说明了各个算法模块的功能以及逻辑。
void setup(void) {
//common
Serial.begin(9600);
//movement
pinMode(Move_Sensor_pin,INPUT);
//Set the infrared interface of the human body to the input state
//display
display.begin(SSD1306_SWITCHCAPVCC);
//initialization OLED 12864
display.clearDisplay(); // Clean screen
display.setTextSize(2); //Set word font to 2
display.setTextColor(WHITE);//Set word color to white
display.setCursor(0,0); //Position the cursor in column 0, row 0
display.print(" SMU-IoT");//Display characters
display.setCursor(0,16);
display.print("Team03");
display.setCursor(0,32);
display.print("CatYou");
display.setCursor(0,48);
display.print("0ighting");
display.display();//Display
//Temperature
Wire.begin(0);
//Wire.begin();
//join i2c bus (address optional for master)
//motuo
printf_begin();
//Set the servo control pin to 5
myservo.attach(Servo_Pin);
//Initialize the servo position 0
myservo.write(0);
}
uint16_t result;
float temp1;
void loop(void) {
Serial.print("run");
delay(500);
fsrReading = analogRead(fsrPin); //heavy
delay(2000);
display.clearDisplay();
display.setCursor(0,0); //Position the cursor in column 0, row 0
display.print(" SMU-IoT");//Display characters
display.setCursor(0,16);
display.print("presre "); display.print(fsrReading);
Serial.print(fsrReading);
Wire.beginTransmission(0x5A);
Wire.write(0x07); // sends instruction byte
Wire.endTransmission(false); // stop transmitting
Wire.requestFrom(0x5A, 3); //Send data n-bytes read
result = Wire.read(); //Receive DATA
result |= Wire.read() << 8; //Receive DATA
uint8_t pec = Wire.read();
temp1 = result*0.02-273.15; //Temperature value conversion
Serial.print(temp1);
float fTemp = temp1;
display.setCursor(0,32);
display.print("Temp ");
display.print(fTemp);
display.display();
while (newLineReceived)// and digitalRead(Move_Sensor_pin)>0
{
Serial.print("run1");
delay(500);
while(inputString.indexOf("SERVO") == 1)
//If the string value "SERVO" to be retrieved appears
{//$SERVO,90#
int i = inputString.indexOf("#",7);
//Retrieve the position of the character string "#" from the received data starting with 7
if(i > 0)
//If retrieved
{String temp = inputString.substring(7, i);
//Extract the characters between the specified subscript 7 to i in the string and assign them to temp
int Pos = temp.toInt();
//Convert string temp to integer
if(Pos >=0 && Pos <= 180)
//If the angle of the steering gear to be rotated in the received agreement is within 0-180
{myservo.write(Pos);
returntemp = "$SERVO,0,#";
//Return matching success
Serial.print(returntemp);
//Return protocol packet
inputString = "";
// clear the string
newLineReceived = false;}
else
{ returntemp = "$SERVO,1,#";
//Return match failed
Serial.print(returntemp);
//Return protocol packet
inputString = "";
// clear the string
newLineReceived = false;
break;
}
}
}
if(inputString.indexOf("SERVO") == -1)
//If the string value "TH" to be retrieved does not appear
{
break;}
}
while(inputString.indexOf("TH") == 1){
//int fTemp = (float)DHT11.temperature;
//fTempAssigned to the temperature value read in floating point type
//int weight = DHT11.humidity;
//iHumidity Assign value to the humidity value read
Serial.print("run2");
delay(500);
fsrReading = analogRead(fsrPin); //heavy
Wire.beginTransmission(0x5A);
Wire.write(0x07); // sends instruction byte
Wire.endTransmission(false); // stop transmitting
Wire.requestFrom(0x5A, 3); //Send data n-bytes read
result = Wire.read(); //Receive DATA
result |= Wire.read() << 8; //Receive DATA
uint8_t pec = Wire.read();
temp1 = result*0.02-273.15; //Temperature value conversion
float fTemp = temp1;
float weight=fsrReading; //weightAssigned to the weight read
Serial.println(fsrReading);
//Parsing switch
// $TH,1#
memset(temp, 0x00, sizeof(temp));
//Empty the temp array
dtostrf(fTemp, 3, 1, temp); // Equivalent to %3.2f
String sTemp = temp;
//The string in the array temp is assigned to sTemp
String sWt = "";
sWt += weight;
returntemp = "$TH,T" + sTemp + ",H" + sWt + "#";
Serial.print(returntemp); //Return protocol packet
inputString = ""; // clear the string
newLineReceived = false;
if(inputString.indexOf("TH") == -1)
//If the string value "TH" to be retrieved does not appear
{
break;}
}
}
void serialEvent()
{
while (Serial.available())
{
incomingByte = Serial.read();
//Read byte by byte, the next sentence is to put the read into the string array to form a completed data packet
if(incomingByte == '$')
//If the serial port receives data, it enters the loop
{
startBit= true;
//If the incoming byte is'$', start reading
}
if(startBit == true)
{
inputString += (char) incomingByte;
// The full-duplex serial port does not need to add a delay below, and half-duplex has to be added
}
if (incomingByte == '#')
{
newLineReceived = true;
//If the incoming byte is'#', the reading ends
startBit = false;
}
}
}
需科学上网可于下方窗口浏览视频;若下方未成功加载视频窗口,可点击此模块标题(左上方)查看Demo视频。
挑战
- 编码:产品中涉及多个模块/传感器,不同模块之间的协作以及与主芯片的连接的编码部分复杂,调试需要大量时间。
- 猫:该项目的目的是建立一个远程喂猫的设备。 但尽管我们是猫咪爱好者,但我们都不是养猫人。因此…我们需要一只猫!!!
- 成本:我们的预算有限,因此项目很少有机会探索其他功能以与市场上的当前产品竞争,在项目蓝图制定期间我们应做出权衡决策。
局限性
- 识别度:目前,每个产品仅适配一只猫,因为它无法识别和区分不同的猫,并单独记录它们的测量值。
- 可扩展性:到目前为止,该产品被设计为仅适用于猫喂养。如果用户希望将此产品应用于其他宠物或更大的宠物(如狗),则该产品需要一些更新。可能需要更强的进料伺服系统、更大的进料容器和更多的压力传感器。
- 可操作性:目前,运动传感器(接近检测传感器)和微信小程序(控制部分)之间没有反应 – 响应关系。因此,用户不会直接通过运动传感器知道猫是否来了。但运动传感器控制压力传感器和温度传感器的启动,用户可以通过接收温度和重量读数来了解猫的接近。为了进一步发展,可以应用摄像头模块来提高猫的识别和接近检测精度。

数据分析
我们通过咨询猫主人并询问相关数据分析来进行数据模拟。
首先,当猫的体温高于正常水平时,体重和食物摄入量往往会减少。
我们用这个设备喂养并记录了猫一个月的健康状况。 周统计数据看似稳定,但日气温波动明显。 从3月17日到3月21日,由于体温高于猫的平均体温,可以推断猫发烧了。 与此同时,猫睡得更多,与主人的互动更少。 在此期间,猫的体重和食物摄入量都减少了。 在其他时间,猫的摄食量和体温都正常,猫的状况很健康。

其次,如果猫的摄食量明显下降,体重也下降了,猫可能生病了。 当这种情况持续三天以上时,我们会发出警报,提醒主人注意猫的情况,及时就医。


总之,不同的猫可能有不同的正常体温。
我们根据下图将猫体温异常的报警阈值设置为39.2℃。 但是,小猫的体温可能会更高。 另外,通过这个实验,我们发现了猫的个体差异,所以这个临界温度应该根据猫的不同年龄和品种手动调整。
综上所述,我们的数据测量具有很高的准确性。 它可以监测和记录猫咪的异常情况,甚至是微小的异常,让喂猫器及时意识到猫咪的身体不适。
总结

我们从这个项目中得到的关键词是激励和解决方案契合。作为一个项目,既涉及技术部分,也涉及非技术部分。关键点的最初想法是解决方案部署,但根据讲师的有意义的反馈来解决用户的需求同样重要。识别利益相关者和存在的相应问题以相应地解决是很重要的。它突出了问题识别部分,这花了我们很多时间来调整原型设计。这种工作一开始让我们很担心。此外,由于我们团队中不同成员的专业是多样化的,分工和协作是我们项目的另一个重点。分工明确,技术部分和非技术部分由2个小组妥善完成。
但由于时间的限制,我们无法做详细的市场投资,虽然它是商业产品的强烈需要。
尽管受到时间和预算的限制,我们还是以巨大的成绩完成了这个项目。我们要感谢教授和讲师的意义指导和及时的反馈和跟进,帮助我们有条不紊地设计和完成项目。愿我们的产品能为猫咪和主人提供更舒适的生活。


