ir_Fujitsu.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. // Copyright 2017 Jonny Graham, David Conran
  2. #include "ir_Fujitsu.h"
  3. #include <algorithm>
  4. #ifndef ARDUINO
  5. #include <string>
  6. #endif
  7. #include "IRsend.h"
  8. #include "IRutils.h"
  9. // Fujitsu A/C support added by Jonny Graham & David Conran
  10. // Equipment it seems compatible with:
  11. // * Fujitsu ASYG30LFCA with remote AR-RAH2E
  12. // * Fujitsu AST9RSGCW with remote AR-DB1
  13. // * <Add models (A/C & remotes) you've gotten it working with here>
  14. // Ref:
  15. // These values are based on averages of measurements
  16. const uint16_t kFujitsuAcHdrMark = 3324;
  17. const uint16_t kFujitsuAcHdrSpace = 1574;
  18. const uint16_t kFujitsuAcBitMark = 448;
  19. const uint16_t kFujitsuAcOneSpace = 1182;
  20. const uint16_t kFujitsuAcZeroSpace = 390;
  21. const uint16_t kFujitsuAcMinGap = 8100;
  22. #if SEND_FUJITSU_AC
  23. // Send a Fujitsu A/C message.
  24. //
  25. // Args:
  26. // data: An array of bytes containing the IR command.
  27. // nbytes: Nr. of bytes of data in the array. Typically one of:
  28. // kFujitsuAcStateLength
  29. // kFujitsuAcStateLength - 1
  30. // kFujitsuAcStateLengthShort
  31. // kFujitsuAcStateLengthShort - 1
  32. // repeat: Nr. of times the message is to be repeated.
  33. // (Default = kFujitsuAcMinRepeat).
  34. //
  35. // Status: BETA / Appears to be working.
  36. //
  37. void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes,
  38. uint16_t repeat) {
  39. sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark,
  40. kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace,
  41. kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false,
  42. repeat, 50);
  43. }
  44. #endif // SEND_FUJITSU_AC
  45. // Code to emulate Fujitsu A/C IR remote control unit.
  46. // Initialise the object.
  47. IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model)
  48. : _irsend(pin) {
  49. setModel(model);
  50. stateReset();
  51. }
  52. void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) {
  53. _model = model;
  54. switch (model) {
  55. case ARDB1:
  56. _state_length = kFujitsuAcStateLength - 1;
  57. _state_length_short = kFujitsuAcStateLengthShort - 1;
  58. break;
  59. default:
  60. _state_length = kFujitsuAcStateLength;
  61. _state_length_short = kFujitsuAcStateLengthShort;
  62. }
  63. }
  64. // Reset the state of the remote to a known good state/sequence.
  65. void IRFujitsuAC::stateReset() {
  66. _temp = 24;
  67. _fanSpeed = kFujitsuAcFanHigh;
  68. _mode = kFujitsuAcModeCool;
  69. _swingMode = kFujitsuAcSwingBoth;
  70. _cmd = kFujitsuAcCmdTurnOn;
  71. buildState();
  72. }
  73. // Configure the pin for output.
  74. void IRFujitsuAC::begin() { _irsend.begin(); }
  75. #if SEND_FUJITSU_AC
  76. // Send the current desired state to the IR LED.
  77. void IRFujitsuAC::send() {
  78. getRaw();
  79. _irsend.sendFujitsuAC(remote_state, getStateLength());
  80. }
  81. #endif // SEND_FUJITSU_AC
  82. void IRFujitsuAC::buildState() {
  83. remote_state[0] = 0x14;
  84. remote_state[1] = 0x63;
  85. remote_state[2] = 0x00;
  86. remote_state[3] = 0x10;
  87. remote_state[4] = 0x10;
  88. bool fullCmd = false;
  89. switch (_cmd) {
  90. case kFujitsuAcCmdTurnOff:
  91. remote_state[5] = 0x02;
  92. break;
  93. case kFujitsuAcCmdStepHoriz:
  94. remote_state[5] = 0x79;
  95. break;
  96. case kFujitsuAcCmdStepVert:
  97. remote_state[5] = 0x6C;
  98. break;
  99. default:
  100. switch (_model) {
  101. case ARRAH2E:
  102. remote_state[5] = 0xFE;
  103. break;
  104. case ARDB1:
  105. remote_state[5] = 0xFC;
  106. break;
  107. }
  108. fullCmd = true;
  109. break;
  110. }
  111. if (fullCmd) { // long codes
  112. uint8_t tempByte = _temp - kFujitsuAcMinTemp;
  113. // Nr. of bytes in the message after this byte.
  114. remote_state[6] = _state_length - 7;
  115. remote_state[7] = 0x30;
  116. remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4);
  117. remote_state[9] = _mode | 0 << 4; // timer off
  118. remote_state[10] = _fanSpeed | _swingMode << 4;
  119. remote_state[11] = 0; // timerOff values
  120. remote_state[12] = 0; // timerOff/On values
  121. remote_state[13] = 0; // timerOn values
  122. if (_model == ARRAH2E)
  123. remote_state[14] = 0x20;
  124. else
  125. remote_state[14] = 0x00;
  126. uint8_t checksum = 0;
  127. uint8_t checksum_complement = 0;
  128. if (_model == ARRAH2E) {
  129. checksum = sumBytes(remote_state + _state_length_short,
  130. _state_length - _state_length_short - 1);
  131. } else if (_model == ARDB1) {
  132. checksum = sumBytes(remote_state, _state_length - 1);
  133. checksum_complement = 0x9B;
  134. }
  135. // and negate the checksum and store it in the last byte.
  136. remote_state[_state_length - 1] = checksum_complement - checksum;
  137. } else { // short codes
  138. if (_model == ARRAH2E)
  139. // The last byte is the inverse of penultimate byte
  140. remote_state[_state_length_short - 1] =
  141. ~remote_state[_state_length_short - 2];
  142. // Zero the rest of the state.
  143. for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++)
  144. remote_state[i] = 0;
  145. }
  146. }
  147. uint8_t IRFujitsuAC::getStateLength() {
  148. buildState(); // Force an update of the internal state.
  149. if ((_model == ARRAH2E && remote_state[5] != 0xFE) ||
  150. (_model == ARDB1 && remote_state[5] != 0xFC))
  151. return _state_length_short;
  152. else
  153. return _state_length;
  154. }
  155. // Return a pointer to the internal state date of the remote.
  156. uint8_t* IRFujitsuAC::getRaw() {
  157. buildState();
  158. return remote_state;
  159. }
  160. void IRFujitsuAC::buildFromState(const uint16_t length) {
  161. switch (length) {
  162. case kFujitsuAcStateLength - 1:
  163. case kFujitsuAcStateLengthShort - 1:
  164. setModel(ARDB1);
  165. break;
  166. default:
  167. setModel(ARRAH2E);
  168. }
  169. switch (remote_state[6]) {
  170. case 8:
  171. setModel(ARDB1);
  172. break;
  173. case 9:
  174. setModel(ARRAH2E);
  175. break;
  176. }
  177. setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp);
  178. if (remote_state[8] & 0x1)
  179. setCmd(kFujitsuAcCmdTurnOn);
  180. else
  181. setCmd(kFujitsuAcCmdStayOn);
  182. setMode(remote_state[9] & 0b111);
  183. setFanSpeed(remote_state[10] & 0b111);
  184. setSwing(remote_state[10] >> 4);
  185. switch (remote_state[5]) {
  186. case kFujitsuAcCmdTurnOff:
  187. case kFujitsuAcCmdStepHoriz:
  188. case kFujitsuAcCmdStepVert:
  189. setCmd(remote_state[5]);
  190. break;
  191. }
  192. }
  193. bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
  194. if (length > kFujitsuAcStateLength) return false;
  195. for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) {
  196. if (i < length)
  197. remote_state[i] = newState[i];
  198. else
  199. remote_state[i] = 0;
  200. }
  201. buildFromState(length);
  202. return true;
  203. }
  204. // Set the requested power state of the A/C to off.
  205. void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; }
  206. void IRFujitsuAC::stepHoriz() {
  207. switch (_model) {
  208. case ARDB1:
  209. break; // This remote doesn't have a horizontal option.
  210. default:
  211. _cmd = kFujitsuAcCmdStepHoriz;
  212. }
  213. }
  214. void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; }
  215. // Set the requested command of the A/C.
  216. void IRFujitsuAC::setCmd(uint8_t cmd) {
  217. switch (cmd) {
  218. case kFujitsuAcCmdTurnOff:
  219. case kFujitsuAcCmdTurnOn:
  220. case kFujitsuAcCmdStayOn:
  221. case kFujitsuAcCmdStepVert:
  222. _cmd = cmd;
  223. break;
  224. case kFujitsuAcCmdStepHoriz:
  225. if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal.
  226. _cmd = cmd;
  227. // FALLTHRU
  228. default:
  229. _cmd = kFujitsuAcCmdStayOn;
  230. break;
  231. }
  232. }
  233. uint8_t IRFujitsuAC::getCmd() { return _cmd; }
  234. bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; }
  235. // Set the temp. in deg C
  236. void IRFujitsuAC::setTemp(uint8_t temp) {
  237. temp = std::max((uint8_t)kFujitsuAcMinTemp, temp);
  238. temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp);
  239. _temp = temp;
  240. }
  241. uint8_t IRFujitsuAC::getTemp() { return _temp; }
  242. // Set the speed of the fan
  243. void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) {
  244. if (fanSpeed > kFujitsuAcFanQuiet)
  245. fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range.
  246. _fanSpeed = fanSpeed;
  247. }
  248. uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; }
  249. // Set the requested climate operation mode of the a/c unit.
  250. void IRFujitsuAC::setMode(uint8_t mode) {
  251. if (mode > kFujitsuAcModeHeat)
  252. mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range.
  253. _mode = mode;
  254. }
  255. uint8_t IRFujitsuAC::getMode() { return _mode; }
  256. // Set the requested swing operation mode of the a/c unit.
  257. void IRFujitsuAC::setSwing(uint8_t swingMode) {
  258. switch (_model) {
  259. case ARDB1:
  260. // Set the mode to max if out of range
  261. if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert;
  262. break;
  263. case ARRAH2E:
  264. default:
  265. // Set the mode to max if out of range
  266. if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth;
  267. }
  268. _swingMode = swingMode;
  269. }
  270. uint8_t IRFujitsuAC::getSwing() { return _swingMode; }
  271. bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) {
  272. uint8_t sum = 0;
  273. uint8_t sum_complement = 0;
  274. uint8_t checksum = state[length - 1];
  275. switch (length) {
  276. case kFujitsuAcStateLengthShort: // ARRAH2E
  277. return state[length - 1] == (uint8_t)~state[length - 2];
  278. case kFujitsuAcStateLength - 1: // ARDB1
  279. sum = sumBytes(state, length - 1);
  280. sum_complement = 0x9B;
  281. break;
  282. case kFujitsuAcStateLength: // ARRAH2E
  283. sum = sumBytes(state + kFujitsuAcStateLengthShort,
  284. length - 1 - kFujitsuAcStateLengthShort);
  285. break;
  286. default: // Includes ARDB1 short.
  287. return true; // Assume the checksum is valid for other lengths.
  288. }
  289. return checksum == (uint8_t)(sum_complement - sum); // Does it match?
  290. }
  291. // Convert the internal state into a human readable string.
  292. #ifdef ARDUINO
  293. String IRFujitsuAC::toString() {
  294. String result = "";
  295. #else
  296. std::string IRFujitsuAC::toString() {
  297. std::string result = "";
  298. #endif // ARDUINO
  299. result += "Power: ";
  300. if (getPower())
  301. result += "On";
  302. else
  303. result += "Off";
  304. result += ", Mode: " + uint64ToString(getMode());
  305. switch (getMode()) {
  306. case kFujitsuAcModeAuto:
  307. result += " (AUTO)";
  308. break;
  309. case kFujitsuAcModeCool:
  310. result += " (COOL)";
  311. break;
  312. case kFujitsuAcModeHeat:
  313. result += " (HEAT)";
  314. break;
  315. case kFujitsuAcModeDry:
  316. result += " (DRY)";
  317. break;
  318. case kFujitsuAcModeFan:
  319. result += " (FAN)";
  320. break;
  321. default:
  322. result += " (UNKNOWN)";
  323. }
  324. result += ", Temp: " + uint64ToString(getTemp()) + "C";
  325. result += ", Fan: " + uint64ToString(getFanSpeed());
  326. switch (getFanSpeed()) {
  327. case kFujitsuAcFanAuto:
  328. result += " (AUTO)";
  329. break;
  330. case kFujitsuAcFanHigh:
  331. result += " (HIGH)";
  332. break;
  333. case kFujitsuAcFanMed:
  334. result += " (MED)";
  335. break;
  336. case kFujitsuAcFanLow:
  337. result += " (LOW)";
  338. break;
  339. case kFujitsuAcFanQuiet:
  340. result += " (QUIET)";
  341. break;
  342. }
  343. result += ", Swing: ";
  344. switch (getSwing()) {
  345. case kFujitsuAcSwingOff:
  346. result += "Off";
  347. break;
  348. case kFujitsuAcSwingVert:
  349. result += "Vert";
  350. break;
  351. case kFujitsuAcSwingHoriz:
  352. result += "Horiz";
  353. break;
  354. case kFujitsuAcSwingBoth:
  355. result += "Vert + Horiz";
  356. break;
  357. default:
  358. result += "UNKNOWN";
  359. }
  360. result += ", Command: ";
  361. switch (getCmd()) {
  362. case kFujitsuAcCmdStepHoriz:
  363. result += "Step vane horizontally";
  364. break;
  365. case kFujitsuAcCmdStepVert:
  366. result += "Step vane vertically";
  367. break;
  368. default:
  369. result += "N/A";
  370. }
  371. return result;
  372. }
  373. #if DECODE_FUJITSU_AC
  374. // Decode a Fujitsu AC IR message if possible.
  375. // Places successful decode information in the results pointer.
  376. // Args:
  377. // results: Ptr to the data to decode and where to store the decode result.
  378. // nbits: The number of data bits to expect. Typically kFujitsuAcBits.
  379. // strict: Flag to indicate if we strictly adhere to the specification.
  380. // Returns:
  381. // boolean: True if it can decode it, false if it can't.
  382. //
  383. // Status: ALPHA / Untested.
  384. //
  385. // Ref:
  386. //
  387. bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits,
  388. bool strict) {
  389. uint16_t offset = kStartOffset;
  390. uint16_t dataBitsSoFar = 0;
  391. // Have we got enough data to successfully decode?
  392. if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1)
  393. return false; // Can't possibly be a valid message.
  394. // Compliance
  395. if (strict) {
  396. switch (nbits) {
  397. case kFujitsuAcBits:
  398. case kFujitsuAcBits - 8:
  399. case kFujitsuAcMinBits:
  400. case kFujitsuAcMinBits + 8:
  401. break;
  402. default:
  403. return false; // Must be called with the correct nr. of bits.
  404. }
  405. }
  406. // Header
  407. if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false;
  408. if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false;
  409. // Data (Fixed signature)
  410. match_result_t data_result =
  411. matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8,
  412. kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark,
  413. kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
  414. if (data_result.success == false) return false; // Fail
  415. if (data_result.data != 0x1010006314) return false; // Signature failed.
  416. dataBitsSoFar += kFujitsuAcMinBits - 8;
  417. offset += data_result.used;
  418. results->state[0] = 0x14;
  419. results->state[1] = 0x63;
  420. results->state[2] = 0x00;
  421. results->state[3] = 0x10;
  422. results->state[4] = 0x10;
  423. // Keep reading bytes until we either run out of message or state to fill.
  424. for (uint16_t i = 5;
  425. offset <= results->rawlen - 16 && i < kFujitsuAcStateLength;
  426. i++, dataBitsSoFar += 8, offset += data_result.used) {
  427. data_result = matchData(
  428. &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace,
  429. kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
  430. if (data_result.success == false) break; // Fail
  431. results->state[i] = data_result.data;
  432. }
  433. // Footer
  434. if (offset > results->rawlen ||
  435. !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark))
  436. return false;
  437. // The space is optional if we are out of capture.
  438. if (offset < results->rawlen &&
  439. !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap))
  440. return false;
  441. // Compliance
  442. if (strict) {
  443. if (dataBitsSoFar != nbits) return false;
  444. }
  445. results->decode_type = FUJITSU_AC;
  446. results->bits = dataBitsSoFar;
  447. // Compliance
  448. switch (dataBitsSoFar) {
  449. case kFujitsuAcMinBits:
  450. // Check if this values indicate that this should have been a long state
  451. // message.
  452. if (results->state[5] == 0xFC) return false;
  453. return true; // Success
  454. case kFujitsuAcMinBits + 8:
  455. // Check if this values indicate that this should have been a long state
  456. // message.
  457. if (results->state[5] == 0xFE) return false;
  458. // The last byte needs to be the inverse of the penultimate byte.
  459. if (results->state[5] != (uint8_t)~results->state[6]) return false;
  460. return true; // Success
  461. case kFujitsuAcBits - 8:
  462. // Long messages of this size require this byte be correct.
  463. if (results->state[5] != 0xFC) return false;
  464. break;
  465. case kFujitsuAcBits:
  466. // Long messages of this size require this byte be correct.
  467. if (results->state[5] != 0xFE) return false;
  468. break;
  469. default:
  470. return false; // Unexpected size.
  471. }
  472. if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8))
  473. return false;
  474. // Success
  475. return true; // All good.
  476. }
  477. #endif // DECODE_FUJITSU_AC