Guest User

cc1101.cpp

a guest
Jun 2nd, 2026
42
0
53 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.09 KB | None | 0 0
  1. #include "cc1101.h"
  2.  
  3. #define cc1101_Select() digitalWrite(chipSelectPin, LOW)
  4. #define cc1101_Deselect() digitalWrite(chipSelectPin, HIGH)
  5. // MISO low = chip ready (CC1101 "CHIP_RDYn" signal).
  6. #define wait_Miso() while (digitalRead(MISO) > 0)
  7.  
  8. CC1101::CC1101(void) {}
  9.  
  10. void CC1101::writeReg(byte registerAddress, byte value)
  11. {
  12.   cc1101_Select();
  13.   wait_Miso();
  14.   SPI.transfer(registerAddress);
  15.   SPI.transfer(value);
  16.   cc1101_Deselect();
  17. }
  18.  
  19. void CC1101::writeBurstReg(byte registerAddress, byte *buffer, byte length)
  20. {
  21.   byte address = registerAddress | WRITE_BURST;
  22.   cc1101_Select();
  23.   wait_Miso();
  24.   SPI.transfer(address);
  25.  
  26.   for (byte i = 0; i < length; i++)
  27.     SPI.transfer(buffer[i]);
  28.  
  29.   cc1101_Deselect();
  30. }
  31.  
  32. void CC1101::cmdStrobe(byte cmd)
  33. {
  34.   cc1101_Select();
  35.   wait_Miso();
  36.   SPI.transfer(cmd);
  37.   cc1101_Deselect();
  38. }
  39.  
  40. byte CC1101::readReg(byte registerAddress, byte registerType)
  41. {
  42.   byte address = registerAddress | registerType;
  43.   cc1101_Select();
  44.   wait_Miso();
  45.   SPI.transfer(address);
  46.   byte val = SPI.transfer(0x00);
  47.   cc1101_Deselect();
  48.  
  49.   return val;
  50. }
  51.  
  52. void CC1101::reset(void)
  53. {
  54.   cc1101_Deselect();
  55.   delayMicroseconds(5);
  56.   cc1101_Select();
  57.   delayMicroseconds(10);
  58.   cc1101_Deselect();
  59.   delayMicroseconds(41);
  60.   cc1101_Select();
  61.  
  62.   wait_Miso();
  63.   SPI.transfer(CC1101_SRES);
  64.   wait_Miso();
  65.  
  66.   cc1101_Deselect();
  67. }
  68.  
  69. void CC1101::setCarrierFreq()
  70. {
  71.   writeReg(CC1101_FREQ2, CC1101_DEFVAL_FREQ2_868);
  72.   writeReg(CC1101_FREQ1, CC1101_DEFVAL_FREQ1_868);
  73.   writeReg(CC1101_FREQ0, CC1101_DEFVAL_FREQ0_868);
  74. }
  75.  
  76. void CC1101::setPowerDownState()
  77. {
  78.   // SPWD only takes effect from IDLE — strobe IDLE first in case we were in RX/TX.
  79.   cmdStrobe(CC1101_SIDLE);
  80.   cmdStrobe(CC1101_SPWD);
  81. }
  82.  
  83. void CC1101::setRxState(void)
  84. {
  85.   writeReg(CC1101_MDMCFG2, CC1101_DEFVAL_MDMCFG2);
  86.   writeReg(CC1101_IOCFG0, CC1101_SERIAL_OUT_IOCFG0);
  87.   cmdStrobe(CC1101_SRX);
  88. }
  89.  
  90. void CC1101::setTxState(void)
  91. {
  92.   writeReg(CC1101_IOCFG0, CC1101_DEFVAL_IOCFG0);
  93.   writeReg(CC1101_MDMCFG2, CC1101_DEFVAL_MDMCFG2);
  94.   cmdStrobe(CC1101_STX);
  95. }
  96.  
  97. void CC1101::configRegisters(void)
  98. {
  99.   reset();
  100.  
  101.   setCarrierFreq();
  102.   writeReg(CC1101_IOCFG0, CC1101_DEFVAL_IOCFG0);
  103.   writeReg(CC1101_IOCFG2, CC1101_DEFVAL_IOCFG2);
  104.   writeReg(CC1101_FSCTRL1, CC1101_DEFVAL_FSCTRL1);
  105.   writeReg(CC1101_FSCTRL0, CC1101_DEFVAL_FSCTRL0);
  106.   writeReg(CC1101_MDMCFG4, CC1101_DEFVAL_MDMCFG4);
  107.   writeReg(CC1101_MDMCFG3, CC1101_DEFVAL_MDMCFG3);
  108.   writeReg(CC1101_MDMCFG2, CC1101_DEFVAL_MDMCFG2);
  109.   writeReg(CC1101_MDMCFG1, CC1101_DEFVAL_MDMCFG1);
  110.   writeReg(CC1101_MDMCFG0, CC1101_DEFVAL_MDMCFG0);
  111.   writeReg(CC1101_CHANNR, CC1101_DEFVAL_CHANNR);
  112.   writeReg(CC1101_DEVIATN, CC1101_DEFVAL_DEVIATN);
  113.   writeReg(CC1101_FREND1, CC1101_DEFVAL_FREND1);
  114.   writeReg(CC1101_FREND0, CC1101_DEFVAL_FREND0);
  115.   writeReg(CC1101_MCSM0, CC1101_DEFVAL_MCSM0);
  116.   writeReg(CC1101_FOCCFG, CC1101_DEFVAL_FOCCFG);
  117.   writeReg(CC1101_BSCFG, CC1101_DEFVAL_BSCFG);
  118.   writeReg(CC1101_AGCCTRL2, CC1101_DEFVAL_AGCCTRL2);
  119.   writeReg(CC1101_AGCCTRL1, CC1101_DEFVAL_AGCCTRL1);
  120.   writeReg(CC1101_AGCCTRL0, CC1101_DEFVAL_AGCCTRL0);
  121.   writeReg(CC1101_WORCTRL, CC1101_DEFVAL_WORCTRL);
  122.   writeReg(CC1101_FSCAL3, CC1101_DEFVAL_FSCAL3);
  123.   writeReg(CC1101_FSCAL2, CC1101_DEFVAL_FSCAL2);
  124.   writeReg(CC1101_FSCAL1, CC1101_DEFVAL_FSCAL1);
  125.   writeReg(CC1101_FSCAL0, CC1101_DEFVAL_FSCAL0);
  126.   writeReg(CC1101_TEST0, CC1101_DEFVAL_TEST0);
  127.   writeReg(CC1101_PKTCTRL1, CC1101_DEFVAL_PKTCTRL1);
  128.   writeReg(CC1101_PKTCTRL0, CC1101_DEFVAL_PKTCTRL0);
  129.  
  130.   uint8_t txBuffer[8] = { 0x6F, 0x26, 0x2E, 0x7F, 0x8A, 0x84, 0xCA, 0xC4
  131.                       };
  132.   writeBurstReg(CC1101_UNKNOWNFIFO, txBuffer, 8);
  133.   cmdStrobe(CC1101_SIDLE);
  134.   cmdStrobe(CC1101_SIDLE);
  135.  
  136. }
  137.  
  138. void CC1101::init(void)
  139. {
  140.   pinMode(chipSelectPin, OUTPUT);
  141.   SPI.begin();
  142.   pinMode(CC1101_GDO2, INPUT);
  143.   reset();
  144.   setPowerDownState();
  145.  
  146.   msgId[indoorHum].codeId = 0x12A0;
  147.   msgId[fanSpeed].codeId = 0x22F1;
  148.   msgId[fanInfo].codeId = 0x31DA;
  149. }
  150.  
  151. void CC1101::setRxMode(void)
  152. {
  153.   configRegisters();
  154.   setRxState();
  155. }
  156.  
  157. // Ramses II checksum: byte sum of all preceding bytes, two's complement.
  158. // The frame as a whole (data + CRC) sums to zero modulo 256.
  159. uint8_t CC1101::calcCrc(uint8_t dataframe[], uint8_t length)
  160. {
  161.   uint8_t sum = 0;
  162.   for (uint8_t i = 0; i < length - 1; i++)
  163.     sum += dataframe[i];  // wraps mod 256
  164.   return (uint8_t)(0 - sum);
  165. }
  166.  
  167. // Slide a 6-byte window across Serial1 looking for Ramses II BOF: 55 ?? 00 33 55 53.
  168. // Byte at offset 1 is intentionally unchecked (preserved from original implementation).
  169. // Timeout only starts ticking once the first byte arrives — a W (22F7) makes the
  170. // fan mutate state before replying, so the quiet period before bytes start can
  171. // exceed the per-frame timeout. Hard upper bound of 7 s keeps us safely under the
  172. // 8 s watchdog if the line is truly silent.
  173. bool CC1101::waitForBof(uint16_t timeoutMs)
  174. {
  175.   uint8_t rxBuffer[6] = {0};
  176.   unsigned long startedMillis = 0;
  177.   unsigned long entryMillis = millis();
  178.   bool started = false;
  179.  
  180.   while (true)
  181.   {
  182.     if (Serial1.available() > 0)
  183.     {
  184.       if (!started)
  185.       {
  186.         started = true;
  187.         startedMillis = millis();
  188.       }
  189.  
  190.       for (uint8_t i = 0; i < 5; i++)
  191.         rxBuffer[i] = rxBuffer[i + 1];
  192.       rxBuffer[5] = Serial1.read();
  193.  
  194.       if ((rxBuffer[5] == 0x53) && (rxBuffer[4] == 0x55) && (rxBuffer[3] == 0x33) &&
  195.           (rxBuffer[2] == 0x00) && (rxBuffer[0] == 0x55))
  196.         return true;
  197.     }
  198.     if (started && millis() - startedMillis > timeoutMs)
  199.       return false;
  200.     if (millis() - entryMillis > 7000UL)
  201.       return false;
  202.   }
  203. }
  204.  
  205. // Build a Ramses II frame: header byte, 6 address bytes, 2-byte opcode, length, payload, CRC.
  206. // `out` must be at least 11 + payloadLength bytes.
  207. void CC1101::buildRamsesFrame(uint8_t header, uint16_t opcode, const uint8_t *payload, uint8_t payloadLength, uint8_t *out)
  208. {
  209.   out[0] = header;
  210.   for (uint8_t i = 0; i < 6; i++)
  211.     out[1 + i] = newFanState.address[i];
  212.   out[7] = (uint8_t)(opcode >> 8);
  213.   out[8] = (uint8_t)(opcode & 0xFF);
  214.   out[9] = payloadLength;
  215.   for (uint8_t i = 0; i < payloadLength; i++)
  216.     out[10 + i] = payload[i];
  217.   const uint8_t frameLength = 11 + payloadLength;
  218.   out[frameLength - 1] = calcCrc(out, frameLength);
  219. }
  220.  
  221. void CC1101::manchesterDecode(uint8_t rxBuffer[], uint8_t length, uint8_t *rxPayload)
  222. {
  223.   uint8_t payloadCount = 0;
  224.   const uint8_t manchesterTable[16] = {0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95, 0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55};
  225.  
  226.   for (uint8_t i = 0; i < length; i += 2)
  227.   {
  228.     for (uint8_t x = 0; x < 16; x++)
  229.     {
  230.       if (rxBuffer[i + 1] == manchesterTable[x])
  231.         rxPayload[payloadCount] = x;
  232.     }
  233.     for (uint8_t x = 0; x < 16; x++)
  234.     {
  235.       if (rxBuffer[i] == manchesterTable[x])
  236.         rxPayload[payloadCount] |= x << 4;
  237.     }
  238.     payloadCount++;
  239.   }
  240. }
  241.  
  242. void CC1101::manchesterEncode(uint8_t txBuffer[], uint8_t length, uint8_t *payloadEncoded)
  243. {
  244.   const uint8_t manchesterTable[16] = {0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95, 0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55};
  245.  
  246.   uint8_t txCount = 0;
  247.   for (int i = 0; i < length; i++)
  248.   {
  249.     payloadEncoded[txCount++] = manchesterTable[(txBuffer[i] & 0xF0) >> 4];
  250.     payloadEncoded[txCount++] = manchesterTable[txBuffer[i] & 0x0F];
  251.   }
  252. }
  253.  
  254. // Decode a CRC- and address-validated Ramses II / Orcon payload into newFanState.
  255. // CRC passing doesn't bound the buffer length, so each opcode branch verifies that
  256. // the bytes it reads are actually present before touching newFanState.
  257. void CC1101::parseRamsesPayload(uint8_t dataframe[], uint8_t length)
  258. {
  259.   // Need at least header (0..6) + opcode (7..8) + protocol-length byte (9).
  260.   if (length < 10) return;
  261.  
  262.   uint16_t opcode = ((uint16_t)dataframe[7] << 8) | (uint16_t)dataframe[8];
  263.   uint8_t payloadLength = dataframe[9];
  264.  
  265.   if ((opcode == 0x22F1) && (payloadLength == 0x03) && (length >= 12))  // FAN SPEED
  266.   {
  267.     newFanState.fanSpeed = dataframe[11];
  268.     msgId[fanSpeed].rxFlag = true;
  269.   }
  270.  
  271.   if ((opcode == 0x22F7) && (payloadLength == 0x03) && (length >= 12))  // BYPASS (BYPASS_OPEN / BYPASS_CLOSE / BYPASS_AUTO)
  272.   {
  273.     newFanState.bypassMode = dataframe[11];
  274.   }
  275.  
  276.   if ((opcode == 0x31D9) && (length >= 13))  // FAN SPEED, protocol-length may differ between MVS and HRC so don't check it
  277.   {
  278.     newFanState.fanSpeed = dataframe[12];
  279.     msgId[fanSpeed].rxFlag = true;
  280.   }
  281.  
  282.   if ((opcode == 0x12A0) && (payloadLength == 0x02) && (length >= 12))  // Humidity
  283.   {
  284.     newFanState.indoorHumidity = dataframe[11];
  285.     msgId[indoorHum].rxFlag = true;
  286.   }
  287.  
  288.   // 2411 RP carries a single config parameter; param 0x75 is the Orcon "Comfort temperature"
  289.   // (bypass target). Older Orcons reply with 22-byte payloads, newer with 23 — the value
  290.   // sits at the same offset in both, so accept either length. Value is a 4-byte big-endian
  291.   // int32 of centi-°C; max is 3000 (30.0°C), so the low 16 bits suffice and match the
  292.   // representation used by the other temperature fields.
  293.   if ((opcode == 0x2411) && ((payloadLength == 0x16) || (payloadLength == 0x17)) && (length >= 19))
  294.   {
  295.     if (dataframe[11] == 0x00 && dataframe[12] == 0x75 && dataframe[14] == 0x92)
  296.     {
  297.       newFanState.comfortTemperature = ((uint16_t)dataframe[17] << 8) | dataframe[18];
  298.     }
  299.   }
  300.  
  301.   if ((opcode == 0x31DA) && (payloadLength == 0x1E) && (length >= 39))  // EXTENDED 31DA INFO
  302.   {
  303.     newFanState.indoorHumidity = dataframe[15];
  304.     newFanState.outdoorHumidity = dataframe[16];
  305.     newFanState.exhaustTemperature = (dataframe[17] * 256) + dataframe[18];
  306.     newFanState.supplyTemperature = (dataframe[19] * 256) + dataframe[20];
  307.     newFanState.indoorTemperature = (dataframe[21] * 256) + dataframe[22];
  308.     newFanState.outdoorTemperature = (dataframe[23] * 256) + dataframe[24];
  309.     newFanState.bypassPosition = dataframe[27];
  310.     newFanState.exhaustFanspeed = dataframe[29];
  311.     newFanState.supplyFanspeed = dataframe[30];
  312.     newFanState.supplyFlow = (dataframe[35] * 256) + dataframe[36];
  313.     newFanState.exhaustFlow = (dataframe[37] * 256) + dataframe[38];
  314.  
  315.     msgId[fanInfo].rxFlag = true;
  316.   }
  317. }
  318.  
  319. bool CC1101::transmitData(uint8_t payload[], uint8_t length)
  320. {
  321.   const uint8_t bufferLength = 100;
  322.   uint8_t rxBuffer[bufferLength];
  323.   int rxLength = 0;
  324.   uint8_t txFrameLength = (length * 2) + 15;  // 15 bytes overhead, length*2 manchester-encoded data
  325.   uint8_t txBuffer[txFrameLength];
  326.   uint8_t txPayloadEncoded[length * 2];
  327.   bool fanFrameValid = false;
  328.  
  329.   manchesterEncode(payload, length, txPayloadEncoded);
  330.  
  331.   // PREAMBLE
  332.   for (int i = 0; i < 9; i++)
  333.     txBuffer[i] = 0x55;
  334.  
  335.   // BOF
  336.   txBuffer[9]  = 0xFF;
  337.   txBuffer[10] = 0x00;
  338.   txBuffer[11] = 0x33;
  339.   txBuffer[12] = 0x55;
  340.   txBuffer[13] = 0x53;
  341.  
  342.   // Manchester-encoded payload
  343.   for (int i = 0; i < txFrameLength - 1; i++)
  344.     txBuffer[i + 14] = txPayloadEncoded[i];
  345.  
  346.   // EOF
  347.   txBuffer[txFrameLength - 1] = 0x35;
  348.  
  349.   Serial1.setTimeout(RX_TIME_OUT);
  350.   configRegisters();
  351.  
  352.   setTxState();
  353.   Serial1.write(txBuffer, txFrameLength);
  354.   Serial1.flush();
  355.  
  356.   // Drain TX loopback (RX and TX pins are tied)
  357.   while (Serial1.available()) Serial1.read();
  358.  
  359.   cmdStrobe(CC1101_SIDLE);
  360.   setRxState();
  361.  
  362.   if (waitForBof(RX_TIME_OUT))
  363.   {
  364.     rxLength = Serial1.readBytesUntil(0x35, rxBuffer, sizeof(rxBuffer) - 1);
  365.  
  366.     if (rxLength > 0 && (rxLength % 2) == 0)
  367.     {
  368.       uint8_t dataframeDecoded[rxLength / 2];
  369.       manchesterDecode(rxBuffer, rxLength, dataframeDecoded);
  370.  
  371.       if (calcCrc(dataframeDecoded, rxLength / 2) == dataframeDecoded[(rxLength / 2) - 1])
  372.       {
  373.         fanFrameValid = true;
  374.         for (uint8_t i = 1; i < 4; i++)
  375.           if (dataframeDecoded[i] != newFanState.address[i + 2])
  376.             fanFrameValid = false;
  377.  
  378.         if (fanFrameValid)
  379.           parseRamsesPayload(dataframeDecoded, rxLength / 2);
  380.       }
  381.     }
  382.   }
  383.  
  384.   cmdStrobe(CC1101_SIDLE);
  385.   writeReg(CC1101_IOCFG0, CC1101_DEFVAL_IOCFG0);
  386.   setPowerDownState();
  387.  
  388.   return fanFrameValid;
  389. }
  390.  
  391. bool CC1101::cloneMode(void)
  392. {
  393.   const uint8_t bufferLength = 75;
  394.   uint8_t rxBuffer[bufferLength];
  395.   int rxLength = 0;
  396.  
  397.   Serial1.setTimeout(RX_TIME_OUT);
  398.  
  399.   // Drain input buffer
  400.   while (Serial1.available()) Serial1.read();
  401.  
  402.   if (!waitForBof(PAIR_TIME_OUT))
  403.     return false;
  404.  
  405.   rxLength = Serial1.readBytesUntil(0x35, rxBuffer, sizeof(rxBuffer) - 1);
  406.   if (rxLength == 0 || (rxLength % 2) != 0)
  407.     return false;
  408.  
  409.   uint8_t dataframeDecoded[rxLength / 2];
  410.   manchesterDecode(rxBuffer, rxLength, dataframeDecoded);
  411.  
  412.   if (calcCrc(dataframeDecoded, rxLength / 2) != dataframeDecoded[(rxLength / 2) - 1])
  413.     return false;
  414.  
  415.   for (uint8_t i = 1; i < 7; i++)
  416.     newFanState.address[i - 1] = dataframeDecoded[i];
  417.  
  418.   return true;
  419. }
  420.  
  421. // Ramses II header bytes: RQ = 0x0C, I = 0x1C, W = 0x2C, RP = 0x3C
  422.  
  423. bool CC1101::setBypass(uint8_t bypassMode)
  424. {
  425.   // 22F7 bypass payload: 00 <mode> EF
  426.   const uint8_t data[3] = { 0x00, bypassMode, 0xEF };
  427.   uint8_t frame[11 + sizeof data];
  428.   buildRamsesFrame(0x2C, 0x22F7, data, sizeof data, frame);
  429.  
  430.   return transmitData(frame, sizeof frame);
  431. }
  432.  
  433. // Request the Orcon "Comfort temperature" (2411 param 0x75) — the bypass target temperature.
  434. // RQ payload: 00 00 <param_id>. Caller diffs newFanState vs currentFanState to detect a
  435. // fresh value; a coincident 31DA in the RX window can flip transmitData to true without
  436. // our RP arriving, in which case the diff stays empty and the next poll picks it up.
  437. bool CC1101::requestComfortTemperature(void)
  438. {
  439.   const uint8_t data[3] = { 0x00, 0x00, 0x75 };
  440.   uint8_t frame[11 + sizeof data];
  441.   buildRamsesFrame(0x0C, 0x2411, data, sizeof data, frame);
  442.  
  443.   return transmitData(frame, sizeof frame);
  444. }
  445.  
  446. // Write the Orcon "Comfort temperature" (2411 param 0x75) — the bypass target.
  447. // W payload (23 bytes, matches the RF15 remote):
  448. //   [0..1]  00 00         prefix
  449. //   [2]     75            param id
  450. //   [3..4]  00 92         flags (W=0x00, RP/I=0x58) + data_type (centi-°C)
  451. //   [5..8]  00 00 hi lo   value, 4-byte BE — max 3000 so top 2 bytes are always 0
  452. //   [9..12] 00 00 00 00   min = 0
  453. //   [13..16] 00 00 0B B8  max = 3000 (30.0°C)
  454. //   [17..20] 00 00 00 01  precision = 1
  455. //   [21..22] 00 01        W trailing
  456. // The fan replies with an I broadcast carrying the new value; the 2411 RP parser
  457. // also matches I-frames so newFanState updates. Caller retries until currentFanState
  458. // matches the requested setpoint.
  459. bool CC1101::setComfortTemperature(uint16_t centiCelsius)
  460. {
  461.   if (centiCelsius > 3000) centiCelsius = 3000;
  462.   uint8_t data[23] = {
  463.     0x00, 0x00, 0x75, 0x00, 0x92,
  464.     0x00, 0x00, (uint8_t)(centiCelsius >> 8), (uint8_t)(centiCelsius & 0xFF),
  465.     0x00, 0x00, 0x00, 0x00,
  466.     0x00, 0x00, 0x0B, 0xB8,
  467.     0x00, 0x00, 0x00, 0x01,
  468.     0x00, 0x01
  469.   };
  470.   uint8_t frame[11 + sizeof data];
  471.   buildRamsesFrame(0x2C, 0x2411, data, sizeof data, frame);
  472.  
  473.   return transmitData(frame, sizeof frame);
  474. }
  475.  
  476. bool CC1101::txFanspeed(uint8_t fanSpeed)
  477. {
  478.   // 22F1 fan speed payload: 00 <speed> 04
  479.   const uint8_t data[3] = { 0x00, fanSpeed, 0x04 };
  480.   uint8_t frame[11 + sizeof data];
  481.   buildRamsesFrame(0x1C, 0x22F1, data, sizeof data, frame);
  482.  
  483.   return transmitData(frame, sizeof frame);
  484. }
  485.  
  486. bool CC1101::txDemand(uint8_t demandByte)
  487. {
  488.   // 31E0 8-byte Orcon CO2-sensor form: 00 00 <XX> 00 01 00 <ST> 00
  489.   // <XX> at index 2: demand 0x00..0xFF; fan maps this to its own curve (clipped).
  490.   // <ST> at index 6: sensor-type marker (0x64 / 0xAA observed; 0x64 default).
  491.   const uint8_t data[8] = { 0x00, 0x00, demandByte, 0x00, 0x01, 0x00, 0x64, 0x00 };
  492.   uint8_t frame[11 + sizeof data];
  493.   buildRamsesFrame(0x1C, 0x31E0, data, sizeof data, frame);
  494.  
  495.   return transmitData(frame, sizeof frame);
  496. }
  497.  
  498. // Polls the fan for one of several status opcodes per call. Two phases:
  499. //   Warm-up (first TX_REQUEST_COUNT calls): round-robin all NUM_CODES opcodes so
  500. //     each opcode has a chance to be heard at least once.
  501. //   Steady-state (after): alternate fanSpeed with whichever of fanInfo or
  502. //     indoorHum is responding (fanInfo preferred once seen).
  503. // Returns true if the TX/RX exchange succeeded.
  504. bool CC1101::requestFanState(void)
  505. {
  506.   uint8_t payload[12] = {0x0C, newFanState.address[0], newFanState.address[1], newFanState.address[2], newFanState.address[3], newFanState.address[4], newFanState.address[5], 0x00, 0x00, 0x01, 0x00, 0x00};
  507.   const uint8_t arrSize = sizeof payload;
  508.   static uint8_t txCount = 0;
  509.   static uint8_t requestCount = 0;
  510.   static uint8_t slotOrder[NUM_CODES] = {fanSpeed, indoorHum, fanInfo};
  511.  
  512.   payload[7] = (msgId[slotOrder[txCount]].codeId >> 8);
  513.   payload[8] = (msgId[slotOrder[txCount]].codeId & 0xFF);
  514.   payload[arrSize - 1] = calcCrc(payload, arrSize);
  515.  
  516.   if (requestCount < TX_REQUEST_COUNT)
  517.   {
  518.     // Warm-up: cycle through all NUM_CODES slots.
  519.     txCount = (txCount < NUM_CODES - 1) ? (txCount + 1) : 0;
  520.     requestCount++;
  521.   }
  522.   else
  523.   {
  524.     // Steady-state: only slots 0 and 1 are used.
  525.     slotOrder[0] = fanSpeed;
  526.     slotOrder[1] = msgId[fanInfo].rxFlag ? fanInfo : indoorHum;
  527.     txCount = (txCount < 1) ? (txCount + 1) : 0;
  528.   }
  529.  
  530.   return transmitData(payload, arrSize);
  531. }
  532.  
Add Comment
Please, Sign In to add comment