Guest User

Untitled

a guest
Dec 18th, 2016
150
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.03 KB | None | 0 0
  1. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. // Reference code for controlling Electra air-conditioners using Arduino.
  3. // The code uses (and is based on) the "IRremote Arduino Library".
  4. // Written by Zigi Walter (zigiwalter@gmail.com).
  5. // Version: 0.7, December 2016.
  6. //
  7. // Library required: IRremote (http://z3t0.github.io/Arduino-IRremote/)
  8. //
  9. // Hardware setup:
  10. // (1) Wire an IR LED to PIN 3 of the Arduino, using an appropriate resistor (or using a transistor);
  11. // (2) Open a serial port connection to the Arduino board (e.g., using Arduino's IDE "Serial Monitor").
  12. // Configure speed to 9600bps, and terminate transimissions with a "Cariage Return" character.
  13. //
  14. // Controlling the AC using serial port commands:
  15. // Commands are of the form "Hnn", where 'H' is a property, and 'nn' is the numerical value of the property.
  16. // Possible values for "H": P (Power), M (Mode - heat/cool/etc.), F (Fan speed), I (tIlt control), T (Temperature),
  17. // S (Sleep), N (oN timer), O (Off timer). See acStatus_t below for more details.
  18. // A few examples:
  19. // "P1" - Toggle the AC on or off;
  20. // "T10" - Set temprature to (minimum+10) degress;
  21. // "F3" - Set fan speed to "auto";
  22. // "N1" - Turn AC on in 10 minutes;
  23. // "O7" - Turn AC off in 70 minutes;
  24. // For debug only,"X0/X1" disables/enables IR transimission; "U01" dumps the status word to the serial port.
  25. //
  26. // See below code for further details.
  27. // Bug reports and comments: zigiwalter@gmail.com
  28. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  29.  
  30. #include <IRremote.h>
  31.  
  32. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  33. // Global definitions (should be moved to a header file)
  34. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  35. const int STATUS_PIN = 13;
  36.  
  37. #define M_BIT_DURATION_IN_USEC (976-25) //Duration of a logical "1"
  38. #define S_BIT_DURATION_IN_USEC (976+30) //Duration of a logical "0"
  39. #define M_TOTAL_DURATION_CORRECTION (103) //Correction factor due to inaccuracies in generation
  40. #define S_TOTAL_DURATION_CORRECTION (-130) //Correction factor due to inaccuracies in generation
  41. #define BASE_TEMPERATURE (15)
  42.  
  43. #define DATA_STREAM_SIZE (3+3+32*2+2*2)
  44. #define DURATION_ARRAY_SIZE (DATA_STREAM_SIZE*2+1)
  45. #define INPUT_ARRAY_SIZE (4)
  46.  
  47. typedef enum {
  48. FAN_LOW = 0,
  49. FAN_MED,
  50. FAN_HIGH,
  51. FAN_AUTO,
  52. } fanSpeed_e;
  53.  
  54. typedef enum {
  55. MODE_COOL = 1,
  56. MODE_HEAT,
  57. MODE_AUTO,
  58. MODE_DRY,
  59. MODE_FAN
  60. } mode_e;
  61.  
  62. typedef enum {
  63. TX_OFF = 0,
  64. TX_ON
  65. } txMode_e;
  66.  
  67. #pragma pack(push)
  68. #pragma pack(1)
  69. /////////////////////////////////////////////////////////
  70. // AC Status Control Word
  71. /////////////////////////////////////////////////////////
  72. typedef union{
  73. struct{
  74. uint32_t offMinutes :3; //Off timer control: tens of minutes to wait. Range: 0-5
  75. uint32_t offHours :5; //Off timer control: hours to wait. Range: 0-23
  76. uint32_t onMinutes :3; //On time control: tens of minutes to wait. Range: 0-5
  77. uint32_t onHours :5; //On time control: hours to wait. Range: 0-23
  78. uint32_t sleep :1; //Sleep mode. On(1)/off(0)
  79. uint32_t temprature :4; //Temperature. Range: 0(min)-15(max)
  80. uint32_t reserved1 :2; //Unused?
  81. uint32_t tilt :1; //Tilt control. On(1)/off(0)
  82. uint32_t tilt2 :1; //Tile2/Unused?
  83. uint32_t reserved0 :1; //Unused?
  84. uint32_t fanSpeed :2; //Fan speed. Min(0)/Med(1)/MAX(2)/AUTO(3)
  85. uint32_t mode :3; //Mode select. COOL(1)/HEAT(2)/AUTO(3)/DRY(4)/FAN(5)
  86. uint32_t power :1; //Toggle On/off. Toggle(1)/No change(0)
  87. };
  88. uint32_t rawVal;
  89. } acStatus_t;
  90.  
  91. /////////////////////////////////////////////////////////
  92. // General program control
  93. /////////////////////////////////////////////////////////
  94. typedef struct{
  95. uint8_t tx; //Enable(1)/disable(0) IR transmission
  96. } progStatus_t;
  97. #pragma pack(pop)
  98.  
  99.  
  100. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  101. // Global variables
  102. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  103. IRsend irsend;
  104. acStatus_t acStatus={0,0,0,0,0,(28-BASE_TEMPERATURE),0,0,0,0,FAN_LOW, MODE_HEAT,0}; //Initialize default status
  105. progStatus_t progStatus={TX_ON};
  106.  
  107. volatile boolean stringComplete = false; //Global flag for CMD parsing
  108. byte inputArray[INPUT_ARRAY_SIZE+1]={0}; //Buffer for CMD storage. Sets a trailing "0" to end strings
  109. uint8_t dataStream[DATA_STREAM_SIZE]={}; //Buffer for manchester code generation
  110. unsigned int rawCodes[DURATION_ARRAY_SIZE]; //Buffer for raw mark/space durations to be sent
  111.  
  112.  
  113. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  114. // Send the IR code
  115. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  116. void sendCode(uint32_t codeLen) {
  117. // Assume 38 KHz
  118. irsend.sendRaw(rawCodes, codeLen, 38);
  119. }
  120.  
  121.  
  122. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  123. // Converts the AC Status DWORD to a '01/10' Manchester Code stream, adding the required prefix and suffix
  124. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  125. uint8_t statusToDataStream(uint8_t* dataStream, acStatus_t& acStatus){
  126. uint8_t bitBuffer[32];
  127. uint32_t value=acStatus.rawVal;
  128. for (uint8_t i=0;i<32;i++){
  129. uint32_t bitVal=((value&0x80000000)==0)?0:1;
  130. bitBuffer[i]=bitVal;
  131. value=(value<<1);
  132. }
  133.  
  134. //Write prefix
  135. uint8_t idx=0;
  136. dataStream[idx++]=1;
  137. dataStream[idx++]=1;
  138. dataStream[idx++]=1;
  139. dataStream[idx++]=0;
  140. dataStream[idx++]=0;
  141. dataStream[idx++]=0;
  142. //Write actaul status
  143. for(uint8_t i=0;i<32;i++){
  144. uint8_t bitVal=bitBuffer[i];
  145. dataStream[idx++] = (bitVal==0)?1:0;
  146. dataStream[idx++] = (bitVal==0)?0:1;
  147. }
  148. //Write Suffix
  149. dataStream[idx++]=0;
  150. dataStream[idx++]=1;
  151. dataStream[idx++]=1;
  152. dataStream[idx++]=0;
  153.  
  154. return(idx);
  155. }
  156.  
  157.  
  158. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  159. // Converts the the '01/10' Manchester Code data stream to a "Raw Durations Buffer", as required by IRSend
  160. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. uint8_t dataStreamToDurations(uint8_t* srcData, uint8_t len){
  162. //Assuming data stream begins with HIGH
  163. uint8_t smIdx=0;
  164. uint32_t duration=M_BIT_DURATION_IN_USEC;
  165. for (uint8_t streamIdx=1;streamIdx<len;streamIdx++){
  166. if (smIdx==RAWBUF){
  167. break;
  168. }
  169. if (srcData[streamIdx]==srcData[streamIdx-1]){ //No change, accumulate duration
  170. if (srcData[streamIdx]==1){
  171. duration+=M_BIT_DURATION_IN_USEC;
  172. }else{
  173. duration+=S_BIT_DURATION_IN_USEC;
  174. }
  175. }else{
  176. if (srcData[streamIdx]==1){ //Level changed, save total duration
  177. rawCodes[smIdx]=duration+M_TOTAL_DURATION_CORRECTION;
  178. duration=M_BIT_DURATION_IN_USEC;
  179. }else{
  180. rawCodes[smIdx]=duration+S_TOTAL_DURATION_CORRECTION;;
  181. duration=S_BIT_DURATION_IN_USEC;
  182. }
  183. smIdx++;
  184.  
  185. }
  186. if(smIdx>=DURATION_ARRAY_SIZE){
  187. Serial.print("Cannot create MARK/SPACE duration array"); //This should not happen
  188. return(smIdx);
  189. }
  190. }
  191. rawCodes[smIdx]=duration; //Save last duration
  192. return(smIdx+1);
  193. }
  194.  
  195.  
  196. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  197. // Dump a raw duration buffer to the serial port (for debug)
  198. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  199. void printCode(unsigned int * durationBuff, uint8_t len){
  200. for (int i = 0; i < len; i++) {
  201. if ((i % 2)==0) {
  202. // Mark
  203. Serial.print(" m");
  204. }else {
  205. // Space
  206. Serial.print(" s");
  207. }
  208. Serial.print(durationBuff[i], DEC);
  209. }
  210. Serial.println("");
  211. return;
  212. }
  213.  
  214.  
  215. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  216. // Dump the AC Status to the serial port
  217. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  218. void printStatus(){
  219. Serial.print("[P:");
  220. Serial.print(acStatus.power);
  221. Serial.print(" M:");
  222. Serial.print(acStatus.mode);
  223. Serial.print(" F:");
  224. Serial.print(acStatus.fanSpeed);
  225. Serial.print(" L:");
  226. Serial.print(acStatus.tilt2);
  227. Serial.print(" I:");
  228. Serial.print(acStatus.tilt);
  229. Serial.print(" T:");
  230. Serial.print(acStatus.temprature);
  231. Serial.print(" S:");
  232. Serial.print(acStatus.sleep);
  233. Serial.print(" oN:");
  234. Serial.print(acStatus.onHours);
  235. Serial.print(":");
  236. Serial.print(acStatus.onMinutes);
  237. Serial.print(" Off:");
  238. Serial.print(acStatus.offHours);
  239. Serial.print(":");
  240. Serial.print(acStatus.offMinutes);
  241. Serial.print("][0x");
  242. Serial.print(acStatus.rawVal,HEX);
  243. Serial.print("][tX:");
  244. Serial.print(progStatus.tx);
  245. Serial.println("]");
  246. }
  247.  
  248.  
  249. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  250. // Setup the serial communication, pin modes, etc.
  251. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  252. void setup()
  253. {
  254. Serial.begin(9600);
  255. pinMode(STATUS_PIN, OUTPUT);
  256. Serial.println("Runnning ac_rc");
  257. printStatus();
  258.  
  259. }
  260.  
  261.  
  262. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  263. // Main loop: Wait for CMD from serial port, then send IR code
  264. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  265. void loop() {
  266. /////////////////////////////////////////////////////////
  267. // Step 0: Parse CMD from serial port
  268. /////////////////////////////////////////////////////////
  269. if(stringComplete!=0){ //Wait for control word from serial port
  270. stringComplete = false;
  271.  
  272. uint8_t cmd=inputArray[0]; //Get control character
  273. uint8_t cmdVal=atoi((const char*)&inputArray[1]); //Get command value
  274. Serial.print("CMD:");
  275. Serial.println((char*)inputArray);
  276. acStatus.power=0; //The "Power" property is momentary, needs to be reset to avoid unintended on/off toggle
  277. switch(cmd){
  278. case 'P':
  279. acStatus.power=1;
  280. break;
  281. case 'M':
  282. acStatus.mode=cmdVal;
  283. break;
  284. case 'F':
  285. acStatus.fanSpeed=cmdVal;
  286. break;
  287. case 'L':
  288. acStatus.tilt2=cmdVal;
  289. break;
  290. case 'I':
  291. acStatus.tilt=cmdVal;
  292. break;
  293. case 'T':
  294. acStatus.temprature=cmdVal;
  295. break;
  296. case 'S':
  297. acStatus.sleep=cmdVal;
  298. break;
  299. case 'N':
  300. acStatus.onHours=cmdVal/6;
  301. acStatus.onMinutes=cmdVal%6;
  302. break;
  303. case 'O':
  304. acStatus.offHours=cmdVal/6;
  305. acStatus.offMinutes=cmdVal%6;
  306. break;
  307. case 'X':
  308. progStatus.tx=cmdVal;
  309. printStatus();
  310. return; //Don't continue
  311. break;
  312. case 'U':
  313. printStatus();
  314. return; //Don't continue
  315. break;
  316. default:
  317. Serial.println("Error decoding CMD!");
  318. return; //Don't continue past here...
  319. }
  320. printStatus();
  321.  
  322. /////////////////////////////////////////////////////////
  323. // Step 1: Create data stream from status
  324. /////////////////////////////////////////////////////////
  325. uint8_t dataLen=statusToDataStream(dataStream,acStatus); //Create a data stream from the status
  326. if (dataLen!=DATA_STREAM_SIZE){
  327. Serial.print("Error! Unexpected code length (");
  328. Serial.print(dataLen);
  329. Serial.println(")");
  330. return;
  331. }
  332. //At this point, dataLen=74
  333.  
  334. /////////////////////////////////////////////////////////
  335. // Step 2: Convert data stream to durations array
  336. /////////////////////////////////////////////////////////
  337. uint8_t len=dataStreamToDurations(dataStream,dataLen);
  338. //printCode(rawCodes,len); //Dump duration array to serial port
  339.  
  340. /////////////////////////////////////////////////////////
  341. // Step 3: Send the IR code.
  342. // Data is transmitted six times:
  343. // {data,data,data,BREAK,data,data,data}
  344. /////////////////////////////////////////////////////////
  345.  
  346. if (progStatus.tx!=0){
  347. Serial.print("Sending code...");
  348. digitalWrite(STATUS_PIN, HIGH);
  349. //Send data 3 times
  350. for (uint8_t i=0;i<3;i++){ //Data needs to be send 3 times
  351. sendCode(len);
  352. }
  353. //Send BREAK
  354. irsend.mark(4*M_BIT_DURATION_IN_USEC+M_TOTAL_DURATION_CORRECTION);
  355. irsend.space(33370);
  356. //Send data 3 times more
  357. for (uint8_t i=0;i<3;i++){
  358. sendCode(len);
  359. }
  360. digitalWrite(STATUS_PIN, LOW);
  361. Serial.println("Done!");
  362. }
  363. Serial.println("");
  364. }//stringComplete
  365. }
  366.  
  367.  
  368. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  369. // Process incoming serial communication.
  370. // Flag "complete" when end of string isdetected.
  371. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  372. void serialEvent(){
  373. static byte inputPos=0;
  374. while (Serial.available()) {
  375. // get the new byte:
  376. char inChar = (char)Serial.read();
  377. inputArray[inputPos]=inChar;
  378. inputPos=(inputPos+1)%INPUT_ARRAY_SIZE;
  379. // if the incoming character is a newline or a carriage return, set a flag
  380. // so the main loop can do something about it:
  381. if (inChar == '\n' || inChar == '\r' ||inChar == 0) {
  382. inputPos=0;
  383. stringComplete=1;
  384. }else{
  385. stringComplete=0;
  386. }
  387. }
  388. }
Add Comment
Please, Sign In to add comment