收藏官网首页
查看: 6068|回复: 1

智能家庭培养箱biopod开源教程

跳转到指定楼层
楼主
 楼主| 发表于 2022-1-16 15:09:26 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
免费使用STM32、APP自动代码生成工具
本帖最后由 冯俊波 于 2022-1-16 15:09 编辑

biopod智能雨林缸60cm



项目内容:
1.灯光控制
2.循环控制
3.温度采集
4.温度和喷淋自动控制(手动控制下加热和喷淋可控,自动模式下加热和喷淋不可控)
5.状态断电记忆



云端部署:
本次设计以esp8266作为主控,SOC方案,利用赛博坦工具快速生成APP。
1.创建产品, 进入开发者中心,点击右上角,创建新产品,按照如图所示创建新的产品。

2.创建数据点。
3.生成ESP8266_32M SOC代码,下载到电脑备用。
4.由左上角的体验新版本切换到新版本开发者中心,点击右上角+创建一个新的移动应用。
5.点开创建好的应用,关联设备到移动应用里面。其他参数根据自己需求进行更改
6.回到新版本主页,在左侧选择自己创建的产品,然后进行模组配置。配置成乐鑫模组,注意只需要修改模组就行,热点参数无需更改。
7.进入应用页面,进行控制页面修改。
8.根据自己需求设置好控制模块的大小以及图标。其余参数根据自己的需求修改。级的每个页面都需要保存。
9.配置好所有参数过后,回到之前创建的移动应用里面,进行应用的构建,构建成功以后扫描后面的二维码下载安装到手机,到此云端部署完成。



硬件接线:
此项目不公开PCB,可以自己购买4路继电器,及防水温度传感器DS18B20探头,ESP12S小系统板。
继电器----GPIO13(加热管)GPIO12(循环电机)GPIO16(喷淋电机)GPIO5(灯光)
配网按键----GPIO14(按下低电平)
温度传感器----GPIO4(传感器需要上拉电阻)



程序修改:
       1.本次采用IDE方式进行开发编译(开发环境链接:https://pan.baidu.com/s/1TTIU-74mBxo9UqxLbX7Grw 提取码:0htq,解压过后即可使用,路径不能有中文),将前面下载的代码进行解压,路径不要含有中文。在IDE环境里面导入项目。导入步步骤易出错,注意根据下图中所示步骤进行导入。

       2.修改编译参数,打开根目录下面的Makefile文件,然后修改23到27行的内容。(注意:本教程代码不可以在网页上进行复制粘贴,由于编码不一致可能会导致程序不能编译,无法编译需要重新解压代码从头再来。代码需要自己手打。每次输入代码过后需要保存以后编译才会生效。)
  1. BOOT?=new
  2. APP?=1
  3. SPI_SPEED?=40
  4. SPI_MODE?=QIO
  5. SPI_SIZE_MAP?=6
复制代码

       3.按键部分无需修改,因为自动生成的代码就是gpio14按键长按短按进行网络配置。继电器引脚的初始化我们写在按键函数的初始化里面, 初始化为输出模式。
  1. GPIO_OUTPUT_SET(GPIO_ID_PIN(5),1);//灯光
  2.     GPIO_OUTPUT_SET(GPIO_ID_PIN(13),1);//加热管
  3.     GPIO_OUTPUT_SET(GPIO_ID_PIN(12),1);//循环电机
  4.     gpio16_output_conf();//喷淋电机
复制代码

4.在gizwits_product.c和gizwits_product.h增加全局变量。
  1. //flash相关
  2. #define sec 137 //137扇区,程序小于480K flash存储的安全区域的起始地址137-1024扇区
  3. #define sec1 138 //138扇区,程序小于480K flash存储的安全区域的起始地址137-1024扇区
  4. bool STATE[6] = {0,0,0,0,0,0};//开机各个开关状态标识
  5. uint32_t Set_Temp=0;   //温度自动控制
  6. uint32_t Open_Time=0;       //喷淋开时间
  7. uint32_t Off_Time=0;    //喷淋关时间
  8. extern bool STATE[6];   //开机各个开关状态标识
  9. extern uint32_t Set_Temp;    //温度自动控制
  10. extern uint32_t Open_Time; //喷淋开时间
  11. extern uint32_t Off_Time;     //喷淋关时间
复制代码

       5.在gizwits_product.c的gizwitsEventProcess函数里面对开关状态进行缓存。程序带有注释,此处不做截图,具体参考下面的程序更改。(注意:此函数的是数据点下发过后,可写类型的数据处理,会根据数据点的不同而不同。程序不能再网页复制,会导致编码不一致程序出错)
  1. int8_t ICACHE_FLASH_ATTR gizwitsEventProcess(eventInfo_t *info, uint8_t *data, uint32_t len)
  2. {
  3.     uint8_t i = 0;
  4.     dataPoint_t * dataPointPtr = (dataPoint_t *)data;
  5.     moduleStatusInfo_t * wifiData = (moduleStatusInfo_t *)data;
  6.     if((NULL == info) || (NULL == data))
  7.     {
  8.         GIZWITS_LOG("!!! gizwitsEventProcess Error \n");
  9.         return -1;
  10.     }
  11.     for(i = 0; i < info->num; i++)
  12.     {
  13.         switch(info->event)
  14.         {
  15.         case EVENT_Water_Cycle :
  16.             currentDataPoint.valueWater_Cycle = dataPointPtr->valueWater_Cycle;
  17.             GIZWITS_LOG("Evt: EVENT_Water_Cycle %d \n", currentDataPoint.valueWater_Cycle);
  18.             if(0x01 == currentDataPoint.valueWater_Cycle)
  19.             {
  20.                    STATE[0]=1;   //水循环打开
  21.             }
  22.             else
  23.             {
  24.                    STATE[0]=0;   //水循环关闭
  25.             }
  26.             STATE[6]=1;//flash存储状态
  27.             break;
  28.         case EVENT_Spray :
  29.             currentDataPoint.valueSpray = dataPointPtr->valueSpray;
  30.             GIZWITS_LOG("Evt: EVENT_Spray %d \n", currentDataPoint.valueSpray);
  31.             if(0x01 == currentDataPoint.valueSpray)
  32.             {
  33.                    if(STATE[4]==0)
  34.                             {
  35.                           STATE[2]=1;   //如果为手动模式,喷淋开关打开,否则不动作
  36.                           STATE[6]=1;//flash存储状态
  37.                             }
  38.             }
  39.             else
  40.             {
  41.                    if(STATE[4]==0)
  42.                    {
  43.                           STATE[2]=0; //如果为手动模式,喷淋开关关闭,否则不动作
  44.                           STATE[6]=1;//flash存储状态
  45.                    }
  46.             }
  47.             currentDataPoint.valueSpray = STATE[2];//更新数据点,APP更新
  48.             break;
  49.         case EVENT_Lamp :
  50.             currentDataPoint.valueLamp = dataPointPtr->valueLamp;
  51.             GIZWITS_LOG("Evt: EVENT_Lamp %d \n", currentDataPoint.valueLamp);
  52.             if(0x01 == currentDataPoint.valueLamp)
  53.             {
  54.                    STATE[1]=1;   //灯光打开
  55.             }
  56.             else
  57.             {
  58.                    STATE[1]=0;   //灯光关闭
  59.             }
  60.             STATE[6]=1;//flash存储状态
  61.             break;
  62.         case EVENT_Heating :
  63.             currentDataPoint.valueHeating = dataPointPtr->valueHeating;
  64.             GIZWITS_LOG("Evt: EVENT_Heating %d \n", currentDataPoint.valueHeating);
  65.             if(0x01 == currentDataPoint.valueHeating)
  66.             {
  67.                    if(STATE[4]==0)
  68.                    {
  69.                           STATE[3]=1;   //如果为手动模式,加热开关打开,否则不动作
  70.                           STATE[6]=1;//flash存储状态
  71.                    }
  72.             }
  73.                      else
  74.                      {
  75.                             if(STATE[4]==0)
  76.                             {
  77.                                    STATE[3]=0; //如果为手动模式,加热开关关闭,否则不动作
  78.                                    STATE[6]=1;//flash存储状态
  79.                             }
  80.                      }
  81.                      currentDataPoint.valueHeating = STATE[3];//更新数据点,APP更新
  82.             break;
  83.         case EVENT_mode:
  84.             currentDataPoint.valuemode = dataPointPtr->valuemode;
  85.             GIZWITS_LOG("Evt: EVENT_mode %d\n", currentDataPoint.valuemode);
  86.             switch(currentDataPoint.valuemode)
  87.             {
  88.             case mode_VALUE0:
  89.                    STATE[4]=0;   //手动模式
  90.                 break;
  91.             case mode_VALUE1:
  92.                    STATE[4]=1;   //自动模式
  93.                 break;
  94.             default:
  95.                 break;
  96.             }
  97.             STATE[6]=1;//flash存储状态
  98.             break;
  99.         case EVENT_Set_Temperature:
  100.             currentDataPoint.valueSet_Temperature= dataPointPtr->valueSet_Temperature;
  101.             GIZWITS_LOG("Evt:EVENT_Set_Temperature %d\n",currentDataPoint.valueSet_Temperature);
  102.             Set_Temp = currentDataPoint.valueSet_Temperature;     //缓存设置温度
  103.             STATE[6]=1;//flash存储状态
  104.             break;
  105.         case EVENT_Spray_Open_Time:
  106.             currentDataPoint.valueSpray_Open_Time= dataPointPtr->valueSpray_Open_Time;
  107.             GIZWITS_LOG("Evt:EVENT_Spray_Open_Time %d\n",currentDataPoint.valueSpray_Open_Time);
  108.             Open_Time = currentDataPoint.valueSpray_Open_Time;//缓存设置开时间
  109.             STATE[6]=1;//flash存储状态
  110.             break;
  111.         case EVENT_Spray_Off_Time:
  112.             currentDataPoint.valueSpray_Off_Time= dataPointPtr->valueSpray_Off_Time;
  113.             GIZWITS_LOG("Evt:EVENT_Spray_Off_Time %d\n",currentDataPoint.valueSpray_Off_Time);
  114.             Off_Time = currentDataPoint.valueSpray_Off_Time;//缓存设置关时间
  115.             STATE[6]=1;//flash存储状态
  116.             break;
复制代码

6. 接下来我们处理断电开机之后开关以及各项参数的初始化。主要是利用flash读取获取参数。数据状态存放在flash,后续教程及程序会有存储体现。初始化主要修改userInit函数。
  1. void ICACHE_FLASH_ATTR userInit(void)
  2. {
  3.     gizMemset((uint8_t *)¤tDataPoint, 0, sizeof(dataPoint_t));
  4.     //flash相关
  5.        uint32 value;
  6.        //定义数组addr_case1
  7.        uint8* addr_case1 = (uint8*)&value;//四字节对齐
  8.        uint8* addr_case2 = (uint8*)&value;//四字节对齐
  9.        //读取flash数据,sec*4*1024就是读取起始地址,就是具体的字节地址
  10.        spi_flash_read(sec*4*1024, (uint32*)addr_case1, sizeof(addr_case1));
  11.        spi_flash_read(sec1*4*1024, (uint32*)addr_case2, sizeof(addr_case2));
  12.        if(addr_case1[0]==1) STATE[0]=1;      //水循环
  13.               else STATE[0]=0;
  14.        if(addr_case1[1]==1) STATE[1]=1;      //灯光
  15.               else STATE[1]=0;
  16.        if(addr_case1[2]==1) STATE[2]=1;      //喷淋
  17.               else STATE[2]=0;
  18.        if(addr_case1[3]==1) STATE[3]=1;      //加热
  19.               else STATE[3]=0;
  20.        if(addr_case2[0]==1) STATE[4]=1;      //模式
  21.               else STATE[4]=0;
  22.        currentDataPoint.valueSet_Temperature = (uint32_t)addr_case2[1];
  23.        currentDataPoint.valueSpray_Open_Time = (uint32_t)addr_case2[2];
  24.        currentDataPoint.valueSpray_Off_Time = (uint32_t)addr_case2[3];
  25.        currentDataPoint.valueWater_Cycle = STATE[0];
  26.        currentDataPoint.valueSpray = STATE[2];
  27.        currentDataPoint.valueLamp = STATE[1];
  28.        currentDataPoint.valueHeating = STATE[3];
  29.        currentDataPoint.valuemode = STATE[4];
  30.        currentDataPoint.valueTemperature = 0;
  31.        Set_Temp = currentDataPoint.valueSet_Temperature;
  32.        Open_Time = currentDataPoint.valueSpray_Open_Time;
  33.        Off_Time = currentDataPoint.valueSpray_Off_Time;
  34.        GPIO_OUTPUT_SET(GPIO_ID_PIN(12),!STATE[0]);//水循环
  35.        GPIO_OUTPUT_SET(GPIO_ID_PIN(5),!STATE[1]);//灯光
  36. }
复制代码

7.在gizwits_product.c新增DS18B20驱动函数。由于程序太长此处不再截图。
  1. /************************
  2. * 函 数 名         : Ds18b20Init
  3. * 函数功能            : 初始化
  4. * 输    入         : 无
  5. * 输    出         : 初始化成功返回1,失败返回0
  6. ************************/
  7. uint8 Ds18b20Init() {
  8.        int i;
  9.        PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
  10.        GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 0);            //将总线拉低480us~960us
  11.        os_delay_us(642);         //延时642us
  12.        GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 1);     //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
  13.        while (GPIO_INPUT_GET(GPIO_ID_PIN(4))) //等待DS18B20拉低总线
  14.        {
  15.               os_delay_us(500);
  16.               os_delay_us(500);
  17.               i++;
  18.               if (i > 5)   //等待>5MS
  19.                             {
  20.                      return 0;  //初始化失败
  21.               }
  22.        }
  23.        return 1;  //初始化成功
  24. }
  25. /************************
  26. * 函 数 名         : Ds18b20WriteByte
  27. * 函数功能            : 向18B20写入一个字节
  28. * 输    入         : dat
  29. * 输    出         : 无
  30. ************************/
  31. void Ds18b20WriteByte(uint8 dat) {
  32.        int i, j;
  33.        for (j = 0; j < 8; j++) {
  34.               GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 0);                 //每写入一位数据之前先把总线拉低1us
  35.               i++;
  36.               GPIO_OUTPUT_SET(GPIO_ID_PIN(4), dat & 0x01);                //然后写入一个数据,从最低位开始
  37.               os_delay_us(70); //延时68us,持续时间最少60us
  38.               GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 1);     //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
  39.               dat >>= 1;
  40.        }
  41. }
  42. /************************
  43. * 函 数 名         : Ds18b20ReadByte
  44. * 函数功能            : 读取一个字节
  45. * 输    入         : 无
  46. * 输    出         : byte
  47. ************************/
  48. uint8 Ds18b20ReadByte() {
  49.        uint8 byte, bi;
  50.        int i, j;
  51.        for (j = 8; j > 0; j--) {
  52.               GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 0);     //先将总线拉低1us
  53.               i++;
  54.               GPIO_OUTPUT_SET(GPIO_ID_PIN(4), 1);       //然后释放总线
  55.               i++;
  56.               i++;   //延时6us等待数据稳定
  57.               bi = GPIO_INPUT_GET(GPIO_ID_PIN(4));    //读取数据,从最低位开始读取
  58.               /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
  59.               byte = (byte >> 1) | (bi << 7);
  60.               os_delay_us(48); //读取完之后等待48us再接着读取下一个数
  61.        }
  62.        return byte;
  63. }
  64. /************************
  65. * 函 数 名         : Ds18b20ChangTemp
  66. * 函数功能            : 让18b20开始转换温度
  67. * 输    入         : 无
  68. * 输    出         : 无
  69. ************************/
  70. void Ds18b20ChangTemp() {
  71.        Ds18b20Init();
  72.        os_delay_us(500);
  73.        os_delay_us(500);
  74.        Ds18b20WriteByte(0xcc);             //跳过ROM操作命令
  75.        Ds18b20WriteByte(0x44);         //温度转换命令
  76. //     延时100ms 等待转换成功,而如果你是一直刷着的话,就不用这个延时了
  77. }
  78. /************************
  79. * 函 数 名         : Ds18b20ReadTempCom
  80. * 函数功能            : 发送读取温度命令
  81. * 输    入         : 无
  82. * 输    出         : 无
  83. ************************/
  84. void Ds18b20ReadTempCom() {
  85.        Ds18b20Init();
  86.        os_delay_us(500);
  87.        os_delay_us(500);
  88.        Ds18b20WriteByte(0xcc);      //跳过ROM操作命令
  89.        Ds18b20WriteByte(0xbe);     //发送读取温度命令
  90. }
  91. /************************
  92. * 函 数 名         : Ds18b20ReadTemp
  93. * 函数功能            : 读取温度
  94. * 输    入         : 无
  95. * 输    出         : temp
  96. ************************/
  97. float Ds18b20ReadTemp() {
  98.        float temp = 0;
  99.        uint8 tmh, tml;
  100.        uint32_t temp1;
  101.        Ds18b20ChangTemp();                    //先写入转换命令
  102.        Ds18b20ReadTempCom();                 //然后等待转换完后发送读取温度命令
  103.        tml = Ds18b20ReadByte();          //读取温度值共16位,先读低字节
  104.        tmh = Ds18b20ReadByte();         //再读高字节
  105.        temp1 = tmh;
  106.        temp1 <<= 8;
  107.        temp1 |= tml;
  108.        temp = temp1*0.0625;
  109.        temp = ((temp+0.005)*100)/100;//保留2位小数,四舍五入
  110.        return temp;
  111. }
复制代码

在gizwits_product.c新增温度传感器的函数**。
  1. uint8 Ds18b20Init();
  2. void Ds18b20WriteByte(uint8 dat);
  3. uint8 Ds18b20ReadByte();
  4. void Ds18b20ChangTemp();
  5. void Ds18b20ReadTempCom();
  6. float Ds18b20ReadTemp();
复制代码

       8.在gizwits_product.c的userHandle函数里面对GPIO输出点,温度采集,flash存储以及逻辑控制进行编写。此处不在截图。
  1. void ICACHE_FLASH_ATTR userHandle(void)
  2. {
  3.        //flash相关
  4.        uint32 value;
  5.        //定义数组addr_case1
  6.        uint8* addr_case1 = (uint8*)&value;
  7.        uint8* addr_case2 = (uint8*)&value;
  8.        LOCAL float tempvalue;//采集温度
  9.        LOCAL uint32_t opentime=0;//开计时
  10.        LOCAL uint32_t offtime=0;//关计时
  11.        LOCAL bool onoff=0;//开关状态,0关,1开
  12.        os_delay_us(642);
  13.        LOCAL uint8_t temp_time=0;//温度采集间隔时间
  14.        if(temp_time<=1)temp_time++;
  15.        else
  16.        {
  17.               temp_time=0;
  18.               tempvalue = Ds18b20ReadTemp();
  19.               currentDataPoint.valueTemperature = tempvalue;
  20.        }
  21.        if(STATE[4])//自动模式下喷淋和加热控制
  22.        {
  23.               //加热温度控制
  24.               if(tempvalue<(float)Set_Temp) STATE[3]=1;
  25.                      else STATE[3]=0;
  26.               //喷淋控制
  27.               if(onoff)//开状态
  28.               {
  29.                      if(opentime>0) opentime--;
  30.                      else
  31.                      {
  32.                             onoff=0;//切换关状态
  33.                             offtime=Off_Time*60;//赋值关闭时间
  34.                             STATE[2]=0;
  35.                      }
  36.               }
  37.               else if(onoff==0)//关状态
  38.               {
  39.                      if(offtime>0) offtime--;
  40.                      else
  41.                      {
  42.                             onoff=1;//切换开状态
  43.                             opentime=Open_Time*60;//赋值打开时间
  44.                             STATE[2]=1;
  45.                      }
  46.               }
  47.               gpio16_output_set(!STATE[2]);//喷淋
  48.               GPIO_OUTPUT_SET(GPIO_ID_PIN(13),!STATE[3]);//加热
  49.               currentDataPoint.valueSpray = STATE[2];
  50.               currentDataPoint.valueHeating = STATE[3];
  51.        }
  52.        else //手动模式
  53.        {
  54.               GPIO_OUTPUT_SET(GPIO_ID_PIN(13),!STATE[3]);//加热
  55.               gpio16_output_set(!STATE[2]);//喷淋
  56.        }
  57.        if(STATE[6]==1)     //状态改变
  58.        {
  59.               STATE[6]=0;//清除状态
  60.               GPIO_OUTPUT_SET(GPIO_ID_PIN(12),!STATE[0]);//水循环
  61.               GPIO_OUTPUT_SET(GPIO_ID_PIN(5),!STATE[1]);//灯光
  62.               //flash存储数据前转换数据
  63.               addr_case1[0] = (uint8)STATE[0];
  64.               addr_case1[1] = (uint8)STATE[1];
  65.               addr_case1[2] = (uint8)STATE[2];
  66.               addr_case1[3] = (uint8)STATE[3];
  67.               addr_case2[0] = (uint8)STATE[4];
  68.               addr_case2[1] = (uint8)currentDataPoint.valueSet_Temperature;
  69.               addr_case2[2] = (uint8)currentDataPoint.valueSpray_Open_Time ;
  70.               addr_case2[3] = (uint8)currentDataPoint.valueSpray_Off_Time;
  71.               //擦除要写入的Flash扇区
  72.               spi_flash_erase_sector(sec);
  73.               //写入数据,sec*4*1024就是写入起始地址,就是具体的字节地址
  74.               spi_flash_write(sec*4*1024, (uint32*)addr_case1, sizeof(addr_case1));
  75.               //擦除要写入的Flash扇区
  76.               spi_flash_erase_sector(sec1);
  77.               //写入数据,sec*4*1024就是写入起始地址,就是具体的字节地址
  78.               spi_flash_write(sec1*4*1024, (uint32*)addr_case2, sizeof(addr_case2));
  79.        }
  80.     system_os_post(USER_TASK_PRIO_2, SIG_UPGRADE_DATA, 0);
  81. }     
复制代码

9.修改完代码之后ctrl+B进行编译固件编译。
10.利用乐鑫烧录软件将生成的固件烧录到ESP8266里面。参数参考下图,注意参数不能有错。下载硬件接线如下表下载模式。记住通电瞬间就要保持这个状态才是下载模式。
11.程序烧录完成之后通过按键长按触发airlink配网(或短按触发softap配网),在APP选择对应的配网进行网络配置及绑定设备。绑定后进入设备即可进行采集和控制
12.实物展示展示


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

本版积分规则

加入Q群 返回顶部

版权与免责声明 © 2006-2024 Gizwits IoT Technology Co., Ltd. ( 粤ICP备11090211号 )

快速回复 返回顶部 返回列表