This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:tech:gw60 [2021/01/19 08:37] – bullar | en:tech:gw60 [2022/09/26 09:59] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
=====GW60 Rollo Controller Upgrade===== | =====GW60 Rollo Controller Upgrade===== | ||
- | under preparation... | + | ====Motivation==== |
+ | If you want to include the shutters in home automation, you need a corresponding control. If you don't want to retrofit the roller shutters yourself, you can switch to electronic belt winders. These are fully developed but require an external power supply. There are several manufacturers on the market who also have a wireless remote control in their range. Almost all of them have in common that they rely on their own standards and need a headquarters from the respective manufacturer. If you want to avoid this, you have to find your own, open solution. | ||
+ | {{ : | ||
+ | {{: | ||
+ | |||
+ | An inexpensive electronic belt winder is the [[https:// | ||
+ | There are also a few remodeling solutions on the Internet that simulate the keystrokes, e.g. with the help of a {{https:// | ||
+ | |||
+ | The dream would be a module that fits into the space provided in the GW60 housing and can also transmit the current position. A return channel is actually already available at the module interface, since RxD and TxD of the GW60 controller go to the receiving module. The applied voltage of 5V can only be loaded with significantly less than 100mA. An ESP8266 is out of the question. | ||
+ | |||
+ | Until the dream came true, I had to go on the new roller blinds [[https:// | ||
+ | |||
+ | ==== Hardware ==== | ||
+ | The first task is to connect the TRÅDFRI module to the GW60 so that it looks mechanically and electrically like the original ZB40 receiver. The TRÅDFRI module works with 3.3V while the GW60 controller is supplied with 5V. The circuit diagram must therefore contain two level shifters and a voltage regulator. | ||
+ | My solution looks like this: | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | On the left, the connection to the GW60 is originally designed as a spring contact. Unfortunately I couldn' | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | On the right, the connections for module programming are designed as pads. This allows the TRÅDFRI module to be flashed. After all, you don't want to buy an expensive roller blind control from IKEA every time to get hold of a module for your own solution. But more about that in another [[de: | ||
+ | The button and the LED are provided for connection to the TRÅDFRI control unit. Once trained, we no longer need either. The available space is sufficient to accommodate all components. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | In order for the cover to fit on the housing, the PCB should be only 1mm thick like the original. | ||
+ | |||
+ | ==== Housing ==== | ||
+ | With a caliper, the housing is quickly created in Fusion360 and then looks like this: | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | As shown, it can be printed in the 3D printer without support structures. The small block on the right serves to protect the POGO pins and can be pushed onto them. | ||
+ | |||
+ | {{: | ||
+ | {{: | ||
+ | |||
+ | The PCB antenna looks out of the housing for better reception. Although the module disappears in the belt case when the GW60 is installed, I did not have any connection problems with the TRÅDFRI buttons or the gateway. | ||
+ | |||
+ | ==== Software ==== | ||
+ | |||
+ | The TRÅDFRI module is now electrically and mechanically connected to the GW60, but the GW60 controller cannot yet do anything with the data. To do this, the existing firmware must be expanded. The GW60 is controlled by a PIC16F690 with an on-board programming interface. The PICkit3, which is also available as a China replica, is an inexpensive programmer. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | To be able to read out the original firmware first, we have to connect the GW60 board to the PICkit3. The test pads on the back of the GW60 PCB are very helpful. There you can easily solder the 5 cables. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | MICROCHIP provides a free IDE with the name [[https:// | ||
+ | Fortunately, | ||
+ | |||
+ | ==== Extension ==== | ||
+ | The real part of the work now again consisted of reverse engineering. There is enough space in the FLASH of the PIC16F690 for expansion. The area '' | ||
+ | - INIT -> is called once during the initialization. | ||
+ | - ISR -> extension of the interrupt service routine | ||
+ | - LOOP -> Main loop | ||
+ | - PANEL -> Simulation of the keystrokes | ||
+ | |||
+ | To do this, the code is exchanged in the following places (a 16-bit word each): | ||
+ | < | ||
+ | | ||
+ | ------------ | ||
+ | ; ------------------------------------------------------ | ||
+ | if 1 ; 1 = include extension | ||
+ | | ||
+ | else | ||
+ | | ||
+ | endif | ||
+ | ; ------------------------------------------------------ | ||
+ | |||
+ | ; ------------------------------------------------------ | ||
+ | if 1 ; 1 = include extension | ||
+ | | ||
+ | else | ||
+ | | ||
+ | endif | ||
+ | ; ------------------------------------------------------ | ||
+ | |||
+ | ; ------------------------------------------------------ | ||
+ | if 1 ; 1 = include extension | ||
+ | | ||
+ | else | ||
+ | | ||
+ | endif | ||
+ | ; ------------------------------------------------------ | ||
+ | |||
+ | ; ------------------------------------------------------ | ||
+ | if 1 ; 1 = include extension=1 | ||
+ | | ||
+ | else | ||
+ | | ||
+ | endif | ||
+ | ; ------------------------------------------------------- | ||
+ | </ | ||
+ | |||
+ | Attention! The addresses are 16-bit word addresses. The lower line shows the original code and the line above the code to be patched. Of course, this only applies to the software version I have. However, in contrast to the PCB, the software was identical in all of the GW60s I bought at different times. | ||
+ | With the exchange, the controller jumps into our extension software and comes back as if nothing had happened. This means that the entire functionality of the GW60 is retained. | ||
+ | |||
+ | The size of the extension code is 374 words and is simply listed here: | ||
+ | < | ||
+ | ADDR 0600h - 0775h | ||
+ | ================== | ||
+ | 7C18 3014 7C10 FC18 B014 FC10 7C1B 2526 0800 AC00 FC17 0800 FC01 C401 8316 E401 9B30 E500 6530 9800 | ||
+ | 0830 9B00 4030 9900 0330 9A00 8312 B517 0800 8312 0313 FC1F 232E FC13 9226 6400 0800 5626 8316 3608 | ||
+ | E402 031D 2F2E 8312 7C13 FC14 0800 8312 FC1A 362E 0318 2B2E 8312 0800 0318 342E 2B2E 2030 2C06 031D | ||
+ | 0800 8316 3108 8312 E021 8316 3208 8312 E021 8316 3308 8312 E021 8316 3408 8312 E021 8316 3808 8312 | ||
+ | E021 8316 3908 8312 E021 0800 8316 3108 EA00 3208 EB00 3808 EA02 031C EB03 3908 EB02 FF30 6B06 031D | ||
+ | 672E EA01 EB01 E701 2B27 3108 EC00 3208 ED00 3308 EC02 031C ED03 3408 ED02 5C27 6A08 E400 8312 0800 | ||
+ | 0030 E021 FF30 E021 D830 E021 6430 E021 8316 FF30 8312 E021 8316 5308 8312 E021 8316 6408 8312 E021 | ||
+ | 8316 6508 8312 E021 8312 0800 4408 031D 9A2E 2C08 031D C72E C40A C72E 4403 031D A32E 2C0A 031D C62E | ||
+ | 982E C401 C72E 0230 4406 031D AC2E 9A30 2C06 031D C62E 982E 0630 4402 0318 B72E 0330 4406 031D B72E | ||
+ | 2C08 C000 982E 0430 4406 031D BE2E 2C08 C100 982E 0530 4406 031D C62E 2C08 C200 C826 C72E C401 0800 | ||
+ | C401 0A30 4006 031D EB2E DD30 4106 031D D72E D730 4206 031D D72E 0327 0800 EE30 4106 031D E12E E430 | ||
+ | 4206 031D E12E 0627 0800 CC30 4106 031D EB2E C630 4206 031D EB2E 0927 0800 CC30 4006 031D F82E CC30 | ||
+ | 4106 031D F82E 4208 031D F82E 0C27 0800 DD30 4006 031D 022F DD30 4106 4206 031D 022F 0F27 0800 7C14 | ||
+ | 7C13 0800 FC14 7C13 0800 FC14 7C13 0800 5626 7826 0800 6430 4106 0319 062F 4108 0319 032F 4108 8316 | ||
+ | B600 8312 5626 8316 3608 E402 0319 0800 7C17 031C 272F 8312 FC16 7C14 0800 8312 FC12 FC14 0800 0310 | ||
+ | EA0D EB0D E701 E70D EA0D EB0D E70D 6708 E800 6B08 ED00 6A08 EC00 0310 EC0D ED0D E80D EC0D ED0D E80D | ||
+ | EC0D ED0D E80D 6C08 EA07 6D08 0318 6D0F EB07 6808 0318 680F E707 0310 EC0D ED0D E80D 6C08 EA07 6D08 | ||
+ | 0318 6D0F EB07 6808 0318 680F E707 0800 E601 E901 1830 CA00 6A0D EB0D E70D E90D E60D EA0D 6C08 E902 | ||
+ | 6D08 031C 6D0F E602 0318 6A14 6A18 732F E607 6C08 E907 CA0B 602F 0800 | ||
+ | </ | ||
+ | |||
+ | The extensions in the four areas mentioned above have the following function: | ||
+ | == INIT == | ||
+ | The own variables are initialized here and the baud rate is set to 2400. The execution takes place at the end of the INIT sequence of the original software. | ||
+ | == ISR == | ||
+ | The original software is already receiving bytes from the UART. Here we only have to signal to the extension with a flag that a new byte has been received. Here, too, the expansion at the end of the ISR can be heard. | ||
+ | == PANEL == | ||
+ | The keystrokes are simulated here when a corresponding command came from the TRÅDFRI module. At the same time, the STOP button is simulated during the run when the preselected intermediate position is reached. | ||
+ | == LOOP == | ||
+ | Here the evaluation of a received frame takes place and on request the status frame with the current position is sent. | ||
+ | |||
+ | The ASM file of the extension contains more explanations: | ||
+ | <file asm GW60_ext.asm> | ||
+ | ; File: | ||
+ | ; Target: | ||
+ | ; Author: | ||
+ | ; Date: | ||
+ | ; Compiler: pic-as (v2.31) | ||
+ | ; IDE: MPLABX v5.45 | ||
+ | ; =========================================================================== | ||
+ | ; ### Extension code for GW60 firmware to support serial TRÅDFRI protocol ### | ||
+ | ; =========================================================================== | ||
+ | ; Free code area in original F/W: | ||
+ | ; | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | ; Free RAM area in original F/W: | ||
+ | ; | ||
+ | ; | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | ; Used RAM in extention: | ||
+ | ; | ||
+ | ; BANK0: | ||
+ | ; | ||
+ | ; bit 0: 1 = UP command | ||
+ | ; bit 1: 1 = DOWN command | ||
+ | ; ... | ||
+ | ; bit 5: 0 = DOWN, 1 = UP direction of new position | ||
+ | ; bit 6: 1 = new position to go to | ||
+ | ; bit 7: 1 = new byte received | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; BANK1: | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; RAM used by original F/W: | ||
+ | ; | ||
+ | ; | ||
+ | ; 0x01 = DOWN | ||
+ | ; 0x02 = UP | ||
+ | ; 0x04 = SUN | ||
+ | ; 0x08 = CLOCK | ||
+ | ; 0x10 = SET | ||
+ | ; 0x20 = REED | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | PROCESSOR 16F690 | ||
+ | # | ||
+ | # | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | ; Globals | ||
+ | GLOBAL isr_ext, init_ext, loop_ext, panel_ext | ||
+ | EXTRN sub_send_byte | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | PSECT ext, | ||
+ | ORG 0x0600 | ||
+ | |||
+ | IF EXTEND=1 | ||
+ | |||
+ | ; =========================================================================== | ||
+ | ; called by F/W at each button read out from panel | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | panel_ext: | ||
+ | btfsc RAM_ext_stat, | ||
+ | bsf byte_DATA_30, | ||
+ | bcf RAM_ext_stat, | ||
+ | btfsc RAM_ext_stat, | ||
+ | bsf byte_DATA_30, | ||
+ | bcf RAM_ext_stat, | ||
+ | btfsc RAM_ext_stat, | ||
+ | call on_pos ; | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | return ; (replaced instruction) | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | |||
+ | ; =========================================================================== | ||
+ | ; called from interrupt service routine | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | isr_ext: | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | movwf byte_DATA_2C ; | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | bsf RAM_ext_stat, | ||
+ | return | ||
+ | |||
+ | ; =========================================================================== | ||
+ | ; called by firmware once at the end of init phase | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | init_ext: | ||
+ | |||
+ | clrf RAM_ext_stat ; | ||
+ | clrf RAM_frm_cnt ; | ||
+ | bsf RP0 | ||
+ | clrf RAM_p_stat ; | ||
+ | movlw 0x9B ; | ||
+ | movwf RAM_chksum | ||
+ | |||
+ | ; | ||
+ | ; | ||
+ | movlw 0x65 ; | ||
+ | movwf TXSTA ; | ||
+ | movlw 0x08 | ||
+ | movwf BAUDCTL ; | ||
+ | movlw 0x40 ; | ||
+ | movwf SPBRG | ||
+ | movlw 0x03 ; | ||
+ | movwf SPBRGH | ||
+ | |||
+ | ; | ||
+ | ; | ||
+ | |||
+ | bcf RP0 | ||
+ | init_exit: | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | bsf byte_DATA_35, | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | return | ||
+ | |||
+ | ; =========================================================================== | ||
+ | ; called from main loop | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | loop_ext: | ||
+ | bcf RP0 | ||
+ | bcf RP1 | ||
+ | |||
+ | ; new byte received ? | ||
+ | btfss RAM_ext_stat, | ||
+ | goto loop_exit ; | ||
+ | bcf RAM_ext_stat, | ||
+ | call receive_frame ; | ||
+ | | ||
+ | ;##### 6-byte RAM monitor | ||
+ | ; | ||
+ | loop_exit: | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | clrwdt ; replaced instruction from original code | ||
+ | ; --------------------------------------------------------------------------- | ||
+ | return | ||
+ | |||
+ | |||
+ | ; =========================================================================== | ||
+ | ; Subroutines | ||
+ | ; =========================================================================== | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; check if on position | ||
+ | on_pos: | ||
+ | call calcpos | ||
+ | bsf RP0 | ||
+ | movf RAM_newpos, | ||
+ | subwf RAM_p_stat | ||
+ | btfss ZERO ; | ||
+ | goto notpos | ||
+ | reached: | ||
+ | bcf RP0 | ||
+ | bcf RAM_ext_stat, | ||
+ | bsf RAM_ext_stat, | ||
+ | return | ||
+ | |||
+ | notpos: bcf RP0 | ||
+ | btfsc RAM_ext_stat, | ||
+ | goto its_up | ||
+ | btfsc CARRY | ||
+ | goto reached | ||
+ | |||
+ | posdone: | ||
+ | bcf RP0 | ||
+ | return | ||
+ | |||
+ | its_up: | ||
+ | goto posdone | ||
+ | goto reached | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; debug output only | ||
+ | ; | ||
+ | monitor: | ||
+ | movlw 0x20 ; | ||
+ | xorwf byte_DATA_2C, | ||
+ | btfss STATUS, | ||
+ | return | ||
+ | | ||
+ | bsf RP0 | ||
+ | movf byte_DATA_B1, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | bsf RP0 | ||
+ | movf byte_DATA_B2, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | bsf RP0 | ||
+ | movf byte_DATA_B3, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | bsf RP0 | ||
+ | movf byte_DATA_B4, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | bsf RP0 | ||
+ | movf byte_DATA_B8, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | bsf RP0 | ||
+ | movf byte_DATA_B9, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | |||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; calc position in % | ||
+ | ; | ||
+ | calcpos: | ||
+ | ; upper limit - actual position | ||
+ | bsf RP0 | ||
+ | movf byte_DATA_B1, | ||
+ | movwf RAM_div16_tmp1_l | ||
+ | movf byte_DATA_B2, | ||
+ | movwf RAM_div16_tmp1_m | ||
+ | MOVF byte_DATA_B8, | ||
+ | SUBWF RAM_div16_tmp1_l, | ||
+ | BTFSS CARRY | ||
+ | DECF RAM_div16_tmp1_m, | ||
+ | MOVF byte_DATA_B9, | ||
+ | SUBWF RAM_div16_tmp1_m, | ||
+ | ; check if actual position was higher then the upper limit | ||
+ | movlw 0xFF | ||
+ | xorwf RAM_div16_tmp1_m, | ||
+ | btfss ZERO | ||
+ | goto noff | ||
+ | clrf RAM_div16_tmp1_l | ||
+ | clrf RAM_div16_tmp1_m | ||
+ | ; upper limit - lower limit = range | ||
+ | noff: | ||
+ | clrf RAM_div16_tmp1_mm ; | ||
+ | |||
+ | ; multiply with 100 | ||
+ | call mul100 | ||
+ | |||
+ | movf byte_DATA_B1, | ||
+ | movwf RAM_div16_tmp2_l | ||
+ | movf byte_DATA_B2, | ||
+ | movwf RAM_div16_tmp2_m | ||
+ | MOVF byte_DATA_B3, | ||
+ | SUBWF RAM_div16_tmp2_l, | ||
+ | BTFSS CARRY | ||
+ | DECF RAM_div16_tmp2_m, | ||
+ | MOVF byte_DATA_B4, | ||
+ | SUBWF RAM_div16_tmp2_m, | ||
+ | |||
+ | ;devide by range | ||
+ | call div16bit | ||
+ | |||
+ | movf RAM_div16_tmp1_l, | ||
+ | movwf RAM_p_stat | ||
+ | |||
+ | bcf RP0 | ||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; send STATUS: | ||
+ | ; 00 FF D8 64 FF 00 00 9B | ||
+ | ; +--------------> | ||
+ | ; +-----------> | ||
+ | ; | ||
+ | ; | ||
+ | send_status: | ||
+ | movlw 0x00 ; | ||
+ | call sub_send_byte ; | ||
+ | movlw 0xFF ; | ||
+ | call sub_send_byte ; | ||
+ | movlw 0xD8 ; | ||
+ | call sub_send_byte ; | ||
+ | movlw 0x64 ; | ||
+ | call sub_send_byte ; | ||
+ | bsf RP0 | ||
+ | movlw 0xFF ; | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | bsf RP0 | ||
+ | movf byte_DATA_53, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | bsf RP0 | ||
+ | movf RAM_p_stat, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | bsf RP0 | ||
+ | movf RAM_chksum, | ||
+ | bcf RP0 | ||
+ | call sub_send_byte ; | ||
+ | bcf RP0 | ||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; IKEA Tradfri commands: | ||
+ | ; UP 0x00 0xFF 0x9A 0x0A 0xDD 0xD7 00FF9A0ADDD7 | ||
+ | ; DOWN 0x00 0xFF 0x9A 0x0A 0xEE 0xE4 | ||
+ | ; STOP 0x00 0xFF 0x9A 0x0A 0xCC 0xC6 00FF9A0ACCC6 | ||
+ | ; GOTO 0x00 0xFF 0x9A 0XDD (POS) (CS) 00FF9ADD32EF | ||
+ | ; Status reqest 0x00 0xFF 0x9A 0xCC 0xCC 0x00 00FF9ACCCC00 | ||
+ | receive_frame: | ||
+ | ; if (RAM_frm_cnt == 0) | ||
+ | movf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1650 | ||
+ | ; if (RAM_rxd_byte == 0x00) | ||
+ | movf byte_DATA_2C, | ||
+ | btfss STATUS, | ||
+ | goto loc_142 | ||
+ | loc_1648: | ||
+ | ; RAM_frm_cnt++ | ||
+ | incf RAM_frm_cnt | ||
+ | goto loc_142 | ||
+ | ; else if (RAM_frm_cnt == 1) | ||
+ | loc_1650: | ||
+ | decf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1658 | ||
+ | ; if (RAM_rxd_byte == 0xFF) | ||
+ | incf byte_DATA_2C, | ||
+ | btfss STATUS, | ||
+ | goto loc_1682 | ||
+ | goto loc_1648 | ||
+ | loc_1656: | ||
+ | ; else; | ||
+ | clrf RAM_frm_cnt | ||
+ | goto loc_142 | ||
+ | loc_1658: | ||
+ | ; else if (RAM_frm_cnt == 2) | ||
+ | movlw 2 | ||
+ | xorwf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1666 | ||
+ | ; if (RAM_rxd_byte == 0x9A) | ||
+ | movlw 0x9A | ||
+ | xorwf byte_DATA_2C, | ||
+ | btfss STATUS, | ||
+ | goto loc_1682 | ||
+ | goto loc_1648 | ||
+ | loc_1666: | ||
+ | ; else if (RAM_frm_cnt < 6) | ||
+ | movlw 6 | ||
+ | subwf RAM_frm_cnt, | ||
+ | BTFSC CARRY | ||
+ | goto loc_1672 | ||
+ | loc_1668: | ||
+ | ; if (RAM_frm_cnt == 3) | ||
+ | movlw 3 | ||
+ | xorwf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1672 | ||
+ | ; RAM_cmd1 = RAM_rxd_byte | ||
+ | movf byte_DATA_2C, | ||
+ | movwf RAM_cmd1 | ||
+ | goto loc_1648 | ||
+ | loc_1672: | ||
+ | ; if (RAM_frm_cnt == 4) | ||
+ | movlw 4 | ||
+ | xorwf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1676 | ||
+ | ; RAM_cmd2 = RAM_rxd_byte; | ||
+ | movf byte_DATA_2C, | ||
+ | movwf RAM_cmd2 | ||
+ | goto loc_1648 | ||
+ | loc_1676: | ||
+ | ; if (RAM_frm_cnt == 5) | ||
+ | movlw 5 | ||
+ | xorwf RAM_frm_cnt, | ||
+ | btfss STATUS, | ||
+ | goto loc_1682 | ||
+ | ; RAM_cmd3 = RAM_rxd_byte; | ||
+ | movf byte_DATA_2C, | ||
+ | movwf RAM_cmd3 | ||
+ | ; decode_frame(); | ||
+ | call decode_frame | ||
+ | goto loc_142 | ||
+ | loc_1682: | ||
+ | ; else RAM_frm_cnt = 0; | ||
+ | clrf RAM_frm_cnt | ||
+ | loc_142: | ||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | decode_frame: | ||
+ | ; RAM_frm_cnt = 0; | ||
+ | clrf RAM_frm_cnt | ||
+ | ; if (RAM_cmd1 == 0x0A) { | ||
+ | movlw 0x0A | ||
+ | xorwf RAM_cmd1, | ||
+ | btfss STATUS, | ||
+ | goto loc_1767 | ||
+ | ; if (RAM_cmd2 == 0xDD && RAM_cmd3 == 0xD7) UP(); | ||
+ | movlw 0xDD | ||
+ | xorwf RAM_cmd2, | ||
+ | btfss STATUS, | ||
+ | goto loc_1755 | ||
+ | movlw 0xD7 | ||
+ | xorwf RAM_cmd3, | ||
+ | btfss STATUS, | ||
+ | goto loc_1755 | ||
+ | call cmd_up | ||
+ | return | ||
+ | loc_1755: | ||
+ | ; if (RAM_cmd2 == 0xEE && RAM_cmd3 == 0xE4) DOWN(); | ||
+ | movlw 0xEE | ||
+ | xorwf RAM_cmd2, | ||
+ | btfss STATUS, | ||
+ | goto loc_1761 | ||
+ | movlw 0xE4 | ||
+ | xorwf RAM_cmd3, | ||
+ | btfss STATUS, | ||
+ | goto loc_1761 | ||
+ | call cmd_down | ||
+ | return | ||
+ | loc_1761: | ||
+ | ; if (RAM_cmd2 == 0xCC && RAM_cmd3 == 0xC6) STOP(); | ||
+ | movlw 0xCC | ||
+ | xorwf RAM_cmd2, | ||
+ | btfss STATUS, | ||
+ | goto loc_1767 | ||
+ | movlw 0xC6 | ||
+ | xorwf RAM_cmd3, | ||
+ | btfss STATUS, | ||
+ | goto loc_1767 | ||
+ | call cmd_stop | ||
+ | return | ||
+ | loc_1767: | ||
+ | ; } if (RAM_cmd1 == 0xCC) { | ||
+ | movlw 0xCC | ||
+ | xorwf RAM_cmd1, | ||
+ | btfss STATUS, | ||
+ | goto loc_1775 | ||
+ | ; if (RAM_cmd2 == 0xCC && RAM_cmd3 == 0x00) STAT(); | ||
+ | movlw 0xCC | ||
+ | xorwf RAM_cmd2, | ||
+ | btfss STATUS, | ||
+ | goto loc_1775 | ||
+ | movf RAM_cmd3, | ||
+ | btfss STATUS, | ||
+ | goto loc_1775 | ||
+ | call cmd_stat | ||
+ | return | ||
+ | loc_1775: | ||
+ | ; } if (RAM_cmd1 == 0xDD) { | ||
+ | movlw 0xDD | ||
+ | xorwf RAM_cmd1, | ||
+ | btfss STATUS, | ||
+ | goto loc_1781 | ||
+ | ; if ((RAM_cmd2 ^ 221) == RAM_cmd3) GOTO(); | ||
+ | movlw 0xDD | ||
+ | xorwf RAM_cmd2, | ||
+ | xorwf RAM_cmd3, | ||
+ | btfss STATUS, | ||
+ | goto loc_1781 | ||
+ | call cmd_goto | ||
+ | loc_1781: | ||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Command: UP | ||
+ | cmd_up: | ||
+ | bsf RAM_ext_stat, | ||
+ | bcf RAM_ext_stat, | ||
+ | ; | ||
+ | ; | ||
+ | return | ||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Command: DOWN | ||
+ | cmd_down: | ||
+ | bsf RAM_ext_stat, | ||
+ | bcf RAM_ext_stat, | ||
+ | ; | ||
+ | ; | ||
+ | return | ||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Command: STOP | ||
+ | cmd_stop: | ||
+ | bsf RAM_ext_stat, | ||
+ | bcf RAM_ext_stat, | ||
+ | ; | ||
+ | ; | ||
+ | return | ||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Command: STATUS | ||
+ | cmd_stat: | ||
+ | call calcpos | ||
+ | call send_status | ||
+ | return | ||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Command: GOTO position | ||
+ | ; new position in RAM_cmd2 | ||
+ | cmd_goto: | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | ; | ||
+ | movlw 0x64 | ||
+ | xorwf RAM_cmd2, | ||
+ | btfsc STATUS, | ||
+ | goto cmd_down ; | ||
+ | movf RAM_cmd2, | ||
+ | btfsc STATUS, | ||
+ | goto cmd_up ; | ||
+ | |||
+ | movf RAM_cmd2, | ||
+ | bsf RP0 | ||
+ | movwf RAM_newpos | ||
+ | bcf RP0 | ||
+ | call calcpos | ||
+ | bsf RP0 | ||
+ | movf RAM_newpos, | ||
+ | subwf RAM_p_stat | ||
+ | btfsc ZERO ; | ||
+ | return | ||
+ | bsf RAM_ext_stat, | ||
+ | btfss CARRY | ||
+ | goto go_dn | ||
+ | |||
+ | go_up: | ||
+ | bsf RAM_ext_stat, | ||
+ | bsf RAM_ext_stat, | ||
+ | return | ||
+ | |||
+ | go_dn: | ||
+ | bcf RAM_ext_stat, | ||
+ | bsf RAM_ext_stat, | ||
+ | return | ||
+ | |||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Multiplication * 100 | ||
+ | ; Author: Generator | ||
+ | ; From: http:// | ||
+ | ; | ||
+ | ; ALGORITHM: | ||
+ | ; Clear accumulator | ||
+ | ; Add input * 64 to accumulator | ||
+ | ; Add input * 32 to accumulator | ||
+ | ; Add input * 4 to accumulator | ||
+ | ; Move accumulator to result | ||
+ | ; Approximated constant: 100, Error: 0 % | ||
+ | ; Input: RAM_div16_tmp1_l/ | ||
+ | ; Output: RAM_div16_tmp1_l/ | ||
+ | ; Code size: 48 instructions | ||
+ | mul100: | ||
+ | ;shift accumulator left 2 times | ||
+ | bcf CARRY | ||
+ | rlf RAM_div16_tmp1_l, | ||
+ | rlf RAM_div16_tmp1_m, | ||
+ | clrf RAM_div16_tmp1_mm | ||
+ | rlf RAM_div16_tmp1_mm, | ||
+ | rlf RAM_div16_tmp1_l, | ||
+ | rlf RAM_div16_tmp1_m, | ||
+ | rlf RAM_div16_tmp1_mm, | ||
+ | ;copy accumulator to temporary | ||
+ | movf RAM_div16_tmp1_mm, | ||
+ | movwf RAM_div16_tmp2_mm | ||
+ | movf RAM_div16_tmp1_m, | ||
+ | movwf RAM_div16_tmp2_m | ||
+ | movf RAM_div16_tmp1_l, | ||
+ | movwf RAM_div16_tmp2_l | ||
+ | ;shift temporary left 3 times | ||
+ | bcf CARRY | ||
+ | rlf RAM_div16_tmp2_l, | ||
+ | rlf RAM_div16_tmp2_m, | ||
+ | rlf RAM_div16_tmp2_mm, | ||
+ | rlf RAM_div16_tmp2_l, | ||
+ | rlf RAM_div16_tmp2_m, | ||
+ | rlf RAM_div16_tmp2_mm, | ||
+ | rlf RAM_div16_tmp2_l, | ||
+ | rlf RAM_div16_tmp2_m, | ||
+ | rlf RAM_div16_tmp2_mm, | ||
+ | ;add temporary to accumulator | ||
+ | movf RAM_div16_tmp2_l, | ||
+ | addwf RAM_div16_tmp1_l, | ||
+ | movf RAM_div16_tmp2_m, | ||
+ | BTFSC CARRY | ||
+ | incfsz RAM_div16_tmp2_m, | ||
+ | addwf RAM_div16_tmp1_m, | ||
+ | movf RAM_div16_tmp2_mm, | ||
+ | BTFSC CARRY | ||
+ | incfsz RAM_div16_tmp2_mm, | ||
+ | addwf RAM_div16_tmp1_mm, | ||
+ | ;shift temporary left 1 times | ||
+ | bcf CARRY | ||
+ | rlf RAM_div16_tmp2_l, | ||
+ | rlf RAM_div16_tmp2_m, | ||
+ | rlf RAM_div16_tmp2_mm, | ||
+ | ;add temporary to accumulator | ||
+ | movf RAM_div16_tmp2_l, | ||
+ | addwf RAM_div16_tmp1_l, | ||
+ | movf RAM_div16_tmp2_m, | ||
+ | BTFSC CARRY | ||
+ | incfsz RAM_div16_tmp2_m, | ||
+ | addwf RAM_div16_tmp1_m, | ||
+ | movf RAM_div16_tmp2_mm, | ||
+ | BTFSC CARRY | ||
+ | incfsz RAM_div16_tmp2_mm, | ||
+ | addwf RAM_div16_tmp1_mm, | ||
+ | return | ||
+ | ; -------------------------------------------------------------------- | ||
+ | ; Division 24bit by 16bit | ||
+ | ; Author: Nikolai Golovchenko | ||
+ | ; From: http:// | ||
+ | ; | ||
+ | ;Inputs: | ||
+ | ; | ||
+ | ; | ||
+ | ;Temporary: | ||
+ | ; | ||
+ | ; | ||
+ | ;Output: | ||
+ | ; | ||
+ | div16bit: | ||
+ | CLRF RAM_div16_rem0 | ||
+ | CLRF RAM_div16_rem1 | ||
+ | MOVLW 24 | ||
+ | MOVWF RAM_div16_lp | ||
+ | LOOPU2416: | ||
+ | RLF RAM_div16_tmp1_l, | ||
+ | RLF RAM_div16_tmp1_m, | ||
+ | RLF RAM_div16_tmp1_mm, | ||
+ | |||
+ | RLF RAM_div16_rem1, | ||
+ | RLF RAM_div16_rem0, | ||
+ | |||
+ | RLF RAM_div16_tmp1_l, | ||
+ | ;since remainder can be 17 bit long in some cases | ||
+ | ;(e.g. 0x800000/ | ||
+ | ;as the next result bit. | ||
+ | |||
+ | MOVF RAM_div16_tmp2_l, | ||
+ | SUBWF RAM_div16_rem1, | ||
+ | MOVF RAM_div16_tmp2_m, | ||
+ | BTFSS CARRY | ||
+ | INCFSZ RAM_div16_tmp2_m, | ||
+ | SUBWF RAM_div16_rem0, | ||
+ | ;here we also need to take into account the 17th bit of remainder, which | ||
+ | ;is in RAM_div16_tmp1_l.0. If we don't have a borrow after subtracting from lower | ||
+ | ;16 bits of remainder, then there is no borrow regardless of 17th bit | ||
+ | ;value. But, if we have the borrow, then that will depend on 17th bit | ||
+ | ;value. If it is 1, then no final borrow will occur. If it is 0, borrow | ||
+ | ;will occur. These values match the borrow flag polarity. | ||
+ | BTFSC CARRY | ||
+ | BSF RAM_div16_tmp1_l, | ||
+ | ; | ||
+ | ;borrow. | ||
+ | ;if borrow did occur, RAM_div16_tmp1_l.0 already | ||
+ | ;holds the final borrow value (0-borrow, | ||
+ | ;1-no borrow) | ||
+ | BTFSC RAM_div16_tmp1_l, | ||
+ | GOTO UOK46LL ; | ||
+ | |||
+ | ADDWF RAM_div16_rem0, | ||
+ | ;contains the value subtracted from it | ||
+ | ; | ||
+ | MOVF RAM_div16_tmp2_l, | ||
+ | ADDWF RAM_div16_rem1, | ||
+ | UOK46LL: | ||
+ | DECFSZ RAM_div16_lp, | ||
+ | GOTO LOOPU2416 ; | ||
+ | RETURN | ||
+ | |||
+ | ; =========================================================================== | ||
+ | |||
+ | ENDIF | ||
+ | |||
+ | END | ||
+ | </ | ||
+ | |||
+ | After the patch of the original software, the GW60 behaves like an IKEA roller blind. It can be operated using the TRÅDFRI buttons and the gateway. It shows the current position and can also move to a specific position. If the gateway is connected to e.g. Siri, a "// Hey Siri, open all shutters //" is enough in the morning and the apartment will be bright. | ||
+ | |||
+ | ==== Conclusion ==== | ||
+ | The conversion has been in use with me since April 2020 without any problems. There is only a small restriction with manual actuation: then the current position is only updated with the next radio command. \\ | ||
+ | If you have to update the software again, I've put the programming interface outside. But it is only nice-to-have... | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | ==== Downloads ==== | ||
+ | |||
+ | * {{: | ||
+ | * {{: | ||
+ | * {{: | ||
==== Donate ==== | ==== Donate ==== | ||
- | If you line my articles feel to donate a cappuccino or so... | + | If you like my articles feel to donate a cappuccino or so... |
< | < |