├── .gitignore ├── README.md └── yelle-remote.ino /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | config.h 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yelle-remote 2 | Connected air-conditioner remote of project Yelle 3 | 4 | Yelle is a project developed in [Fikrimuhal](http://fikrimuhal.com) Head Quarters. It allows the team to control air-conditioning system via slack messages. 5 | 6 | ## Intro 7 | 8 | This program runs on chip esp8266 and with the help of [Arduino core for esp8266](https://github.com/esp8266/Arduino), it's developed in Arduino environment. 9 | 10 | yelle-remote currently runs in [NodeMCU](http://nodemcu.com/index_en.html) which is an esp8266 based development board. However it's supposed to run in other esp8266 boards as well. 11 | 12 | 13 | ## How to run yelle-remote? 14 | 15 | To run the code you should follow these steps: 16 | 17 | #### 1. Download the latest Arduino IDE 18 | 19 | You can download the Arduino IDE to your computer from this link: https://www.arduino.cc/en/Main/Software 20 | 21 | #### 2. Install ESP8266 Core for Arduino 22 | 23 | The easiest way is to use Boards Manager. You can follow these instructions the install it: https://github.com/esp8266/Arduino#installing-with-boards-manager 24 | 25 | #### 3. Download IRremoteESP8266 Library 26 | 27 | [This](https://github.com/markszabo/IRremoteESP8266) library provides a very easy to use interface to receive and send infrared waves via your ESP chip. It also has protocol implementations and command mappings for lots of brands. 28 | 29 | So download this library and add to your Arduino sketch using Library Manager or any method you want. You can follow this instruction set in case you have trouble adding the library: https://www.arduino.cc/en/Guide/Libraries 30 | 31 | #### 4. Add config.h 32 | 33 | Create a file named config.h and put it beside of yelle-remote.ino. This file should include the following lines: 34 | 35 | ```c 36 | #define WIFI_SSID "your_ssid" 37 | #define WIFI_PASS "your_password" 38 | #define SERVER_NAME "your.domain.com" 39 | #define SERVER_PORT "80" 40 | ``` 41 | 42 | #### 5. Run yelle-remote.ino 43 | 44 | Select your board, port and do other configuration. Now, you should be ready to go with yelle-remote! 45 | 46 | ## How to setup the hardware? 47 | 48 | ### Used Components 49 | * NodeMCU v1.0 50 | * IR LED (SB-5010IRB-CT) x2 51 | * General purpose transistor (NPN) 52 | * 10R Resistor (could differ according to transistor and IR LED of your choice) 53 | * Power adapter (or any kind of 5V power source) 54 | * Breadboard, jumpers, cables, etc. 55 | 56 | ### Wiring 57 | Here is the wiring scheme of the hardware: 58 | ![Wiring scheme of the hardware][wiring-scheme] 59 | 60 | Please note that Vcc is connected to NodeMCU's Vin and GND in emitter of the transistor is connected to NodeMCU's GND. I kept it this way to make the scheme easy on the eye. 61 | 62 | Node mcu is powered via a usb power adapter. 63 | 64 | ![Photo of yelle-remote][yelle-remote] 65 | 66 | 67 | [wiring-scheme]: https://cloud.githubusercontent.com/assets/4990386/18359557/1a5dd128-7603-11e6-842c-8a2fe17c6484.png 68 | [yelle-remote]: https://cloud.githubusercontent.com/assets/4990386/18359343/30fd484c-7602-11e6-8ac5-7f7c4dad7549.png 69 | -------------------------------------------------------------------------------- /yelle-remote.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * yelle-remote.ino 3 | * 4 | * An arduino program runs on ESP8266 (NodeMCU) 5 | * which controls an Arcelik Air Conditioner 6 | * sending proper IR signals. 7 | * 8 | * This program pulls the last command from server once in a while and executes it converting to IR signals. 9 | * 10 | * @author Ahmet Safa Orhan (http://github.com/safaorhan) 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "config.h" 18 | 19 | // Hardware config 20 | const int IR_LED = 5; 21 | 22 | // Sensitive info 23 | char ssid[] = WIFI_SSID; // Network SSID (name) [config.h] 24 | char pass[] = WIFI_PASS; // Network password [config.h] 25 | char server[] = SERVER_NAME; // Server address [config.h] 26 | int server_port = SERVER_PORT; // Server port [config.h] 27 | 28 | // App. logic 29 | const unsigned long postingInterval = 2L * 1000L; // delay between updates, in milliseconds 30 | 31 | // API Constants 32 | const int CMD_OFF = 100; // NO PARAMS 33 | const int CMD_ON = 101; // NO PARAMS 34 | const int CMD_TEMP = 102; // TWO DIGIT TEMPERATURE 35 | const int CMD_HEAT = 103; // PARAM_COOLING, PARAM_HEATING 36 | const int PARAM_COOLING = 0; // CMD_HEAT 37 | const int PARAM_HEATING = 1; // CMD_HEAT 38 | 39 | unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds 40 | 41 | WiFiClient client; 42 | IRsend irsend(IR_LED); 43 | 44 | 45 | /* 46 | * ---- BEGINNING of air-conditioner IR code generation 47 | * 48 | * It's generally for LG AC. 49 | * However basic functions work for Arcelik AC as well. 50 | * 51 | * Thanks chaeplin (https://github.com/chaeplin) for this example: 52 | * https://github.com/z3t0/Arduino-IRremote/blob/master/examples/LGACSendDemo/LGACSendDemo.ino 53 | */ 54 | 55 | const int AC_TYPE = 0; 56 | // 0 : TOWER 57 | // 1 : WALL 58 | 59 | int AC_HEAT = 0; 60 | // 0 : cooling 61 | // 1 : heating 62 | 63 | int AC_POWER_ON = 0; 64 | // 0 : off 65 | // 1 : on 66 | 67 | int AC_AIR_ACLEAN = 0; 68 | // 0 : off 69 | // 1 : on --> power on 70 | 71 | int AC_TEMPERATURE = 27; 72 | // temperature : 18 ~ 30 73 | 74 | int AC_FLOW = 1; 75 | // 0 : low 76 | // 1 : mid 77 | // 2 : high 78 | // if AC_TYPE =1, 3 : change 79 | // 80 | 81 | 82 | const int AC_FLOW_TOWER[3] = {0, 4, 6}; 83 | const int AC_FLOW_WALL[4] = {0, 2, 4, 5}; 84 | 85 | unsigned long AC_CODE_TO_SEND; 86 | 87 | void ac_send_code(unsigned long code) 88 | { 89 | Serial.print("code to send : "); 90 | Serial.print(code, BIN); 91 | Serial.print(" : "); 92 | Serial.println(code, HEX); 93 | 94 | irsend.sendLG(code, 28); 95 | } 96 | 97 | 98 | int lastTemperature = 24; 99 | 100 | void ac_activate() { 101 | ac_activate(lastTemperature, 1); 102 | } 103 | 104 | void ac_activate(int temperature, int air_flow) 105 | { 106 | lastTemperature = temperature; 107 | 108 | int AC_MSBITS1 = 8; 109 | int AC_MSBITS2 = 8; 110 | int AC_MSBITS3 = 0; 111 | int AC_MSBITS4 ; 112 | if ( AC_HEAT == 1 ) { 113 | // heating 114 | AC_MSBITS4 = 4; 115 | } else { 116 | // cooling 117 | AC_MSBITS4 = 0; 118 | } 119 | int AC_MSBITS5 = temperature - 15; 120 | int AC_MSBITS6 ; 121 | 122 | if ( AC_TYPE == 0) { 123 | AC_MSBITS6 = AC_FLOW_TOWER[air_flow]; 124 | } else { 125 | AC_MSBITS6 = AC_FLOW_WALL[air_flow]; 126 | } 127 | 128 | int AC_MSBITS7 = (AC_MSBITS3 + AC_MSBITS4 + AC_MSBITS5 + AC_MSBITS6) & B00001111; 129 | 130 | AC_CODE_TO_SEND = AC_MSBITS1 << 4 ; 131 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS2) << 4; 132 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS3) << 4; 133 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS4) << 4; 134 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS5) << 4; 135 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS6) << 4; 136 | AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS7); 137 | 138 | ac_send_code(AC_CODE_TO_SEND); 139 | 140 | AC_POWER_ON = 1; 141 | AC_TEMPERATURE = temperature; 142 | AC_FLOW = air_flow; 143 | } 144 | 145 | void ac_change_air_swing(int air_swing) 146 | { 147 | if ( AC_TYPE == 0) { 148 | if ( air_swing == 1) { 149 | AC_CODE_TO_SEND = 0x881316B; 150 | } else { 151 | AC_CODE_TO_SEND = 0x881317C; 152 | } 153 | } else { 154 | if ( air_swing == 1) { 155 | AC_CODE_TO_SEND = 0x8813149; 156 | } else { 157 | AC_CODE_TO_SEND = 0x881315A; 158 | } 159 | } 160 | 161 | ac_send_code(AC_CODE_TO_SEND); 162 | } 163 | 164 | void ac_power_down() 165 | { 166 | AC_CODE_TO_SEND = 0x88C0051; 167 | 168 | ac_send_code(AC_CODE_TO_SEND); 169 | 170 | AC_POWER_ON = 0; 171 | } 172 | 173 | //This is not used (It's kept for future reference) 174 | 175 | //void ac_air_clean(int air_clean) 176 | //{ 177 | // if ( air_clean == 1) { 178 | // AC_CODE_TO_SEND = 0x88C000C; 179 | // } else { 180 | // AC_CODE_TO_SEND = 0x88C0084; 181 | // } 182 | // 183 | // ac_send_code(AC_CODE_TO_SEND); 184 | // 185 | // AC_AIR_ACLEAN = air_clean; 186 | //} 187 | 188 | 189 | // ---- END of air-conditioner code generation part. 190 | 191 | /** 192 | * Interprets a command in format TIMESTAMP_COMMAND_PARAM 193 | * and executes it. 194 | * 195 | * @param s Command to be executed. 196 | */ 197 | void executeCommand(String s) { 198 | 199 | int index1 = s.indexOf('_'); 200 | int index2 = s.lastIndexOf('_'); 201 | 202 | String timestamp = s.substring(0, index1); 203 | int cmd = s.substring(index1 + 1, index2).toInt(); 204 | int param = s.substring(index2 + 1).toInt(); 205 | 206 | Serial.println("TIME: " + timestamp); 207 | Serial.println("CMD: " + cmd); 208 | Serial.println("PARAM: " + param); 209 | 210 | switch (cmd) { 211 | case CMD_OFF: 212 | ac_power_down(); 213 | break; 214 | case CMD_ON: 215 | ac_activate(); 216 | break; 217 | case CMD_TEMP: 218 | ac_activate(param, 1); 219 | break; 220 | case CMD_HEAT: 221 | AC_HEAT = param == PARAM_COOLING ? 0 : 1; 222 | ac_activate(); 223 | break; 224 | default: 225 | Serial.println("INVALID CODE"); 226 | digitalWrite(16, LOW); delay(800); 227 | break; 228 | } 229 | 230 | digitalWrite(16, LOW); delay(200); 231 | digitalWrite(16, HIGH); 232 | } 233 | 234 | 235 | /** 236 | * Makes a GET request to given endpoint. It appends param and value to the query. 237 | * 238 | * Example: GET /endpoint?param=value HTTP/1.1 239 | */ 240 | void httpRequest(String endpoint, String param, String value) { 241 | // close any connection before send a new request. 242 | // This will free the socket on the WiFi shield 243 | client.stop(); 244 | 245 | // if there's a successful connection: 246 | if (client.connect(server, server_port)) { 247 | Serial.print("*"); 248 | // send the HTTP PUT request: 249 | client.print("GET " + endpoint); 250 | client.print("?" + param + "=" + value); 251 | client.println(" HTTP/1.1"); 252 | client.println("User-Agent: ArduinoWiFi/1.1"); 253 | client.println("Connection: close"); 254 | client.println(); 255 | 256 | // note the time that the connection was made: 257 | lastConnectionTime = millis(); 258 | } else { 259 | // if you couldn't make a connection: 260 | Serial.println("connection failed"); 261 | delay(1000); 262 | } 263 | } 264 | 265 | //This is for debugging purposes 266 | void printWifiStatus() { 267 | // print the SSID of the network you're attached to: 268 | Serial.print("SSID: "); 269 | Serial.println(WiFi.SSID()); 270 | 271 | // print your WiFi shield's IP address: 272 | IPAddress ip = WiFi.localIP(); 273 | Serial.print("IP Address: "); 274 | Serial.println(ip); 275 | 276 | // print the received signal strength: 277 | long rssi = WiFi.RSSI(); 278 | Serial.print("signal strength (RSSI):"); 279 | Serial.print(rssi); 280 | Serial.println(" dBm"); 281 | } 282 | 283 | 284 | // If postingInterval amount of time passed since lastConnection time, 285 | // make an httpRequest to server. 286 | void makeRequestIfItsTime() { 287 | if (millis() - lastConnectionTime > postingInterval) { 288 | httpRequest("/last-command", "test", "1"); 289 | } 290 | } 291 | 292 | 293 | // If last command (response of the last GET request) is changed, execute it 294 | String lastCommand = ""; 295 | void queryLastCommandIfAvailable() { 296 | String command = ""; 297 | 298 | while (client.available()) { 299 | command = client.readStringUntil('\r'); 300 | command.trim(); 301 | } 302 | 303 | if (command != "" && command != lastCommand) { 304 | Serial.print("New command: "); 305 | Serial.println(command); 306 | lastCommand = command; 307 | executeCommand(command); 308 | } 309 | } 310 | 311 | //If WiFi connection is interrupted or ended somehow, try to reconnect. 312 | void connectToWifiIfNeeded() { 313 | if (WiFi.status() != WL_CONNECTED) { 314 | Serial.print("Attempting to connect to SSID: "); 315 | Serial.println(ssid); 316 | WiFi.begin(ssid, pass); 317 | while (WiFi.status() != WL_CONNECTED) { 318 | Serial.print("."); 319 | delay(500); 320 | } 321 | // you're connected now, so print out the status: 322 | printWifiStatus(); 323 | } 324 | } 325 | 326 | void setup() 327 | { 328 | Serial.begin(115200); 329 | irsend.begin(); 330 | pinMode(16, OUTPUT); 331 | delay(10); 332 | } 333 | 334 | void loop() 335 | { 336 | connectToWifiIfNeeded(); 337 | makeRequestIfItsTime(); 338 | queryLastCommandIfAvailable(); 339 | } 340 | --------------------------------------------------------------------------------