亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

带蓝牙的智能恒温器、通风和灯光控制

组件和用品

Arduino Nano R3
× 1
HC-05 蓝牙模块
× 1
Maxim Integrated DS18B20 可编程分辨率单线数字温度计
× 1
oled 1306 I2C 双色
× 1
ControlEverything.com 用于 I2C 的 4 通道继电器控制器
× 1

应用和在线服务

适用于 Android 的 Morich 串行蓝牙终端
适用于 Android 的 Arduino 蓝牙控制设备

关于这个项目

简介

简单但有用的 DIY Arduino 恒温器项目,带蓝牙。

智能恒温器程序控制加热器燃气锅炉,系统通过继电器切换我的厨房灯和浴室通风风扇 - 可以通过按钮和 Android 设备或计算机通过蓝牙进行控制。 摄氏度华氏度 版本也可用!所有零件都是手工制作的。

在Arduino Nano(或更高)板上开发,使用Dallas DS18B20温度传感器,HC-05/06蓝牙适配器,I2C 128X64双色OLED显示屏——您可以选择不同的LCD/OLED显示屏,u8glib支持多种类型。

视频

视频展示了所有重要的工作方式,让您从头开始构建项目。

加热器工作方式

1:一次性加热(15 分钟)定时模式, 春秋之夜很有用

2:恒温模式(更高优先级) , 调整后的目标温度存储在 EEPROM 存储器中

锅炉节能算法

频繁、短期的切换会缩短加热器燃气锅炉的使用寿命,为了避免这种情况,程序使用修正值——这些值声明了过热和冷却的程度。较低的温度需要较大的校正值,因为墙壁较冷,并且更好地吸收新鲜加热的空气的热量,因此上述效果会更强。

打开窗口检测

程序检测窗户是否打开,因此温度在一分钟内至少下降 -0, 2°C (-0, 36F) - 加热器停止或无法启动,并且通风风扇将打开以帮助清新空气。当空气由于环境的热容量而变得至少 +0, 12°C (+0, 216F) 时,系统将切换回正常模式,并且“窗口警报”事件将被解除。

错误检测

测量温度低于 0°C (32F) 或高于 40°C (104F) 将被评估为故障或其他问题(错误、破窗、火灾等),所有设备将关闭。

蓝牙通讯

Android GUI 应用程序有 8 个按钮,发送大写和小写字母以打开 ('A') 或关闭 ('a') 定时加热器,'B' 和 'b' 会打开播放,'C ' 和 'c' 灯……

我项目的另一个优势是极客之友蓝牙串口终端的使用。只需使用串行终端通过蓝牙与系统聊天——它可以是一个 Android 应用程序,但即使是普通的 PC 也可以——例如 Arduino IDE 的串行监视器。

控制器每分钟自动发送温度报告,并即时报告所有事件,例如连接的设备已打开/关闭,恒温器例程激活等。

控制代码

它接受控制代码并发送确认消息。我的命令结构基于两位数字,如‘XY’ – where;

‘X’是设备码,‘Y’是操作码

30、31、32:照明关/开/翻转逻辑状态

40、41、42:播出关/开/翻转逻辑状态

50、51、52:一次性加热器程序关闭/开启/翻转逻辑状态

恒温器功能的目标温度将接受 10 – 24 个数字

‘r’——受控设备工作状态报告

'w' - 如果您不想等待其自动解除,则手动禁用“窗口警报”事件

'A, a... H, h' - 字母被接受,因为 GUI 应用程序会发送

华氏版

把所有的修正变量、参考值和比值都换算了,所以系统保持了它的计算和工作的特殊性。

只有少数修改。华氏版新设备代码:

1 - 照明(10:关闭,11:开启,12:翻转状态)

2 - 正在播出

3 - 加热器

50 - 76 数字是目标温度值

续集

更新:具有内部蓝牙链接和语音控制的完整家庭自动化系统!

DallasTemperature、elapsedMillis、OneWire、SoftwareSerial、olikraus/u8glib

如果您使用不同的按钮电阻,或者需要检查 A0 读数,我建议您检查一下:

http://blog.codebender.cc/2015/07/20/tutorial-multiple-buttons-on-1-analog-pin/

代码

  • Thermostat v11 - 摄氏版本.ino
  • 恒温器 v11 - 华氏度版本.ino
Thermostat v11 - 摄氏版本.inoArduino
程序代码自行解释,对初学者有用。很好的评论,描述了每一个重要的步骤;什么,为什么和如何。按功能划分模块,这就是为什么易于概述。 HC-05 蓝牙适配器,I2C 128X64 双色 OLED 显示屏。// 系统可以通过蓝牙通过按钮和 Android 智能手机控制。//// 它处理 HEATER 燃气锅炉、浴室通风机和厨房照明 - 用继电器切换.// 加热器有两种工作方式。 1:一次性加热(15 分钟)定时模式,2:恒温模式(更高优先级)。调整后的目标温度。// 存储在 EEPROM 中。程序检测硬件错误和窗口打开 - 在这些情况下,加热器停止和/或无法启动。//// 由 Gyula Osi 设计和编程。// 保留所有权利。// --------- -------------------------------------------------- -------------------------------------------------- -----------------------// ---- 显示 I2C 总线,SDA(TX) -> A4,SCL(RX) -> A5#include "U8glib.h"U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // 显示构造函数byte frame =0; // 起始标志指针// ---- 端口和相关声明const byte buzzer =9; // 蜂鸣器到 D9int btnVal; // 存储来自buttonsconst字节relayA =11的模拟值; // airingbool aState =0;bool aStateByCmd; // 如果“窗口警报”消失,则播出控制回到原来的状态const 字节relayL =13; //lightingbool lState =0;const字节relayH =10; // 加热器常量字节 ledH =6; // PWM 端口允许输出电平调整用于改变亮度const byte ledA =5; const byte ledL =3;const byte Bright[3] ={0, 10, 100};byte BrightHeat =Bright[2];#define ledInterval 1000 // 加热器 LED 闪烁间隔unsigned long prev =0;// ----用于显示和串行报告的字符串 #define STATE_ITEMS 3 // 用于输出的设备状态指示器字符串// i -> 0 1 2const String state_str[STATE_ITEMS] ={"on.", "off.", "auto."};字符串加热器状态; String airingState;StringlightingState;#define FUNCT_ITEMS 11// i -> 0 1 2 3 4 5 6 7 8 9 10 String funct_str[FUNCT_ITEMS] ={"Heater is ", "Airing is ", "Lighting is ", "Window警报!”,“硬件错误!”,“点击一个键>>”,“或发送代码!”,“目标温度=”,“温度=”,“*”,“-”};//- -- 温度测量和加热器相关功能#include elapsedMillis timer0; // 8 位,PWM 定时器,由函数 elapsedMillis()#define sftyTmrInterval 15 * 60000 使用 // 一次加热(15 分钟)定时模式间隔 [ms]bool sftyTmrEnded; // boolean 启动,timer0 已经结束#include byte tTarget; // 调整后的目标 temprconst int addr =0; // EEPROM内存中tTarget变量的当前地址bool hState =0; // 加热器布尔 statebool hThermostat =0; // 恒温器布尔状态#include "OneWire.h" #include "DallasTemperature.h"#define DS18B20 2 // 在 D2OneWire temprWire(DS18B20); 上设置 OneWire 总线; // 设置 DS18B20 以在 OneWire 总线上工作达拉斯温度传感器(&temprWire);float tempr; // 测量值float temprPrev; // 趋势分析测量值的副本bool windowAlrt =0; // 指定的 tempr 度 dropbool measError =0; // 测量的 tempr 值超出指定的范围 normalconst long temprInterval =60000; // 循环 tempr 测量间隔 [ms]unsigned long temprTmrPrev =0; // 当 temprMeas() 调用 float heatCorrVal 时,经过的时间将是前一个; // 声明过热和冷却的程度,参见 tempMeas() 函数 // ---- 虚拟 RXD/TXD 上的串行通信配置#include // HC-05const 的 SW 串行 RX 和 TX 引脚int RX1 =8;const int TX1 =4;SoftwareSerial sUART(RX1,TX1);字符 RX[2]; // 存储接收到的串行 databool errMsgSentBySys =0;布尔启动 =1; // 在启动时避免重复 BT 报告const uint8_t frame1[] U8G_PROGMEM ={ // XBM 映射 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFFxFF, 0xFF 、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF , 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF , 0x7F, 0x00, 0xFC, 0xFF, 0x7F, 0xF0, 0x3F, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F,0xFF,0xF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF,0xF,0xF,0xF , 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF , 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C , 0x3F, 0x1E, 0xFF, 0x00, 0xFE, 0x1F, 0xFF, 0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0x0 , 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x1F, 0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE,0xE,0xE,0xF,0xE,0xF,0xE,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0xE,0xF , 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE, 0x7F, 0xFC, 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E,0xF,0xF,0xF,0xF,0xF,0xF,0xF,0x0F,0x0F,0x0F , 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0xFC, 0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0x,0x,0x,0x,1F,0x,0Fx,0Fx,0Fx,0C,0 ,0xF8的,为0x1F,0x7C,0XF0,0x00时,0x7E的,0XF0,为0x0F,0xFE的,为0x18,0x1F的,0x0C,0x3E的,0X1E,为0x3C,0x3E的,0xF8的,为0x1F,0x7C,0XF0,0x00时,0x7E的,0XF0,为0x0F,0xFE的,为0x18,0x1F的,0x0C,0x3E的,0X1E,为0x3C,0x3E的,0xF8的,为0x1F,0x7C,0XF0,0x3F的,0x78,0XF0,为0x0F,0xFE的,为0x18,0x1F的,0x0C,0x3E的,0X1E,为0x3C,0x3E的,0xF8的,0x1F的, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8的,0x1F的,0x7C,0XF0,0x3F的,0x78,0XF0,为0x0F,0xFE的,为0x18,0x1F的,0x0C,0x3E的,0X1E,为0x3C,0x3E的,0xF8的,为0x1F,0x7C,0XF0,0x3F的,0x78,0XF0,0x3F的,0×00,的0x7E,0x00时,为0x3C,0x80的,0×07,0XF0,0x00时,0xF8的,0x7F的,0×00,为0x3C,0x00时,0X1E,将0xC0,0x3F的,0×00,的0x7E,0x00时,为0x3C,0x80的,0×07,0XF0,0x00时,0xF8的,0x7F的, 0x00、0x3C、0x00、0x1E、0xC0、0xFF、0xFF、0xFF、0x1F、0xFC、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0 0xFC、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0x7F、0x00、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 0xFF、0xFF、0xFF、0xFF、0xFF、0x7F、0x00、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xF F、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、 0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF 0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0xE6, 0x3F, 0x06, 0xC6, 0x7F, 0xFE, 0xE7, 0x3F, 0x7E, 0x7,0x,60x,0x,60x,0x,60x,0x0x,0x0x,0x0x0 0x66,0x00时,0x60的,0x60的,0x60的,的0x30,0×06,0x60的,0×00,0x00时,0×00,的0x30,0×06,0x66,0x60的,0×06,0x66,0x00时,0x60的,0x60的,0x60的,的0x30,0×06,0x60的,0×00,为0x00,0x00时,的0x30,0X1E,0x66,0x60的,0×06,0x66,0x00时,0x60的,0x60的,0x60的,的0x30,0×06,0x60的,0×00,0x00时,0×00,的0x30,0x3E的,0x66,0x60的,0×06,0x66,0x00时, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0 xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 0xFE, 0xC3, 0x3F, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0xF,0x3,0xF,0xF,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F为0x00,0x00时,为0x3C,写0xDE,0xE7,0x61,0X1E,0×07,0x70,0x70,取0xE0,0x1D,为0x3C,0X1E,0x00时,0x70,0x00时,0×00,为0x3C,0X1E,0xE7,0x61,0X1E,0×07,0x70, 0x70,取0xE0,0X31,为0x3C,0X1E,0x00时,0x70,0x00时,0×00,为0x3C,0X1E,0xE6,0x61,0X1E,0×07,0x70,0x70,取0xE0,0x61,为0x3C,0X1E,0x00时,0x70,0x00时,0×00,为0x3C,0X1E,0xE6,0x61,0X1E,0×07,0x70,0x70,取0xE0,0x61,为0x3C,0X1E,0x00时,0x70,0x00时,0×00,0x7F的,0X1E,0xE6,0x3F的,0xFC有,0xE3,0x3F的,0x70,取0xE0, 0x61,的0x7E,0xFE的,0xE7,0x3F的,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00 ,为0x00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00,0x00时,0×00 , 0x00, 0x00, 0x00, };void setup() { sUART.begin(9600); pinMode(继电器H,输出);中继处理器H(4); // 获取工作状态 pinMode(ledH, OUTPUT); pinMode(继电器A,输出);中继处理器A(4); pinMode(ledA,输出); pinMode(relayL, OUTPUT);中继处理器L(4); pinMode(ledL,输出); pinMode(蜂鸣器,输出);传感器开始(); // 启动 DS18B20 temprMeas(); // 不要等待 meas.timer,在启动时调用该函数一次 tTarget =EEPROM.read(addr); // 启动时从EEPROM的当前地址读取之前存储的tTarget值startup =0;如果 (!measError) { sTX(8); // 启动后调用工作状态即时报告 } else { sTX(2); }}void loop() { temprTimer(); ledHandler(); btnReadings(); sRX();安全Tmr(); u8g.firstPage(); // scr 循环 do { draw(); } while( u8g.nextPage() );如果(帧 ==0){ 延迟(3000);帧 =1; clrScr(); }}void btnReadings() { // --------- Btn 读数 btnVal =analogRead(A0); // 从 A0 读取模拟值 if (btnVal>=510 &&btnVal <=516) { // btn Lighting delay(100); // btn debounce buzz(3, 1);中继处理器L(2); // 使用逻辑状态翻转操作码作为参数调用适当的函数 } else if (btnVal>=849 &&btnVal <=855){ // btn Airing if (windowAlrt ==1) { // 如果系统处于窗口警报模式,禁用它 windowAlrt =0;嗡嗡声(4, 3);中继处理器A(3); } else { // else 打开/关闭通风机延迟(100);嗡嗡声(3, 1);中继处理器A(2); } } else if (btnVal>=927 &&btnVal <=933){ // btn 一次加热(15 分钟)定时模式延迟(100);嗡嗡声(3, 1);中继处理器H(2); } else if (btnVal>=767 &&btnVal <=777) { // btn 减少 tTarget delay(100); tTargetHandler(0); } else if (btnVal>=687 &&btnVal <=697) { // btn 增加 tTarget delay(100); tTargetHandler(1); } else if (btnVal>=856 &&btnVal <=862) { // inc &dec btns 同时 tTarget =14; // <====初始值 - 在第一次通电时按下这些按钮! }}void sRX() { // ------------- 接收串行数据 while (sUART.available()> 0) { // 如果数据可用于读取(字节 i =0; i <2; i++) { RX[i] =sUART.read(); } } int iRX[2]; for (byte i =0; i <2; i++) { iRX[i] =RX[i] - '0'; } switch (RX[0]) { // ------------ 接受单字母控制代码 case 'A':relayHandlerH(1); // 1 =中断;案例'a':relayHandlerH(0); // 0 =关闭中断; // 接收到的串行数据可以是来自“Arduino case 'B':// 蓝牙控制设备”Android 应用程序的单个字母。如果一个正确的字母relayHandlerA(1); // 字符到达,程序代码不会等待第二个,break; // 但使用适当的操作代码调用适用的函数。 case 'b':// 组合数字数据的案例可以在下面看到。中继处理器A(0);休息;案例'C':relayHandlerL(1);休息;案例'c':relayHandlerL(0);休息;案例“D”:案例“d”:tTarget =21; tTargetEEPROM();嗡嗡声(3, 1);休息;案例“E”:案例“e”:tTarget =19; tTargetEEPROM();嗡嗡声(3, 1);休息;案例'F':案例'f':tTarget =14; tTargetEEPROM();嗡嗡声(3, 1);休息; case 'R':// 调用有关受控设备的概览报告 case 'r':sTX(8);休息; case 'W':// 禁用窗口警报状态 case 'w':windowAlrt =0;嗡嗡声(4, 3);中继处理器A(3);休息; } // ----------------------- 接受组合数字控制代码 // 在这种情况下,两位数字控制代码以字符格式到达,//例如,来自 Android 蓝牙串行应用程序。在字符到整数 // 转换后(仅当第一个字符为“1”或“2”时)将遵循合并过程, // 条件和语句系统将做出决定并执行它。 // 适当的数字代码是: // // ---------------- 目标温度: // 10 - 24 个值将被接受为恒温器功能的目标温度。 // // ---------------- 设备控制代码: // 第一个 =设备代码,第二个 =操作员代码 // 30、31、32 打开照明:30=关闭, 31=on // 32=翻转逻辑状态(开 -> 关 / 关 -> 开)// 40, 41, 42 将对通风机执行相同的操作 // 50, 51, 52 处理一次性加热 (15 mins) 定时加热器程序如上 // // ---------------- 分类操作符代码: // X3、X4 被分类,仅用于内部程序序列的函数调用 if ( RX[0] =='3') {relayHandlerL(iRX[1]); } if (RX[0] =='4') {relayHandlerA(iRX[1]); } if (RX[0] =='5') {relayHandlerH(iRX[1]); } if ((iRX[0] * 10 + iRX[1] <=24) &&(iRX[0] * 10 + iRX[1]>=10)) { // 只接受 10 到 24 tTarget =之间的数值iRX[0] * 10 + iRX[1]; // 合并两个整数并设置 tTarget tTargetEEPROM(); // 设置后,调用 EEPROM 处理函数,并 buzz(3, 1); // 将 tTarget 值写入 EEPROM 的相应字节 } if (RX[0] =='0') { // 测试 (byte i =1; i <5; i++) { buzz(5, 2); } } for (byte i =0; i <2; i++) { // 清空所有消息接收器和转换变量 RX[i] ='Z'; } }void relayHandlerL(byte lOperator) { // Lighting Handler Sequence // 操作符是:0=off, 1=on, 2=flip the state, 4=fill/refill the light state char var. if ((measError) &&((lOperator ==1) || (lOperator ==2))) { sTX(4);返回; } if ((lOperator ==2) || (lOperator ==0) &&(lState) || (lOperator ==1) &&(!lState)) { lState =!lState;数字写入(中继L,lState);嗡嗡声(2, 1); } if (lOperator>=0) { // 用正确的状态指示字符串填充工作状态字符 if (lState) {lightingState =state_str[0]; } else {lightingState =state_str[1]; } if (!startup) { sTX(7); } }}void relayHandlerA(byte aOperator) { // Airing Handler Sequence if ((measError) &&((aOperator ==1) || (aOperator ==2))) { // 操作符是:0=off, 1=on,2=翻转状态,sTX(4); // 3=由 temprMeas() 函数调用,4=填充/重新填充播放状态 char var。返回; } aState =digitalRead(relayA); if (!windowAlrt) { if ((aOperator ==2) || (aState) &&(aOperator ==0) || (!aState) &&(aOperator ==1)) { aState =!aState;数字写入(中继A,aState); aStateByCmd =digitalRead(relayA);嗡嗡声(2, 1); } } if (aOperator ==3) { // 由 temprMeas() 函数调用,'windowAlrt' 结束或开始 if ((!aState) &&(windowAlrt) || (aState) &&(!windowAlrt) &&(!aStateByCmd )) { digitalWrite(relayA, windowAlrt); } } aState =digitalRead(relayA); if (aOperator>=0) { if (aState) { if (windowAlrt) { airingState =state_str[2]; } else { airingState =state_str[0]; } } else { airingState =state_str[1]; } } if (!startup) { sTX(6); }} void relayHandlerH(byte hOperator) { // Heater Handler Sequence // 操作符为:0=off, 1=on, 2=flip the state, // 3=由 temprMeas() 函数调用,4=fill/refill加热器状态字符变量。 if ((measError) &&((hOperator ==1) || (hOperator ==2))) { sTX(4);返回; } if ((!hThermostat) &&(!windowAlrt) &&(!measError)) { // 打开/关闭一次性加热(15 分钟)定时模式 if ((hOperator ==2) || (hOperator ==1 ) &&(!hState) || (!hOperator) &&(hState)) { buzz(2, 1); hState =!hState; sftyTmrEnded =0;定时器0 =0;数字写入(relayH,hState); } } if (windowAlrt) { sTX(3); } if (hOperator ==3) { // 这个函数被 temprMeas() 函数调用 (op 3) // 为了检查 windowAlrt 和 measError 布尔值 if ((windowAlrt) &&(hState)) { // 一个窗口是打开并且加热器正在运行 digitalWrite(relayH, 0);嗡嗡声(5, 3); } if ((!windowAlrt) &&(!measError)) { if ((hThermostat) || (!hThermostat) &&(hState) &&(sftyTmrEnded)) { digitalWrite(relayH, hThermostat); // 继续执行恒温器程序的命令 } } } hState =digitalRead(relayH); if (hOperator>=0) { if (hState) { if (hThermostat) {heaterState =state_str[2]; } else { heatState =state_str[0]; } } else { heatState =state_str[1]; } if ((((!windowAlrt) &&(hOperator !=3)) || (hState)) &&(!startup)) { sTX(5); } }}void safetyTmr () { // 一次性加热(15 分钟定时)模式的定时器 if ((hState) &&(!sftyTmrEnded) &&(timer0> sftyTmrInterval) &&(!hThermostat)) { sftyTmrEnded =1;中继处理器H(0); for (byte i =1; i <5; i++) { buzz(i, 2); } }}void temprTimer() { // temprMeas() 的循环定时器 unsigned long temprTmrCurr =millis(); if (temprInterval <=temprTmrCurr - temprTmrPrev) { temprTmrPrev =temprTmrCurr; temprMeas(); } }void temprMeas() { // ----------- 温度测量和比较序列 temprPrev =tempr; // 保存下一次比较的值sensors.requestTemperatures(); // 更新传感器读数 tempr =sensors.getTempCByIndex(0); // 读取温度 if ((tempr>=40) || (tempr <=0)) { // 极端测量值:if (!errMsgSentBySys) { // -127, -196.60 是硬件错误,+85 是典型的软件错误错误,但 sTX(4); // 可能是火灾,或者是一个破碎的窗口 } errMsgSentBySys =1; h恒温器=0;如果(hState){relayHandlerH(0); } if (aState) { relayHandlerA(0); } if (lState) { relayHandlerL(0); } measError =1; for (byte i =1; i <10; i++) { buzz(4, 1);延迟(50); } } else { temprPrev =tempr;测量错误 =0; errMsgSentBySys =0; } if (!measError) { // ------------ 温度分析序列的开始 if (tempr <=17) { // 加热器燃气锅炉的频繁、短期切换会缩短它的生命周期,heatCorrVal =0.5; // heatCorrVal 值有助于避免它。声明过热和冷却的程度。 } // 较低的温度需要更大的 heatCorrVal,因为墙壁更冷并且吸收更好 if ((tempr> 17) &&(tempr <19)) { // 来自新鲜加热的空气的温暖,因此上述效果会更加有效。 heatCorrVal =0.4; } if (tempr>=19) { heatCorrVal =0.3; } if (tTarget - tempr>=heatCorrVal) { // 如果差值等于或大于 heatCorrVal sftyTmrEnded =1,则从目标中减去测量值; // 如果正在运行 hThermostat =1,则停用一次性加热(15 分钟)定时程序; // 打开恒温器 buzz(1, 1); } if ((tTarget - tempr <=-1 * heatCorrVal) &&(hThermostat)) { hThermostat =0; } if ((temprPrev - tempr>=0.2) &&(!windowAlrt) &&(tempr <=20)) { // 在一个测量周期和采暖季节温度 windowAlrt =1; // 丢弃,它将评估为打开的窗口 sftyTmrEnded =1; for (byte i =1; i <5; i++) { buzz(4, 1);延迟(50); }relayHandlerA(3); // 调用 airing 函数 (opcode =3),帮助刷新空气 } if ((temprPrev - tempr <=-0.12) &&(windowAlrt)) { // tempr.下降结束,空气变热 windowAlrt =0; // 由于环境的热容量,buzz(4, 3); // 所以切换回普通模式relayHandlerA(3); }relayHandlerH(3); // 该函数将检查调用者 param(3) &windowAlrt &measError 布尔值 if (!windowAlrt) { sTX(1); } }}void tTargetHandler (bool set) { // 通过增加或减少来设置需要的 tempr if (!set) { // incr if (tTarget <24) { // 直到达到上限 tTarget++;嗡嗡声(3, 1); } else { 嗡嗡声(2, 3); } } else { // decr if (tTarget> 10) { tTarget--;嗡嗡声(3, 1); } else { 嗡嗡声(2, 3); } } tTargetEEPROM();}void tTargetEEPROM() { EEPROM.write(addr, tTarget); // after incr/decr/set, write the tTarget value to the appropriate byte of the EEPROM delay(10); sTX(2);}void draw(void) { // logo handler if (frame ==0) { u8g.drawXBMP( 0, 0, 128, 64, frame1); } else if (frame ==1) screenFunctState(); } void screenFunctState(void) { // function state screen temprWriteOut(0, 64); u8g.drawHLine(0, 46, 128); u8g.setFont(u8g_font_unifont); if (!windowAlrt) { u8g.setPrintPos( 0, 14); u8g.print(funct_str[0]); u8g.setPrintPos(84, 14); u8g.print(heaterState); } else { u8g.setPrintPos( 0, 14); u8g.print(funct_str[3]); } u8g.setPrintPos( 0, 28); u8g.print(funct_str[1]); u8g.setPrintPos(88, 28); u8g.print(airingState); u8g.setPrintPos( 0, 42); u8g.print(funct_str[2]); u8g.setPrintPos(95, 42); u8g.print(lightingState); if ((!hState) &&(!aState) &&(!lState)) { screenStndby(); // if all of controlled devices are in off, call standby screen }}void screenStndby() { // standby scr u8g.firstPage(); do { u8g.setFontRefHeightText(); u8g.setFont(u8g_font_unifont); if (!measError) { u8g.setPrintPos(33, 52); u8g.print(funct_str[5]); u8g.setPrintPos( 8, 64); u8g.print(funct_str[6]); } else { u8g.setPrintPos( 4, 48); u8g.print(funct_str[4]); } temprWriteOut(0, 16); } while( u8g.nextPage() );}void temprWriteOut (byte tX, byte tY) { // draw tempr &tTarget variables onto different coordinates u8g.setFont(u8g_font_courB14);//u8g.setFont(u8g_font_6x12); // you can save ~10% of prog.memory using this font with 2x2 scale char buftTarget[9]; sprintf (buftTarget, "%d", tTarget); // int to char//u8g.setScale2x2();//tY =tY / 2; u8g.setPrintPos(tX, tY); u8g.print(buftTarget); u8g.setPrintPos(tX+18, tY); u8g.print(funct_str[9]); u8g.setPrintPos(tX+50, tY); u8g.print(tempr); u8g.print(char(176)); u8g.print("C");//u8g.undoScale();}void clrScr(){ u8g.firstPage(); do { } while( u8g.nextPage() );}void ledHandler() { // the brightness of a led is low, if the indicated device is off, and high, if its on if (aState) { analogWrite(ledA, bright[2]); } else { analogWrite(ledA, bright[1]); } if (lState) { analogWrite(ledL, bright[2]); } else { analogWrite(ledL, bright[1]); } if (hState) { if (!hThermostat) { ledBlnk(); // the heater led blinks when the One Time Heating (15 mins) timed mode is activated, } else { brightHeat =bright[2]; // and constant bright, if the thermostat routine is active } } else { brightHeat =bright[1]; } analogWrite(ledH, brightHeat);}void ledBlnk() { unsigned long curr =millis(); if (ledInterval <=curr - prev) { // subtract prev value from current, if the difference equals or greater than ledInterval const. prev =curr; // overwrite the earlier value with the current and flip brightness level if (brightHeat ==bright[1]) { brightHeat =bright[2]; } else { brightHeat =bright[1]; } } analogWrite(ledH, brightHeat);}void buzz(byte b, byte d) { // call with frequency and delay parameters tone(buzzer, b * 1000); delay(d * 100); noTone(buzzer);}void sTX(byte reportTX) { // sending serial reports switch (reportTX) { case 0:for (byte i =0; i <9; i++) { sUART.print(funct_str[10]); } sUART.println(funct_str[10]);休息; case 1:sUART.print(funct_str[8]); // Tempr. sUART.print(tempr); sUART.print(char(176)); sUART.println("C");休息; case 2:sUART.print(funct_str[7]); // TTemp sUART.print(tTarget); sUART.print(char(176)); sUART.println("C");休息; case 3:sUART.print(funct_str[3]); // Window Alert sUART.print(funct_str[9]); sUART.print(tempr); sUART.print(char(176)); sUART.println("C");休息; case 4:sUART.println(funct_str[4]); // Error report break; case 5:sUART.print(funct_str[0]); // Working state of devices sUART.println(heaterState);休息; case 6:sUART.print(funct_str[1]); sUART.println(airingState);休息; case 7:sUART.print(funct_str[2]); sUART.println(lightingState);休息; case 8:// Overview report sTX(0); relayHandlerH(4); relayHandlerA(4); relayHandlerL(4); sTX(2); if (measError) { sTX(4); } 休息; }}
Thermostat v11 - Fahrenheit version.inoArduino
The program code explains itself, useful for beginners. Well commented, describes every important steps;什么,为什么和如何。 Divided into modules by functions, that’s why easy to overview.
Works in Fahrenheit, I changed only the Target temperature values (50-76), and the device codes (1, 2 and 3).
// Fahrenheit version v1.1// Program code of an intelligent Arduino smarthome thermostat solution, // based on Arduino Nano (or higher) board, DS18B20 thermo sensor, HC-05 Bluetooth adapter, I2C 128X64 bicolor OLED display.// The system can be controlled by buttons and Android smartphone via bluetooth.//// It handles the HEATER gas boiler, the bathroom AIRING ventilator and the kitchen LIGHTING - swithed with relays.// The heater has two working ways. 1:One Time Heating (15 mins) timed mode, 2:Thermostat mode (higher priority). The adjusted target tempr.// stored in EEPROM. The program detects hardware errors and window opening - in these cases the heater stops and/or will not start.//// Designed and programmed by Gyula Osi.// All rights reserved.// ------------------------------------------------------------------------------------------------------------------------------------// ---- Display I2C Bus, SDA(TX) -> A4, SCL(RX) -> A5#include "U8glib.h"U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // display constructorbyte frame =0; // start logo pointer// ---- Ports and related declarationsconst byte buzzer =9; // buzzer to D9int btnVal; // stores analog values from buttonsconst byte relayA =11; // airingbool aState =0;bool aStateByCmd; // if "window alert" is gone, the airing control goes back to the original stateconst byte relayL =13; // lightingbool lState =0;const byte relayH =10; // heaterconst byte ledH =6; // PWM ports allow output level adjustment used to change brightnessconst byte ledA =5; const byte ledL =3;const byte bright[3] ={0, 10, 100};byte brightHeat =bright[2];#define ledInterval 1000 // heater led blinking intervalunsigned long prev =0;// ---- Strings for Display and Serial Reports #define STATE_ITEMS 3 // device state indicator strings for outputs// i -> 0 1 2const String state_str[STATE_ITEMS] ={"on.", "off.", "auto."}; String heaterState; String airingState;String lightingState;#define FUNCT_ITEMS 11// i -> 0 1 2 3 4 5 6 7 8 9 10 String funct_str[FUNCT_ITEMS] ={"Heater is ", "Airing is ", "Lighting is ", "Window Alert!", "Hardware Error!", "Hit a Key>>", "or send a Code!", "Target tempr =", "Temperature =", " * ", " -"};// ---- Temperature Measurement and Heater Related Features#include  elapsedMillis timer0; // 8-bit, PWM timer, used by function elapsedMillis()#define sftyTmrInterval 15 * 60000 // one Time Heating (15 mins) timed mode interval [ms]bool sftyTmrEnded; // boolean startup, the timer0 has ended#include byte tTarget; // adjusted target temprconst int addr =0; // the current address of the tTarget variable in the EEPROM memorybool hState =0; // heater boolean statebool hThermostat =0; // thermostat boolean state#include "OneWire.h" #include "DallasTemperature.h"#define DS18B20 2 // setup the OneWire bus on D2OneWire temprWire(DS18B20); // setup DS18B20 to work on the OneWire busDallasTemperature sensors(&temprWire);float tempr; // measured valuefloat temprPrev; // copy of measured value for trend analysisbool windowAlrt =0; // specified degree of tempr dropbool measError =0; // measured tempr value is out of the range specified as normalconst long temprInterval =60000; // cyclic tempr measurement interval [ms]unsigned long temprTmrPrev =0; // the elapsed will be the previous when temprMeas() calledfloat heatCorrVal; // declares the degree of overheating and cooling back, see tempMeas() function// ---- Configuration of Serial Communication on virtual RXD/TXD#include  // SW serial RX &TX pins for HC-05const int RX1 =8;const int TX1 =4;SoftwareSerial sUART(RX1,TX1); char RX[2]; // store received serial databool errMsgSentBySys =0; bool startup =1; // keep avoid a duplicate BT report at startupconst uint8_t frame1[] U8G_PROGMEM ={ // XBM map 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x7F, 0xF0, 0x3F, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFC, 0xFF, 0x7F, 0xF0, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C , 0x3F, 0x1E, 0xFF, 0x00, 0xFE, 0x1F, 0xFF, 0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x7F, 0x8C, 0x3F, 0x1E, 0xFF, 0x00, 0xFE, 0x1F, 0xFF, 0xF1, 0x00, 0x18, 0xC0, 0x8F, 0xFF, 0x1F, 0x1F, 0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE, 0x7F, 0xFC, 0x8F, 0xFF, 0x1F, 0x1F, 0x0C, 0x3E, 0x1E, 0xFF, 0xFF, 0xF8, 0x1F, 0xFF, 0x31, 0xFE, 0x7F, 0xFC, 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0xFC, 0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 0x0F, 0x0E, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0xFC, 0x00, 0xF8, 0x1F, 0x7C, 0x30, 0xFE, 0x7F, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x00, 0x7E, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x0F, 0xFE, 0x18, 0x1F, 0x0C, 0x3E, 0x1E, 0x3C, 0x3E, 0xF8, 0x1F, 0x7C, 0xF0, 0x3F, 0x78, 0xF0, 0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 0x00, 0xF8, 0x7F, 0x00, 0x3C, 0x00, 0x1E, 0xC0, 0x3F, 0x00, 0x7E, 0x00, 0x3C, 0x80, 0x07, 0xF0, 0x00, 0xF8, 0x7F, 0x00, 0x3C, 0x00, 0x1E, 0xC0, 0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x06, 0xE6, 0x3F, 0x06, 0xC6, 0x7F, 0xFE, 0xE7, 0x3F, 0x7E, 0xFE, 0xC7, 0x7F, 0x00, 0x00, 0x30, 0x06, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x06, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x1E, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x30, 0x3E, 0x66, 0x60, 0x06, 0x66, 0x00, 0x60, 0x60, 0x60, 0x30, 0x06, 0x60, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0 xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 0xFE, 0xC3, 0x3F, 0x00, 0x00, 0x3C, 0x7E, 0xE6, 0x61, 0x1E, 0xC7, 0x3F, 0x70, 0xE0, 0x3F, 0x3C, 0xFE, 0xC3, 0x3F, 0x00, 0x00, 0x3C, 0xDE, 0xE7, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x1D, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE7, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x31, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x61, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x3C, 0x1E, 0xE6, 0x61, 0x1E, 0x07, 0x70, 0x70, 0xE0, 0x61, 0x3C, 0x1E, 0x00, 0x70, 0x00, 0x00, 0x7F, 0x1E, 0xE6, 0x3F, 0xFC, 0xE3, 0x3F, 0x70, 0xE0, 0x61, 0x7E, 0xFE, 0xE7, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };void setup() { sUART.begin(9600); pinMode(relayH, OUTPUT); relayHandlerH(4); // get the working state pinMode(ledH, OUTPUT); pinMode(relayA, OUTPUT); relayHandlerA(4); pinMode(ledA, OUTPUT); pinMode(relayL, OUTPUT); relayHandlerL(4); pinMode(ledL, OUTPUT); pinMode(蜂鸣器,输出);传感器开始(); // start DS18B20 temprMeas(); // do not wait for the meas.timer, call the function once at startup tTarget =EEPROM.read(addr); // read the previously stored tTarget value from the current address of the EEPROM at startup startup =0; if (!measError) { sTX(8); // call for instant report of working states after startup } else { sTX(2); }}void loop() { temprTimer(); ledHandler(); btnReadings(); sRX(); safetyTmr(); u8g.firstPage(); // scr loop do { draw(); } while( u8g.nextPage() ); if (frame ==0) { delay(3000); frame =1; clrScr(); }}void btnReadings() { // --------- Btn Readings btnVal =analogRead(A0); // read analog val from A0 if (btnVal>=510 &&btnVal <=516) { // btn Lighting delay(100); // btn debounce buzz(3, 1); relayHandlerL(2); // call proper function with logical state flip opcode as parameter } else if (btnVal>=849 &&btnVal <=855){ // btn Airing if (windowAlrt ==1) { // if the system is in Window Alert mode, disable it windowAlrt =0; buzz(4, 3); relayHandlerA(3); } else { // else turn on/off the airing ventilator delay(100); buzz(3, 1); relayHandlerA(2); } } else if (btnVal>=927 &&btnVal <=933){ // btn One Time Heating (15 mins) timed mode delay(100); buzz(3, 1); relayHandlerH(2); } else if (btnVal>=767 &&btnVal <=777) { // btn decrease tTarget delay(100); tTargetHandler(0); } else if (btnVal>=687 &&btnVal <=697) { // btn increase tTarget delay(100); tTargetHandler(1); } else if (btnVal>=856 &&btnVal <=862) { // inc &dec btns at the same time tTarget =14; // <====initial value - press these buttons at the very first powerup! }}void sRX() { // ------------- Receive Serial Data while (sUART.available()> 0) { // if data is available to read for (byte i =0; i <2; i++) { RX[i] =sUART.read(); } } int iRX[2]; for (byte i =0; i <2; i++) { iRX[i] =RX[i] - '0'; } switch (RX[0]) { // ------------ accept SINGLE ALPHABETICAL control codes case 'A':relayHandlerH(1); // 1 =on break; case 'a':relayHandlerH(0); // 0 =off break; // Received serial data can be a single alphabetical letter from "Arduino case 'B':// Bluetooth Control Device" Android app. If a proper alphabetical relayHandlerA(1); // character arrives, the program code will not wait for the second one, break; // but calls the applicable function with a proper operation code. case 'b':// Cases of combined numeric data can be seen below. relayHandlerA(0);休息; case 'C':relayHandlerL(1);休息; case 'c':relayHandlerL(0);休息; case 'D':case 'd':tTarget =21; tTargetEEPROM(); buzz(3, 1);休息; case 'E':case 'e':tTarget =19; tTargetEEPROM(); buzz(3, 1);休息; case 'F':case 'f':tTarget =14; tTargetEEPROM(); buzz(3, 1);休息; case 'R':// call for an overview report about controlled devices case 'r':sTX(8);休息; case 'W':// disable Window Alert state case 'w':windowAlrt =0; buzz(4, 3); relayHandlerA(3);休息; } // ----------------------- accept COMBINED NUMERIC control codes // In this case a two-digit numeric control code arrives in char format, // from an Android bluetooth serial app for instance. After a char to integer // conversion (only if the first char is '1' or '2') a merge-process will follow, // and the system of conditions and statements will make a decision and execute it. // Appropriate numeric codes are:// // ---------------- Target Temperature:// 50 - 76 values will be accepted as a target temperature for the thermostat function. // // ---------------- Device Control Codes:// First =device code, Second =operator code // 10, 11, 12 turns the lighting:10=off, 11=on // 12=flip logical state (on -> off / off -> on) // 20, 21, 22 will do the same to the airing ventilator // 30, 31, 32 handles the One Time Heating (15 mins) timed heater program as above // // ---------------- Classified Operator Codes:// X3, X4 are classified, used only for function calls by inner program sequences if (RX[0] =='1') { relayHandlerL(iRX[1]); } if (RX[0] =='2') { relayHandlerA(iRX[1]); } if (RX[0] =='3') { relayHandlerH(iRX[1]); } if ((iRX[0] * 10 + iRX[1] <=76) &&(iRX[0] * 10 + iRX[1]>=50)) { // accept only numeric values between 50 &76 tTarget =iRX[0] * 10 + iRX[1]; // merge two integers and set tTarget tTargetEEPROM(); // after set, call the EEPROM handler function, and buzz(3, 1); // write the tTarget value to the appropriate byte of the EEPROM } if (RX[0] =='0') { // test for (byte i =1; i <5; i++) { buzz(5, 2); } } for (byte i =0; i <2; i++) { // empty all message receiver and conversion variables RX[i] ='Z'; } }void relayHandlerL(byte lOperator) { // Lighting Handler Sequence // operators are:0=off, 1=on, 2=flip the state, 4=fill/refill the lighting state char var. if ((measError) &&((lOperator ==1) || (lOperator ==2))) { sTX(4);返回; } if ((lOperator ==2) || (lOperator ==0) &&(lState) || (lOperator ==1) &&(!lState)) { lState =!lState; digitalWrite(relayL, lState); buzz(2, 1); } if (lOperator>=0) { // fill up the working state char with the proper state indicator string if (lState) { lightingState =state_str[0]; } else { lightingState =state_str[1]; } if (!startup) { sTX(7); } }}void relayHandlerA(byte aOperator) { // Airing Handler Sequence if ((measError) &&((aOperator ==1) || (aOperator ==2))) { // operators are:0=off, 1=on, 2=flip the state, sTX(4); // 3=called by temprMeas() funct., 4=fill/refill the airing state char var.返回; } aState =digitalRead(relayA); if (!windowAlrt) { if ((aOperator ==2) || (aState) &&(aOperator ==0) || (!aState) &&(aOperator ==1)) { aState =!aState; digitalWrite(relayA, aState); aStateByCmd =digitalRead(relayA); buzz(2, 1); } } if (aOperator ==3) { // called by the temprMeas() function, 'windowAlrt' ended or started if ((!aState) &&(windowAlrt) || (aState) &&(!windowAlrt) &&(!aStateByCmd)) { digitalWrite(relayA, windowAlrt); } } aState =digitalRead(relayA); if (aOperator>=0) { if (aState) { if (windowAlrt) { airingState =state_str[2]; } else { airingState =state_str[0]; } } else { airingState =state_str[1]; } } if (!startup) { sTX(6); }} void relayHandlerH(byte hOperator) { // Heater Handler Sequence // operators are:0=off, 1=on, 2=flip the state, // 3=called by temprMeas() funct., 4=fill/refill the heater state char var. if ((measError) &&((hOperator ==1) || (hOperator ==2))) { sTX(4);返回; } if ((!hThermostat) &&(!windowAlrt) &&(!measError)) { // turn on/off the One Time Heating (15 mins) timed mode if ((hOperator ==2) || (hOperator ==1) &&(!hState) || (!hOperator) &&(hState)) { buzz(2, 1); hState =!hState; sftyTmrEnded =0; timer0 =0; digitalWrite(relayH, hState); } } if (windowAlrt) { sTX(3); } if (hOperator ==3) { // this function called by the temprMeas() function (op 3) // in order to examine windowAlrt &measError booleans if ((windowAlrt) &&(hState)) { // a window is open and the heater is running digitalWrite(relayH, 0); buzz(5, 3); } if ((!windowAlrt) &&(!measError)) { if ((hThermostat) || (!hThermostat) &&(hState) &&(sftyTmrEnded)) { digitalWrite(relayH, hThermostat); // proceed the command of the Thermostat Routine } } } hState =digitalRead(relayH); if (hOperator>=0) { if (hState) { if (hThermostat) { heaterState =state_str[2]; } else { heaterState =state_str[0]; } } else { heaterState =state_str[1]; } if ((((!windowAlrt) &&(hOperator !=3)) || (hState)) &&(!startup)) { sTX(5); } }}void safetyTmr () { // Timer for the One Time Heating (15 mins timed) mode if ((hState) &&(!sftyTmrEnded) &&(timer0> sftyTmrInterval) &&(!hThermostat)) { sftyTmrEnded =1; relayHandlerH(0); for (byte i =1; i <5; i++) { buzz(i, 2); } }}void temprTimer() { // Cyclic Timer for temprMeas() unsigned long temprTmrCurr =millis(); if (temprInterval <=temprTmrCurr - temprTmrPrev) { temprTmrPrev =temprTmrCurr; temprMeas(); } }void temprMeas() { // ----------- Temperature Measurement &Comparison Sequence temprPrev =tempr; // save the value for next comparison sensors.requestTemperatures(); // update sensor readings tempr =sensors.getTempFByIndex(0); // read remperature if ((tempr>=104) || (tempr <=32)) { // extreme meas values:if (!errMsgSentBySys) { // -127, -196.60 are HW errors, +85 is tipically SW error, but sTX(4); // can be fire, or a broken window } errMsgSentBySys =1; hThermostat =0; if (hState) { relayHandlerH(0); } if (aState) { relayHandlerA(0); } if (lState) { relayHandlerL(0); } measError =1; for (byte i =1; i <10; i++) { buzz(4, 1);延迟(50); } } else { temprPrev =tempr; measError =0; errMsgSentBySys =0; } if (!measError) { // ------------ Start of Temperature Analysis Sequence if (tempr <=62.6) { // Frequent, short-term switching of the heater gas boiler would cut short its lifetime, the heatCorrVal =0.9; // heatCorrVal value helps to keep avoid it. Declares the degree of overheating and cooling back. } // Lower temperature demands greater heatCorrVal, because the walls are colder and adsorb better the if ((tempr> 62.6) &&(tempr <66.2)) { // warmth from the freshly heated-up air, so the above described effect would more effective. heatCorrVal =0.72; } if (tempr>=66.2) { heatCorrVal =0.54; } if (tTarget - tempr>=heatCorrVal) { // subtract measured value from target, if the difference equals or greater than heatCorrVal sftyTmrEnded =1; // deactivate the One Time Heating (15 mins) timed program if it is running hThermostat =1; // turn on the thermostat buzz(1, 1); } if ((tTarget - tempr <=-1 * heatCorrVal) &&(hThermostat)) { hThermostat =0; } if ((temprPrev - tempr>=0.36) &&(!windowAlrt) &&(tempr <=68)) { // in a measurement cycle and in heating season the temperature windowAlrt =1; // drops, it will evaluate as a window is open sftyTmrEnded =1; for (byte i =1; i <5; i++) { buzz(4, 1);延迟(50); } relayHandlerA(3); // call airing function (opcode =3), to help refresh the air } if ((temprPrev - tempr <=-0.216) &&(windowAlrt)) { // the tempr. falling is over, the air became warmer windowAlrt =0; // due to the heat capacity of the environment, buzz(4, 3); // so switch back to normal mode relayHandlerA(3); } relayHandlerH(3); // the function will examine caller param(3) &windowAlrt &measError booleans if (!windowAlrt) { sTX(1); } }}void tTargetHandler (bool set) { // set the needed tempr by increasing or decreasing if (!set) { // incr if (tTarget <76) { // until it reaches the upper limit tTarget++; buzz(3, 1); } else { buzz(2, 3); } } else { // decr if (tTarget> 50) { tTarget--; buzz(3, 1); } else { buzz(2, 3); } } tTargetEEPROM();}void tTargetEEPROM() { EEPROM.write(addr, tTarget); // after incr/decr/set, write the tTarget value to the appropriate byte of the EEPROM delay(10); sTX(2);}void draw(void) { // logo handler if (frame ==0) { u8g.drawXBMP( 0, 0, 128, 64, frame1); } else if (frame ==1) screenFunctState(); } void screenFunctState(void) { // function state screen temprWriteOut(0, 64); u8g.drawHLine(0, 46, 128); u8g.setFont(u8g_font_unifont); if (!windowAlrt) { u8g.setPrintPos( 0, 14); u8g.print(funct_str[0]); u8g.setPrintPos(84, 14); u8g.print(heaterState); } else { u8g.setPrintPos( 0, 14); u8g.print(funct_str[3]); } u8g.setPrintPos( 0, 28); u8g.print(funct_str[1]); u8g.setPrintPos(88, 28); u8g.print(airingState); u8g.setPrintPos( 0, 42); u8g.print(funct_str[2]); u8g.setPrintPos(95, 42); u8g.print(lightingState); if ((!hState) &&(!aState) &&(!lState)) { screenStndby(); // if all of controlled devices are in off, call standby screen }}void screenStndby() { // standby scr u8g.firstPage(); do { u8g.setFontRefHeightText(); u8g.setFont(u8g_font_unifont); if (!measError) { u8g.setPrintPos(33, 52); u8g.print(funct_str[5]); u8g.setPrintPos( 8, 64); u8g.print(funct_str[6]); } else { u8g.setPrintPos( 4, 48); u8g.print(funct_str[4]); } temprWriteOut(0, 16); } while( u8g.nextPage() );}void temprWriteOut (byte tX, byte tY) { // draw tempr &tTarget variables onto different coordinates u8g.setFont(u8g_font_courB14);//u8g.setFont(u8g_font_6x12); // you can save ~10% of prog.memory using this font with 2x2 scale char buftTarget[9]; sprintf (buftTarget, "%d", tTarget); // int to char//u8g.setScale2x2();//tY =tY / 2; u8g.setPrintPos(tX, tY); u8g.print(buftTarget); u8g.setPrintPos(tX+18, tY); u8g.print(funct_str[9]); u8g.setPrintPos(tX+50, tY); u8g.print(tempr); //u8g.print(char(176)); u8g.print("F");//u8g.undoScale();}void clrScr(){ u8g.firstPage(); do { } while( u8g.nextPage() );}void ledHandler() { // the brightness of a led is low, if the indicated device is off, and high, if its on if (aState) { analogWrite(ledA, bright[2]); } else { analogWrite(ledA, bright[1]); } if (lState) { analogWrite(ledL, bright[2]); } else { analogWrite(ledL, bright[1]); } if (hState) { if (!hThermostat) { ledBlnk(); // the heater led blinks when the One Time Heating (15 mins) timed mode is activated, } else { brightHeat =bright[2]; // and constant bright, if the thermostat routine is active } } else { brightHeat =bright[1]; } analogWrite(ledH, brightHeat);}void ledBlnk() { unsigned long curr =millis(); if (ledInterval <=curr - prev) { // subtract prev value from current, if the difference equals or greater than ledInterval const. prev =curr; // overwrite the earlier value with the current and flip brightness level if (brightHeat ==bright[1]) { brightHeat =bright[2]; } else { brightHeat =bright[1]; } } analogWrite(ledH, brightHeat);}void buzz(byte b, byte d) { // call with frequency and delay parameters tone(buzzer, b * 1000); delay(d * 100); noTone(buzzer);}void sTX(byte reportTX) { // sending serial reports switch (reportTX) { case 0:for (byte i =0; i <9; i++) { sUART.print(funct_str[10]); } sUART.println(funct_str[10]);休息; case 1:sUART.print(funct_str[8]); // Tempr. sUART.print(tempr); //sUART.print(char(176)); sUART.println("F");休息; case 2:sUART.print(funct_str[7]); // TTemp sUART.print(tTarget); //sUART.print(char(176)); sUART.println("F");休息; case 3:sUART.print(funct_str[3]); // Window Alert sUART.print(funct_str[9]); sUART.print(tempr); //sUART.print(char(176)); sUART.println("F");休息; case 4:sUART.println(funct_str[4]); // Error report break; case 5:sUART.print(funct_str[0]); // Working state of devices sUART.println(heaterState);休息; case 6:sUART.print(funct_str[1]); sUART.println(airingState);休息; case 7:sUART.print(funct_str[2]); sUART.println(lightingState);休息; case 8:// Overview report sTX(0); relayHandlerH(4); relayHandlerA(4); relayHandlerL(4); sTX(2); if (measError) { sTX(4); } 休息; }}

示意图

For jumper wire test... You have to solder it

制造工艺

  1. 带二维码、RFID 和温度验证的访问控制
  2. 使用 Arduino、1Sheeld 和 Android 的通用远程控制
  3. 使用 Arduino 控制硬币接收器
  4. 带有 Arduino 的自动植物浇水系统
  5. Arduino 带蓝牙控制 LED!
  6. 智能鞋(自动系带和发电)
  7. Arduino Nano:使用操纵杆控制 2 个步进电机
  8. 使用 Arduino 和 MPU6050 控制伺服电机
  9. 带有 OK Google 的蓝牙语音控制设备
  10. 使用 Arduino Uno 和蓝牙进行汽车控制
  11. 带智能显示屏的智能电池充电器多路复用器
  12. 带有滚动文本的 Arduino 蓝牙控制矩阵