/* ##DESC_TITLE_STR1## ##VERSION_SCRIPT## ##DELIMITER## Шаблон позволяет добавлять системы кондиционирования производства Hitachi (включая OEM - бренды), с разъемом CN7 или CN1101 по ModBus RTU протоколу в умный дом Larnitech, с помощью шлюза ONOKOM HT-1-MB-B Шаблон разработан техническим отделом ООО "ОНОКОМ" Телефон: +7 (812) 955-59-05 Адрес: СПб, Московское шоссе д.25 корп.1 Новости: t.me/ONOKOM_NEWS Бот: t.me/ONOKOM_bot Сайт: onokom.ru Logo */ #define MAX_QNTY 10 u16 useExtMask = 0; u8 Read_Coils_Command = 0x01; u8 Write_Coils_Command = 0x05; u8 Read_Holding_Command = 0x03; u8 Write_Holding_Command = 0x06; u8 Read_Discrete_Command = 0x02; u8 Read_Input_Command = 0x04; u8 regCounter = 0; u8 ACcounter = 0; u8 flag = 0; u8 ACWidgetData[9]; u8 commandsArray[] = { Read_Coils_Command, Read_Holding_Command, Read_Coils_Command, Read_Discrete_Command, Read_Input_Command}; u8 addrArray[] = { 1, 1, 100, 1, 1 }; u8 qntyArray[] = { 15, 32, 1, 24, 3 }; u8 lastCmd = 0; u16 lastAddr = 0; u8 lastQnty = 0; u8 lastDev = 0; u16 MBaddr_Address = 128; u16 MBaddr_mode = 1; u16 MBaddr_T = 5; u16 MBaddr_fan = 8; u16 MBaddr_VanesH = 9; u16 MBaddr_VanesV = 10; u8 ac_fan; u8 ac_mode; u8 ac_vane; u16 ac_T; u8 acNum; const u16 widgetAC_ID = ADDR2ID(AC1_WIDGET); const u8 widgetAC_arrSubID[] = { ADDR2SID(AC1_WIDGET), ADDR2SID(AC2_WIDGET), ADDR2SID(AC3_WIDGET), ADDR2SID(AC4_WIDGET), ADDR2SID(AC5_WIDGET), ADDR2SID(AC6_WIDGET), ADDR2SID(AC7_WIDGET), ADDR2SID(AC8_WIDGET), ADDR2SID(AC9_WIDGET), ADDR2SID(AC10_WIDGET) }; u8 widgetACconnected_arrSubID[] = { ADDR2SID(AC1_ACCONNECTED), ADDR2SID(AC2_ACCONNECTED), ADDR2SID(AC3_ACCONNECTED), ADDR2SID(AC4_ACCONNECTED), ADDR2SID(AC5_ACCONNECTED), ADDR2SID(AC6_ACCONNECTED), ADDR2SID(AC7_ACCONNECTED), ADDR2SID(AC8_ACCONNECTED), ADDR2SID(AC9_ACCONNECTED), ADDR2SID(AC10_ACCONNECTED) }; u8 widgetRoomTemp_arrSubID[] = { ADDR2SID(AC1_INDTEMP), ADDR2SID(AC2_INDTEMP), ADDR2SID(AC3_INDTEMP), ADDR2SID(AC4_INDTEMP), ADDR2SID(AC5_INDTEMP), ADDR2SID(AC6_INDTEMP), ADDR2SID(AC7_INDTEMP), ADDR2SID(AC8_INDTEMP), ADDR2SID(AC9_INDTEMP), ADDR2SID(AC10_INDTEMP) }; u8 widgetOutHeTemp_arrSubID[] = { ADDR2SID(AC1_OUTHETEMP), ADDR2SID(AC2_OUTHETEMP), ADDR2SID(AC3_OUTHETEMP), ADDR2SID(AC4_OUTHETEMP), ADDR2SID(AC5_OUTHETEMP), ADDR2SID(AC6_OUTHETEMP), ADDR2SID(AC7_OUTHETEMP), ADDR2SID(AC8_OUTHETEMP), ADDR2SID(AC9_OUTHETEMP), ADDR2SID(AC10_OUTHETEMP) }; const u8 deviceAC_arrID[] = { AC1_ADDR, AC2_ADDR, AC3_ADDR, AC4_ADDR, AC5_ADDR, AC6_ADDR, AC7_ADDR, AC8_ADDR, AC9_ADDR, AC10_ADDR }; // ------------------------------------------ #if SEND_PUSH == true #define FLAG_SEND_PUSH 1 #endif #if MODE_SCRIPT == SET_NEW_ID u16 APP_IDThatPress = 2047; u8 Idle_Addr = 1; u8 setup_found_on_1 = 0; u8 setup_wait_verify = 0; #define PUSH_TEXT_LEN 64 #define PUSH_PACKET_LEN 66 // 1-st byte like (1) + 64 text + '\0' u8 pushText[64]; u8 pushPacket[66]; // ---- ПРОБНЫЙ ОПРОС АДРЕСА 1 ---- void setupTimeout() { if (!setup_found_on_1) { #ifdef FLAG_SEND_PUSH setStatus(@APP_IDThatPress:32, {4, "Устройство на адресе 1 не найдено"}); #endif } } void checkSetup() { // Read 1 coil for check for "alive" setStatus(RS485, {Idle_Addr, Read_Coils_Command, 0, 1, 0, 1, 0xcc16}); // If no ack after 1 sec -> "dead" delayedCallMs(setupTimeout, 1000); } // ----- VERIFY NEW MODBUS ADDRESS ----- void verify_timeout() { if (setup_wait_verify) { setup_wait_verify = 0; #ifdef FLAG_SEND_PUSH setStatus(@APP_IDThatPress:32, {4, "Не удалось подтвердить новый адрес, попробуйте ещё раз"}); #endif } } void verify_new_addr() { setup_wait_verify = 1; // Read 1 coil from new address setStatus(RS485, {NEWADDRESS, Read_Coils_Command, 0, 1, 0, 1, 0xcc16}); // If no ack after 2 sec -> unlucky delayedCallMs(verify_timeout, 2000); } void onInit() { setup_found_on_1 = 0; cancelDelayedCall(setupTimeout); checkSetup(); } // Function for forming string for push void sendPushAddressChanged(u8 oldAddr, u8 newAddr) { // Packing as {1, "str", 0} /* 1 - message priority, str - message string, 0 - idk what is it */ sprintf(pushText, "Адрес Modbus изменен: %d -> %d", oldAddr, newAddr); pushPacket[0] = 1; u8 j = 0; for (; j < (64 - 1); ++j) { u8 c = pushText[j]; pushPacket[1 + j] = c; if (c == 0) break; } if (j == (64 - 1)) { pushPacket[1 + j] = 0; } #ifdef FLAG_SEND_PUSH setStatus(@APP_IDThatPress:32, pushPacket); #endif } V-ID/RS485 { // srvMessage("Data from AC 0 = %d, 1 = %d, 2 = %d, 3 = %d, 4 = %d, 5 = %d, 6 = %d", opt(0), opt(1), opt(2), opt(3), opt(4), opt(5), opt(6)); // srvMessage("Data from AC 7 = %d, 8 = %d, 9 = %d, 10 = %d, 11 = %d, 12 = %d", opt(7), opt(8), opt(9), opt(10), opt(11), opt(12)); // srvMessage("Data from AC 13 = %d, 14 = %d, 15 = %d, 16 = %d, 17 = %d, 18 = %d", opt(13), opt(14), opt(15), opt(16), opt(17), opt(18)); // srvMessage("Data from AC 19 = %d, 20 = %d, 21 = %d, 22 = %d, 23 = %d, 24 = %d", opt(19), opt(20), opt(21), opt(22), opt(23), opt(24)); if (opt(0) == Idle_Addr && opt(1) == Read_Coils_Command) { if (!setup_found_on_1) { setup_found_on_1 = 1; cancelDelayedCall(setupTimeout); #ifdef FLAG_SEND_PUSH setStatus(@APP_IDThatPress:32, {1, "Найдено устройство на адресе 1"}); #endif } } // Wait ack from new addr if (setup_wait_verify && opt(0) == NEWADDRESS && opt(1) == Read_Coils_Command) { setup_wait_verify = 0; cancelDelayedCall(verify_timeout); #ifdef FLAG_SEND_PUSH sendPushAddressChanged(OLDADDRESS, NEWADDRESS); #endif srvMessage("Modbus address correctly changed from %d to %d", OLDADDRESS, NEWADDRESS); } } V-ID/CHANGEADDRESS { if (opt(0) & 0x01) { u16 eid = exciterId(); if (eid) APP_IDThatPress = eid; srvMessage("Try to change Modbus address from %d to %d", OLDADDRESS, NEWADDRESS); setStatus(RS485, {OLDADDRESS, Write_Holding_Command, 0, 128, 0, NEWADDRESS, 0xcc16}); delayedCallMs(verify_new_addr, 600); } } #endif #if MODE_SCRIPT == IDLE void clearFlag() { flag = 0; } void setdata() { flag = 1; cancelDelayedCall(clearFlag); delayedCallMs(clearFlag, 2000); } void setFan() { setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Holding_Command, 0, MBaddr_fan & 0xFF, 0, ac_fan & 0xFF, 0xcc16}); } void setT() { setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Holding_Command, 0, MBaddr_T & 0xFF, ac_T >> 8, ac_T & 0xFF, 0xcc16}); } void setMode() { setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Holding_Command, 0, MBaddr_mode & 0xFF, 0, ac_mode & 0xFF, 0xcc16}); } void setVaneV() { setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Holding_Command, 0, MBaddr_VanesH & 0xFF, 0, ac_vane & 0x0F, 0xcc16}); } void setVaneH() { setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Holding_Command, 0, MBaddr_VanesV & 0xFF, 0, (ac_vane >> 4) & 0x0F, 0xcc16}); } void setData_f88 (u16 ID, u8 SID, u16 data) { i8 tempInt; u8 tempFrac; tempInt = data / 10; tempFrac = ((data - tempInt * 10) * 256) / 10; setStatus(@ID:@SID, { tempFrac, tempInt }); } void onInit() { srvMessage("AC total count -> %d", COUNT_USE_AC); } u8 findWidgetBySubID (u8 subid) { for (u8 i = 0; i < COUNT_USE_AC; ++i) { if (subid == widgetAC_arrSubID[i]) return i; } return 0xFF; } u8 findACbyAddress (u8 mbAddress) { for (u8 i = 0; i < COUNT_USE_AC; ++i) { if (mbAddress == deviceAC_arrID[i]) return i; } return 0xFF; } u8 getWidgetModeFromGateway (u8 gatewayMode) { u8 widgetMode = 0; if (gatewayMode == 1) widgetMode = 3; else if (gatewayMode == 2) widgetMode = 1; else if (gatewayMode == 3) widgetMode = 4; else if (gatewayMode == 4) widgetMode = 2; else if (gatewayMode == 5) widgetMode = 0; return widgetMode; } u8 getGatewayModeFromWidget (u8 widgetMode) { u8 gatewayMode = widgetMode; if (widgetMode == 0) gatewayMode = 5; else if (widgetMode == 1) gatewayMode = 2; else if (widgetMode == 2) gatewayMode = 4; else if (widgetMode == 3) gatewayMode = 1; else if (widgetMode == 4) gatewayMode = 3; return gatewayMode; } u8 getFanForWidgetFromGateway (u8 fanGateway) { u8 widgetFan = fanGateway; if (fanGateway == 1) widgetFan = 6; else if (fanGateway == 2) widgetFan = 1; else if (fanGateway == 3) widgetFan = 2; else if (fanGateway == 4) widgetFan = 3; else if (fanGateway == 5) widgetFan = 4; return widgetFan; } u8 getFanForGatewayFromWidget (u8 fanWidget) { u8 fanGateway = fanWidget; if (fanWidget == 1) fanGateway = 2; else if (fanWidget == 2) fanGateway = 3; else if (fanWidget == 3) fanGateway = 4; else if (fanWidget == 4) fanGateway = 5; else if (fanWidget == 6) fanGateway = 1; return fanGateway; } u8 getVanesForWidgetFromGateway (u8 vaneGateway) { u8 widgetVaneH = 0; u8 widgetVaneV = 0; u8 vaneGatewayH = vaneGateway & 0x0F; u8 vaneGatewayV = (vaneGateway >> 4) & 0x0F; if (vaneGatewayH == 0) widgetVaneH = 0; else if (vaneGatewayH == 1) widgetVaneH = 6; else widgetVaneH = 7 - vaneGatewayH; if (vaneGatewayV == 0) widgetVaneV = 5; else if (vaneGatewayV == 1) widgetVaneV = 6; else widgetVaneV = vaneGatewayV-2; return ((widgetVaneH << 4) | widgetVaneV); } u8 getVanesForGatewayFromWidget (u8 vaneWidget) { u8 gatewayVaneH = 0; u8 gatewayVaneV = 0; u8 vaneWidgetV = vaneWidget & 0x0F; u8 vaneWidgetH = (vaneWidget >> 4) & 0x0F; if (vaneWidgetH == 0) gatewayVaneH = 0; else if (vaneWidgetH == 6) gatewayVaneH = 1; else gatewayVaneH = 7 - vaneWidgetH; if (vaneWidgetV == 5) gatewayVaneV = 0; else if (vaneWidgetV == 6) gatewayVaneV = 1; else gatewayVaneV = vaneWidgetV + 2; return ((gatewayVaneV << 4) | gatewayVaneH); } V-ID/AC1_WIDGET#ifdef AC2_ADDR,AC2_WIDGET#endif#ifdef AC3_ADDR,AC3_WIDGET#endif#ifdef AC4_ADDR,AC4_WIDGET#endif#ifdef AC5_ADDR,AC5_WIDGET#endif#ifdef AC6_ADDR,AC6_WIDGET#endif#ifdef AC7_ADDR,AC7_WIDGET#endif#ifdef AC8_ADDR,AC8_WIDGET#endif#ifdef AC9_ADDR,AC9_WIDGET#endif#ifdef AC10_ADDR,AC10_WIDGET#endif { if (exciterId() != widgetAC_ID) { acNum = findWidgetBySubID(senderSubId()); srvMessage("Vane total %d, hor - %d, ver - %d", opt(3), opt(3)&0x0f, (opt(3)>>4)&0x0f); if (acNum != 0xFF) { srvMessage("Set WIDGET AC %d(%d) onoff to -> %d", acNum, deviceAC_arrID[acNum], (opt(0) & 0x01) * 0xFF); ac_T = opt(2) * 100; ac_mode = getGatewayModeFromWidget((opt(0) >> 4) & 0x0F); ac_fan = getFanForGatewayFromWidget(opt(4)); ac_vane = getVanesForGatewayFromWidget(opt(3)); setdata(); setStatus(RS485, {deviceAC_arrID[acNum], Write_Coils_Command, 0, 1, (opt(0) & 0x01) * 0xFF, 0, 0xcc16}); cancelDelayedCall(setFan); delayedCallMs(setFan, 250); cancelDelayedCall(setT); delayedCallMs(setT, 500); cancelDelayedCall(setMode); delayedCallMs(setMode, 750); cancelDelayedCall(setVaneH); delayedCallMs(setVaneH, 1000); cancelDelayedCall(setVaneV); delayedCallMs(setVaneV, 1250); } } } V-ID/AC1_ACCONNECTED#ifdef AC2_ADDR,AC2_ACCONNECTED#endif#ifdef AC3_ADDR,AC3_ACCONNECTED#endif#ifdef AC4_ADDR,AC4_ACCONNECTED#endif#ifdef AC5_ADDR,AC5_ACCONNECTED#endif#ifdef AC6_ADDR,AC6_ACCONNECTED#endif#ifdef AC7_ADDR,AC7_ACCONNECTED#endif#ifdef AC8_ADDR,AC8_ACCONNECTED#endif#ifdef AC9_ADDR,AC9_ACCONNECTED#endif#ifdef AC10_ADDR,AC10_ACCONNECTED#endif { if (exciterId() != widgetAC_ID) { u8 widgetNum = 0xFF; for (u8 i = 0; i < COUNT_USE_AC; ++i) { if (senderSubId() == widgetACconnected_arrSubID[i]) widgetNum = i; } if (widgetNum != 0xFF) { srvMessage("Set ACconnected AC %d(%d) to -> %d", widgetNum, deviceAC_arrID[widgetNum], (opt(0) & 0x01) * 0xFF); setdata(); setStatus(RS485, {deviceAC_arrID[widgetNum], Write_Coils_Command, 0, 20, (opt(0) & 0x01) * 0xFF, 0, 0xcc16}); } } } V-ID/s:1 { if (!flag) { if (++regCounter == sizeof(commandsArray)) { regCounter = 0; if (++ACcounter == COUNT_USE_AC) ACcounter = 0; } lastCmd = commandsArray[regCounter]; lastAddr = addrArray[regCounter]; lastQnty = qntyArray[regCounter]; lastDev = deviceAC_arrID[ACcounter]; setStatus(RS485, {deviceAC_arrID[ACcounter], commandsArray[regCounter], (addrArray[regCounter] >> 8) & 0xFF, addrArray[regCounter] & 0xFF, 0, qntyArray[regCounter], 0xcc16}); //srvMessage("Request to AC id%d, com %d, addr %d, qnty %d", deviceAC_arrID[ACcounter], commandsArray[regCounter], addrArray[regCounter], qntyArray[regCounter]); } } u8 errorMSG[50]; V-ID/RS485 { u8 ACnumInArray = findACbyAddress(opt(0)); if (ACnumInArray == 0xFF) return; getStatus(@widgetAC_ID:@widgetAC_arrSubID[ACnumInArray], &ACWidgetData); //srvMessage("Data from AC %d(%d), %d, %d", opt(0), ACnumInArray, widgetAC_ID, widgetAC_arrSubID[ACnumInArray]); // === COILS (0x01): addr 0, count 15 OR addr 100, count 1 === if ( (opt(1) == Read_Coils_Command) && !flag ) { // Read all modes and funcs if(lastAddr == 1) { srvMessage("Addr 1"); setStatus(@widgetAC_ID:@widgetAC_arrSubID[ACnumInArray], { (ACWidgetData[0] & 0x0E) | (opt(3) & 0x01) } ); setStatus(@widgetAC_ID:@widgetACconnected_arrSubID[ACnumInArray], (opt(5) >> 3) & 0x01); // } else if(lastAddr == 100) { //read coil 100 "use external temp sensor" if (opt(3) & 0x01) useExtMask |= (1 << ACnumInArray); else useExtMask &= ~(1 << ACnumInArray); } } // === HOLDING (0x03): addr 1, count 32 === else if ( (opt(1) == Read_Holding_Command) && (opt(2) == (qntyArray[1] * 2)) && !flag ) { if(lastAddr == 1) { //srvMessage("Data from AC 0 = %d, 1 = %d, 2 = %d, 3 = %d, 4 = %d, 5 = %d, 6 = %d", opt(0), opt(1), opt(2), opt(3), opt(4), opt(5), opt(6)); setStatus(@widgetAC_ID:@widgetAC_arrSubID[ACnumInArray], {(ACWidgetData[0] & 0x0F) | (getWidgetModeFromGateway(opt(4)) << 4), 0, ((opt(11) << 8) | opt(12)) / 100, getVanesForWidgetFromGateway((opt(22) << 4) | (opt(20) & 0x0F)), getFanForWidgetFromGateway(opt(18))}); // Update "hor.swing" button state u8 verVaneGateway = (opt(20) & 0x0F); u8 isSwing = (verVaneGateway == 1); // "1" is swing u16 roomRaw = ((opt(7) << 8) | opt(8)); u16 extRaw = ((opt(51) << 8) | opt(52)); u16 displayRaw = (useExtMask & (1 << ACnumInArray)) ? extRaw : roomRaw; setData_f88(widgetAC_ID, widgetRoomTemp_arrSubID[ACnumInArray], displayRaw / 10); setData_f88(widgetAC_ID, widgetRoomTemp_arrSubID[ACnumInArray], ((opt(7) << 8) | opt(8)) / 10); setData_f88(widgetAC_ID, widgetOutHeTemp_arrSubID[ACnumInArray], ((opt(49) << 8) | opt(50)) / 10); } } } #endif