Guest User

mysensors.ino

a guest
Jul 27th, 2025
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.06 KB | None | 0 0
  1. #include <Arduino.h>
  2. #include <avr/wdt.h>
  3. #include "cc1101.h"
  4. #define ONBOARD_LED 7
  5. #define FAN_INTERVAL 5000 // RF link interval
  6. #define LED_FLASH_TIME 25 // ms
  7. #define TX_RETRY_CNT 5 // x
  8.  
  9. enum module_states
  10. {
  11. JUST_BOOTED,
  12. RF15_PAIRINGSMODE,
  13. PAIRING_FAIL,
  14. NORMAL_MODE
  15. };
  16.  
  17. CC1101 radio;
  18.  
  19. // Enable serial gateway
  20. #define MY_GATEWAY_SERIAL
  21. #define MY_BAUD_RATE 38400 // Do not change, limitation by frequency (crystal)
  22.  
  23. //#define MY_DEBUG
  24. #include <MySensors.h>
  25. #define CHILD_ID_FAN 1
  26. #define CHILD_ID_CLONE 2
  27. #define CHILD_ID_TARGET_ADDRESS 3
  28. #define CHILD_ID_SOURCE_ADDRESS 4
  29.  
  30. #define CHILD_ID_INDOOR_HUM 5
  31. #define CHILD_ID_OUTDOOR_HUM 6
  32. #define CHILD_ID_EXHAUST_TEMP 7
  33. #define CHILD_ID_SUPPLY_TEMP 8
  34. #define CHILD_ID_BYPASS_POS 9
  35. #define CHILD_ID_EXHAUST_FANSPEED 10
  36. #define CHILD_ID_SUPPLY_FANSPEED 11
  37. #define CHILD_ID_SUPPLY_FLOW 12
  38. #define CHILD_ID_EXHAUST_FLOW 13
  39. #define CHILD_ID_INDOOR_TEMP 14
  40. #define CHILD_ID_OUTDOOR_TEMP 15
  41. #define CHILD_ID_BYPASS_MODE 16
  42.  
  43. #define SN "FanX"
  44. #define SV "1.10"
  45.  
  46. MyMessage msgSourceAddress(CHILD_ID_TARGET_ADDRESS, V_VAR1);
  47. MyMessage msgTargetAddress(CHILD_ID_SOURCE_ADDRESS, V_VAR1);
  48. MyMessage msgCloneSwitch(CHILD_ID_CLONE, V_STATUS);
  49. MyMessage msgFANspeed(CHILD_ID_FAN, V_PERCENTAGE);
  50. MyMessage msgFANstate(CHILD_ID_FAN, V_STATUS);
  51.  
  52. MyMessage msgIndoorHUM(CHILD_ID_INDOOR_HUM, V_HUM);
  53. MyMessage msgOutdoorHUM(CHILD_ID_OUTDOOR_HUM, V_HUM);
  54.  
  55. MyMessage msgExhaustTEMP(CHILD_ID_EXHAUST_TEMP, V_TEMP);
  56. MyMessage msgSupplyTEMP(CHILD_ID_SUPPLY_TEMP, V_TEMP);
  57.  
  58. MyMessage msgIndoorTEMP(CHILD_ID_INDOOR_TEMP, V_TEMP);
  59. MyMessage msgOutdoorTEMP(CHILD_ID_OUTDOOR_TEMP, V_TEMP);
  60.  
  61. MyMessage msgBypassPOS(CHILD_ID_BYPASS_POS, V_VAR1);
  62.  
  63. MyMessage msgExhaustFAN(CHILD_ID_EXHAUST_FANSPEED, V_VAR1);
  64. MyMessage msgSupplyFAN(CHILD_ID_SUPPLY_FANSPEED, V_VAR1);
  65.  
  66. MyMessage msgSupplyFLOW(CHILD_ID_SUPPLY_FLOW, V_VAR1);
  67. MyMessage msgExhaustFLOW(CHILD_ID_EXHAUST_FLOW, V_VAR1);
  68.  
  69. MyMessage msgBypassMODE_VAL(CHILD_ID_BYPASS_MODE, V_PERCENTAGE);
  70. MyMessage msgBypassMODE_STATE(CHILD_ID_BYPASS_MODE, V_STATUS);
  71.  
  72. volatile uint8_t fan_bypass_mode_req = 0xEE; // set to invalid state, equal to current_state
  73. volatile int fan_speed_req = 1;
  74. volatile int prev_fan_speed = 0;
  75.  
  76. module_states dongle_state = JUST_BOOTED;
  77.  
  78. //******************************************************************************************//
  79. // //
  80. // Init //
  81. // //
  82. //******************************************************************************************//
  83.  
  84. void setup()
  85. {
  86. wdt_disable();
  87. // Setup locally attached sensors
  88. pinMode(ONBOARD_LED, OUTPUT);
  89. digitalWrite(ONBOARD_LED, HIGH);
  90. Serial1.begin(38400); // used for transmitting data to FAN
  91. while (!Serial1);
  92. radio.current_fan_state.fan_speed = 1;
  93. radio.current_fan_state.bypass_mode = 0xEE; //unknown, set to invalid state, equal to requested
  94. wdt_enable(WDTO_8S);
  95. }
  96.  
  97. void before()
  98. {
  99. radio.init();
  100. }
  101.  
  102. void presentation()
  103. {
  104. // Present locally attached sensors
  105. sendSketchInfo(SN, SV);
  106. present(CHILD_ID_FAN, S_DIMMER, "FAN speed");
  107. present(CHILD_ID_CLONE, S_BINARY, "Clone switch");
  108. present(CHILD_ID_TARGET_ADDRESS, S_CUSTOM, "Target address");
  109. present(CHILD_ID_SOURCE_ADDRESS, S_CUSTOM, "Source address");
  110.  
  111. present(CHILD_ID_INDOOR_HUM, S_HUM, "Indoor humidity (%)");
  112. present(CHILD_ID_OUTDOOR_HUM, S_HUM, "Outdoor humidity (%)");
  113. present(CHILD_ID_EXHAUST_TEMP, S_TEMP, "Exhaust temperature (ºC)");
  114. present(CHILD_ID_SUPPLY_TEMP, S_TEMP, "Supply temperature (ºC)");
  115.  
  116. present(CHILD_ID_BYPASS_POS, S_CUSTOM, "Bypass position (%)");
  117. present(CHILD_ID_EXHAUST_FANSPEED, S_CUSTOM, "Exhaust FAN (%)");
  118. present(CHILD_ID_SUPPLY_FANSPEED, S_CUSTOM, "Supply FAN (%)");
  119.  
  120. present(CHILD_ID_SUPPLY_FLOW, S_CUSTOM, "Supply flow (m3/h)");
  121. present(CHILD_ID_EXHAUST_FLOW, S_CUSTOM, "Exhaust flow (m3/h)");
  122.  
  123. present(CHILD_ID_INDOOR_TEMP, S_TEMP, "Indoor temperature (ºC)");
  124. present(CHILD_ID_OUTDOOR_TEMP, S_TEMP, "Outdoor temperature (ºC)");
  125.  
  126. present(CHILD_ID_BYPASS_MODE, S_DIMMER, "Bypass mode (1 , 2, 3)");
  127. }
  128.  
  129. //******************************************************************************************//
  130. // //
  131. // Led flash //
  132. // //
  133. //******************************************************************************************//
  134. void led_flash(uint8_t flash_cnt)
  135. {
  136. for (int i = 0; i < flash_cnt; i++)
  137. {
  138. digitalWrite(ONBOARD_LED, LOW); // RADIO LED ON
  139. wait(LED_FLASH_TIME * 2);
  140. digitalWrite(ONBOARD_LED, HIGH); // RADIO LED OFF
  141. wait(LED_FLASH_TIME * 2);
  142. }
  143. }
  144.  
  145. //******************************************************************************************//
  146. // //
  147. // Update new params to controller //
  148. // //
  149. //******************************************************************************************//
  150.  
  151. float temp_from_hex(uint16_t hex_value)
  152. {
  153. if(hex_value >= 32768)
  154. return (hex_value - 65536)/100.0;
  155. else
  156. return (hex_value)/100.0;
  157. }
  158.  
  159. void update_new_params()
  160. {
  161. float flow;
  162.  
  163. if(radio.current_fan_state.indoor_humidity != radio.new_fan_state.indoor_humidity)
  164. {
  165. send(msgIndoorHUM.set(radio.new_fan_state.indoor_humidity));
  166. radio.current_fan_state.indoor_humidity = radio.new_fan_state.indoor_humidity;
  167. }
  168. if(radio.current_fan_state.outdoor_humidity != radio.new_fan_state.outdoor_humidity)
  169. {
  170. send(msgOutdoorHUM.set(radio.new_fan_state.outdoor_humidity));
  171. radio.current_fan_state.outdoor_humidity = radio.new_fan_state.outdoor_humidity;
  172. }
  173. if(radio.current_fan_state.exhaust_temperature != radio.new_fan_state.exhaust_temperature)
  174. {
  175. send(msgExhaustTEMP.set(temp_from_hex(radio.new_fan_state.exhaust_temperature), 1));
  176. radio.current_fan_state.exhaust_temperature = radio.new_fan_state.exhaust_temperature;
  177. }
  178. if(radio.current_fan_state.supply_temperature != radio.new_fan_state.supply_temperature)
  179. {
  180. send(msgSupplyTEMP.set(temp_from_hex(radio.new_fan_state.supply_temperature), 1));
  181. radio.current_fan_state.supply_temperature = radio.new_fan_state.supply_temperature;
  182. }
  183. if(radio.current_fan_state.indoor_temperature != radio.new_fan_state.indoor_temperature)
  184. {
  185. send(msgIndoorTEMP.set(temp_from_hex(radio.new_fan_state.indoor_temperature), 1));
  186. radio.current_fan_state.indoor_temperature = radio.new_fan_state.indoor_temperature;
  187. }
  188. if(radio.current_fan_state.outdoor_temperature != radio.new_fan_state.outdoor_temperature)
  189. {
  190. send(msgOutdoorTEMP.set(temp_from_hex(radio.new_fan_state.outdoor_temperature), 1));
  191. radio.current_fan_state.outdoor_temperature = radio.new_fan_state.outdoor_temperature;
  192. }
  193. if(radio.current_fan_state.bypass_position != radio.new_fan_state.bypass_position)
  194. {
  195. send(msgBypassPOS.set(radio.new_fan_state.bypass_position));
  196. radio.current_fan_state.bypass_position = radio.new_fan_state.bypass_position;
  197. }
  198. if(radio.current_fan_state.exhaust_fanspeed != radio.new_fan_state.exhaust_fanspeed)
  199. {
  200. send(msgExhaustFAN.set(radio.new_fan_state.exhaust_fanspeed));
  201. radio.current_fan_state.exhaust_fanspeed = radio.new_fan_state.exhaust_fanspeed;
  202. }
  203. if(radio.current_fan_state.supply_fanspeed != radio.new_fan_state.supply_fanspeed)
  204. {
  205. send(msgSupplyFAN.set(radio.new_fan_state.supply_fanspeed));
  206. radio.current_fan_state.supply_fanspeed = radio.new_fan_state.supply_fanspeed;
  207. }
  208.  
  209. if(radio.current_fan_state.supply_flow != radio.new_fan_state.supply_flow)
  210. {
  211. flow = ((radio.new_fan_state.supply_flow/100.0)*3.6); // l/s => m3/h
  212. send(msgSupplyFLOW.set(flow, 1));
  213. radio.current_fan_state.supply_flow != radio.new_fan_state.supply_flow;
  214. }
  215.  
  216. if(radio.current_fan_state.exhaust_flow != radio.new_fan_state.exhaust_flow)
  217. {
  218. flow = ((radio.new_fan_state.exhaust_flow/100.0)*3.6); // l/s => m3/h
  219. send(msgExhaustFLOW.set(flow, 1));
  220. radio.current_fan_state.exhaust_flow != radio.new_fan_state.exhaust_flow;
  221. }
  222.  
  223. }
  224.  
  225. //******************************************************************************************//
  226. // //
  227. // MAIN //
  228. // //
  229. //******************************************************************************************//
  230.  
  231. void loop()
  232. {
  233.  
  234. static uint8_t tx_retry_cntr = 0;
  235. static unsigned long req_current_millis = 0;
  236. static unsigned long prev_req_current_millis = 0;
  237. static bool first_run_flag = true;
  238.  
  239. switch (dongle_state)
  240. {
  241. case (JUST_BOOTED):
  242.  
  243. led_flash(1);
  244.  
  245. // Inform MySensors once
  246. send(msgSourceAddress.set(0));
  247. send(msgTargetAddress.set(0));
  248. send(msgCloneSwitch.set(0));
  249. send(msgFANspeed.set(0));
  250. send(msgFANstate.set(0));
  251. send(msgBypassMODE_VAL.set(4)); // Unknown
  252. send(msgBypassMODE_STATE.set(1)); // On
  253.  
  254. // Check communication with Radio chip
  255. if ((radio.readReg(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & 0x1f) == 1)
  256. {
  257. // Read EEPROM address to check if there is an address known
  258. bool empty_eeprom = true;
  259.  
  260. for (int i = 0; i < 6; i++)
  261. {
  262. radio.new_fan_state.address[i] = loadState(i);
  263. if (radio.new_fan_state.address[i] != 0xFF) empty_eeprom = false;
  264. }
  265.  
  266. if (empty_eeprom)
  267. dongle_state = PAIRING_FAIL;
  268. else
  269. {
  270. sendNewSourceAddressToGateway();
  271. sendNewTargetAddressToGateway();
  272. dongle_state = NORMAL_MODE;
  273. }
  274.  
  275. }
  276. else
  277. wait(1000);
  278.  
  279. break;
  280.  
  281. case (RF15_PAIRINGSMODE):
  282.  
  283. digitalWrite(ONBOARD_LED, LOW); // RADIO LED ON
  284.  
  285. radio.set_rx_mode();
  286. if (radio.clone_mode())
  287. {
  288. // Store in EEPROM via MySensors function saveState
  289. for (int i = 0; i < 6; i++)
  290. saveState(i, radio.new_fan_state.address[i]);
  291.  
  292. // Update Gateway
  293. sendNewSourceAddressToGateway();
  294. sendNewTargetAddressToGateway();
  295.  
  296. prev_req_current_millis = millis(); // prevent burst of transmissions
  297. dongle_state = NORMAL_MODE;
  298. }
  299. else
  300. dongle_state = PAIRING_FAIL;
  301.  
  302. digitalWrite(ONBOARD_LED, HIGH); // RADIO LED OFF
  303.  
  304. clone_mode_ended();
  305.  
  306. break;
  307.  
  308. case (PAIRING_FAIL):
  309.  
  310. // LED error pattern
  311. led_flash(2);
  312. wait(750);
  313.  
  314. break;
  315.  
  316. case (NORMAL_MODE):
  317. if (radio.current_fan_state.fan_speed != fan_speed_req) // Set new data or fan speed request?
  318. {
  319. if (tx_retry_cntr < TX_RETRY_CNT)
  320. {
  321. if (radio.tx_fanspeed(fan_speed_req)) // Succes, blink led
  322. {
  323. led_flash(1);
  324. radio.current_fan_state.fan_speed = radio.new_fan_state.fan_speed; // If ok, current fan speed should match requested one
  325. }
  326. tx_retry_cntr++;
  327. }
  328. else
  329. radio.current_fan_state.fan_speed = fan_speed_req; // Forget about this session, maybe more succes next time?
  330. }
  331. else if (radio.current_fan_state.bypass_mode != fan_bypass_mode_req)
  332. {
  333. if (tx_retry_cntr < TX_RETRY_CNT)
  334. {
  335. if (radio.set_bypass(fan_bypass_mode_req))
  336. {
  337. led_flash(1);
  338. radio.current_fan_state.bypass_mode = radio.new_fan_state.bypass_mode;
  339. }
  340. tx_retry_cntr++;
  341. }
  342. else
  343. {
  344. radio.current_fan_state.bypass_mode = fan_bypass_mode_req;
  345. }
  346. }
  347. else
  348. {
  349. tx_retry_cntr = 0; // reset cntr
  350.  
  351. // Request fan speed on regular interval FAN_INTERVAL
  352. req_current_millis = millis();
  353. if((req_current_millis-prev_req_current_millis) > FAN_INTERVAL)
  354. {
  355. prev_req_current_millis += FAN_INTERVAL;
  356.  
  357. if(radio.request_fan_state())
  358. {
  359. led_flash(1);
  360.  
  361. if(first_run_flag) // Update once on first boot
  362. {
  363. radio.current_fan_state.fan_speed = radio.new_fan_state.fan_speed;
  364. fan_speed_req = radio.current_fan_state.fan_speed; // Prevent RF update next cycle
  365.  
  366. if(radio.current_fan_state.fan_speed == 0)
  367. update_fan_state(0);
  368. else
  369. update_fan_state(1);
  370.  
  371. update_fan_speed(radio.current_fan_state.fan_speed);
  372.  
  373. first_run_flag = false;
  374. }
  375. else
  376. {
  377. update_new_params(); // update changed params to controller
  378.  
  379. // FAN SPEED CHANGE
  380. if(radio.current_fan_state.fan_speed != radio.new_fan_state.fan_speed) // FAN SPEED CHANGED
  381. {
  382. if(radio.new_fan_state.fan_speed == 0) // DETERMINE TO REPORT FAN STATE CHANGE OF SPEED CHANGE TO CONTROLLER
  383. {
  384. prev_fan_speed = radio.current_fan_state.fan_speed;
  385. radio.current_fan_state.fan_speed = radio.new_fan_state.fan_speed;
  386. fan_speed_req = radio.current_fan_state.fan_speed; // Prevent RF update next cycle
  387. update_fan_state(0);
  388. }
  389. else
  390. {
  391. if(radio.current_fan_state.fan_speed == 0) // Fan is off, new value != 0, therefore: send once V_STATUS = TRUE
  392. update_fan_state(1);
  393.  
  394. radio.current_fan_state.fan_speed = radio.new_fan_state.fan_speed;
  395. fan_speed_req = radio.current_fan_state.fan_speed; // Prevent RF update next cycle
  396. update_fan_speed(radio.current_fan_state.fan_speed);
  397. }
  398. }
  399. }
  400. }
  401. }
  402. }
  403.  
  404. break;
  405. }
  406.  
  407. wdt_reset();
  408.  
  409. }
  410.  
  411. //******************************************************************************************//
  412. // //
  413. // Receive from MySensors //
  414. // //
  415. //******************************************************************************************//
  416.  
  417. void receive(const MyMessage &message)
  418. {
  419. if(message.isAck())
  420. {
  421. //Serial.println("This is an ack from gateway");
  422. return;
  423. }
  424.  
  425. if( (message.getType() == V_STATUS) && (message.getSensor() == CHILD_ID_CLONE) )
  426. {
  427. if(message.getBool())
  428. {
  429. dongle_state = RF15_PAIRINGSMODE;
  430. clone_mode_started();
  431. }
  432. }
  433.  
  434. if(message.getSensor() == CHILD_ID_FAN) // FAN
  435. {
  436. if(message.getType() == V_STATUS) // ON, OFF SWITCH
  437. {
  438. if(!message.getBool()) // FAN OFF
  439. {
  440. prev_fan_speed = radio.current_fan_state.fan_speed; // STORE FAN SPEED (FOR ONLY RECEIVING "V_STATUS = TRUE" NEXT TIME)
  441. fan_speed_req = 0; // SEND "0" TO FAN
  442. update_fan_state(0); // REPORT STATE BACK TO CONTROLLER
  443. }
  444. else
  445. {
  446. fan_speed_req = prev_fan_speed; // SET BACK FAN SPEED TO FAN ONLY
  447. update_fan_state(1); // REPORT BACK TO CONTROLLER
  448. }
  449. }
  450.  
  451. if (message.getType() == V_PERCENTAGE)
  452. {
  453. fan_speed_req = constrain(message.getInt(), 0, 4);
  454. update_fan_speed(fan_speed_req);
  455. }
  456. }
  457.  
  458. if(message.getSensor() == CHILD_ID_BYPASS_MODE) // BYPASS MODE
  459. {
  460. if (message.getType() == V_PERCENTAGE)
  461. {
  462. int16_t temp_var = constrain(message.getInt(), 1, 3);
  463. update_bypass_mode(temp_var);
  464.  
  465. switch (temp_var)
  466. {
  467. case 1: // auto
  468. fan_bypass_mode_req = 0xFF;
  469. break;
  470.  
  471. case 2: // open
  472. fan_bypass_mode_req = 0xC8;
  473. break;
  474.  
  475. case 3: // close
  476. fan_bypass_mode_req = 0x00;
  477. break;
  478.  
  479. default:
  480. break;
  481. }
  482. }
  483. }
  484.  
  485. }
  486.  
  487. //******************************************************************************************//
  488. // //
  489. // Transmit new state //
  490. // //
  491. //******************************************************************************************//
  492. void sendNewSourceAddressToGateway()
  493. {
  494. // Ramses II format
  495. uint32_t source_address = ((uint32_t)radio.new_fan_state.address[3]<<16) | ((uint32_t)radio.new_fan_state.address[4]<<8) | ((uint32_t)radio.new_fan_state.address[5]<<0);
  496. uint8_t device_id = ((source_address & 0xFC0000)>>18);
  497. uint32_t address_id = source_address & 0x03FFFF;
  498. String result_string = String(device_id) + ":" + String(address_id);
  499.  
  500. send(msgSourceAddress.set(result_string.c_str()));
  501. }
  502.  
  503. void sendNewTargetAddressToGateway()
  504. {
  505. // Ramses II format
  506. uint32_t target_address = ((uint32_t)radio.new_fan_state.address[0]<<16) | ((uint32_t)radio.new_fan_state.address[1]<<8) | ((uint32_t)radio.new_fan_state.address[2]<<0);
  507. uint8_t device_id = ((target_address & 0xFC0000)>>18);
  508. uint32_t address_id = target_address & 0x03FFFF;
  509. String result_string = String(device_id) + ":" + String(address_id);
  510.  
  511. send(msgTargetAddress.set(result_string.c_str()));
  512. }
  513.  
  514. void update_fan_speed(int16_t speed_level)
  515. {
  516. send(msgFANspeed.set(speed_level));
  517. }
  518.  
  519. void update_fan_state(int16_t fan_state)
  520. {
  521. send(msgFANstate.set(fan_state));
  522. }
  523.  
  524. void clone_mode_ended(void)
  525. {
  526. send(msgCloneSwitch.set(0));
  527. }
  528.  
  529. void clone_mode_started(void)
  530. {
  531. send(msgCloneSwitch.set(1));
  532. }
  533.  
  534. void update_bypass_mode(int16_t bypass_mode)
  535. {
  536. send(msgBypassMODE_VAL.set(bypass_mode));
  537. }
Advertisement
Add Comment
Please, Sign In to add comment