This is a custom firmware for the TQL25-KT972 motor module (powered by the STM32F030K6T6 ARM microcontroller) used by IKEA Fyrtyr and Kadrilj roller blinds.
TLDR; Please update to version 0.85 :)
UPDATE 5.4.2022: Versions 0.83 and 0.84 had the maximum motor current limit disabled because it was postulated that this was causing false detection of motor stalling. This however seems no to be the case, so from 0.85 the limit is re-enabled with a maximum current of 2A.
Problems with motor stalling usually occur with lower speeds (< 5 RPM) and the main reason this is most likely because the motor not designed to be used with such low voltages associated with these speeds. For example with 2 RPM the PWM duty cycle causes the effective voltage to be 1.2V - 1.8V which makes the motor more suspectible for stalling should there be any extra friction during movement.
Normally when motor stalls the system assumes the curtains are winded up for calibration and resets its position to zero. However if the motor stalls prematurely on lower speeds, there's a position mismatch which might later cause movement of the curtains outside the physical lower limit and eventually rewinding of the curtains in the opposite direction. This could cause annoying mess with wrinkled curtains and even damage the gluing between the curtains and the axel rod!
Few changes were made to 0.85 to make premature stalling less likely or at least the recovery from this more robust. The most important is a new feature called MINIMUM CALIBRATION CURRENT (by default this is 500 mA). In my tests I've measured currents during normal calibration (when stalling at the top) to be around 1-2A depending on the motor speed, where as the problematic stalls occur usually with less than 300 mA. So if the motor stalls at levels lower than the minimum calibration current, it is assumed that the curtains aren't really at the top position and the position is not reset in this case. Even though the motor has stopped it's correct position is still known.
If these problematic stalls happens, you can try using a higher speeds or try to make sure there isn't any extra friction in the system (such as those protruding metal tabs in the motor housing when it was disassembled and reassembled). In any case, it seems that 3-4 RPM is the minimum speed which the Fyrtur can sustaing without stalling but your mileage may wary.
Regarding the maximum current limit feature in this firmware, it was designed to be an extra layer of safety in addition to the fuse on the custom (NOT ORIGINAL!) Fyrtur board. The main method of stall detection is via timeout of signals from the Hall sensors. This is also the way the original firmware works as far as I know. In my reverse engineering project of the original Ikea motor firmware I didn't find any evidence that the current sensing circuitry would be used for stall detection (take this with a grain of salt - it's possible that I have overlooked this part of the code). So it seems that even Ikea (or the subcontractor that manufactured the motor module) didn't bother to use the current sensing circuitry even though the hardware is there.
Needless to say, this kind of software feature, even if enabled, doesn't prevent from possible programming errors or physical catastrofic failures. Thus it's mandatory that other precautions are made to prevent too high current consumption in case of actual faulty operation. Most (if not all) power adapters shut down the output if their maximum current draw is exceeded. The original Fyrtur board doesn't have any kind of fuse, but the Li-Ion battery modules are specified to have maximum 2A output. So I would like to assume that they have an internal current limiting / protection circuitry, BUT I HAVEN'T TESTED THEIR SAFETY WHEN SHORT CIRCUITED NOR DO I WANT TO!
The custom firmware mimics the functionality of the original firmware with following enhancements:
Although the new firmware can be used with original Ikea Fyrtur wireless (Zigbee) module, most of its additional features can only be utilized with ESP8266/ESP32 based module using WiFi and MQTT connectivity
The motor is connected with 4 wires (TX, RX, GND, VCC). TX and RX are UART lines operating at 2400 kbps (8N1 = 1 start bit, 8 data bits,no parity, 1 stop bit) using 3.3V logic high. VCC is nominal 5.0-7.4 V.
Note that disassembling the motor unit will most definitely void your warranty!
Please first dump the original firmware (as described below) before proceeding to install custom FW!
This software is provided "as is". Use with caution and at your own risk! (See LICENSE file for DISCLAIMER)
For compiling the sources, install STM32CubeIDE (I used version 1.4.2). UPDATE: Currently I'm using Visual Studio Code and the "STM32 for VSCode" plugin.
Alternatively you can use binaries found in bin/ folder.
To flash the motor unit firmware a STM programmer is needed (recommended one is ST-Link V2 which can be had for about 20 EUR / 22 USD). The programming mode that is used is Serial Wire Debug (SWD) instead of older JTAG standard. First solder 5 male Dupont wires to IO1 header as described below. The required wires are GND, RST, SWCLK, SWDIO, 3V3 (To help identifying the correct order, 3V3 is the square pin hole).
Then route the wires via holes in the the motor housing. You also need to carve a second narrow hole on the plastic end cap to fit the wires coming through (not shown in the picture). Be careful when reattaching the aluminum tube so that the little aluminum tabs are pushed firmly down in the plastic trenches and are absolutely flush with the rest of the tube (even slightest protusion can create annoying sound from friction and cause uneven movement!)
Attach SWD wires to the flat cable that came with the ST-Link.
There is also Open Source ST-Link utility available. It can be installed via HomeBrew on MacOS X (brew install stlink
)
Probing:
Found 1 stlink programmers
serial: 363f6b065250343857440343
hla-serial: "\x36\x3f\x6b\x06\x52\x50\x34\x38\x57\x44\x03\x43"
flash: 32768 (pagesize: 1024)
sram: 4096
chipid: 0x0444
descr: F0xx small
Reading:
$ st-flash read original-fyrtur-fw.bin 0x8000000 32768
st-flash 1.6.1
2020-11-15T14:19:51 INFO common.c: F0xx small: 4 KiB SRAM, 32 KiB flash in at least 1 KiB pages.
Writing:
$ st-flash write Release/fyrtur-motor-board.bin 0x8000000
st-flash 1.6.1
2020-11-15T14:17:14 INFO common.c: F0xx small: 4 KiB SRAM, 32 KiB flash in at least 1 KiB pages.
file Release/fyrtur-motor-board.bin md5 checksum: 37264f7467389a6709a3f3bdea35977, stlink checksum: 0x00183980
2020-11-15T14:17:14 INFO common.c: Attempting to write 17768 (0x4568) bytes to stm32 address: 134217728 (0x8000000)
2020-11-15T14:17:14 INFO common.c: Flash page at addr: 0x08000000 erased
2020-11-15T14:17:14 INFO common.c: Flash page at addr: 0x08000400 erased
...
2020-11-15T14:17:14 INFO common.c: Flash page at addr: 0x08004400 erased
2020-11-15T14:17:14 INFO common.c: Finished erasing 18 pages of 1024 (0x400) bytes
2020-11-15T14:17:14 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2020-11-15T14:17:14 INFO flash_loader.c: Successfully loaded flash loader in sram
18/18 pages written
2020-11-15T14:17:15 INFO common.c: Starting verification of write complete
2020-11-15T14:17:15 INFO common.c: Flash written and verified! jolly good!
ERROR common.c: stlink_flash_loader_run(0x8000000) failed! == -1
try power-cycling the motor board and/or retry flashing.After first flashing the custom firmware with SWD interface the subsequent updates can be done vith ESP Wifi module (via UART interface) without ST-Link. Note that it is possible to brick the module if firmware upload is interrupted, so in any case it is recommended to solder wires to SWD header and route them outside the enclosure should you still need to manually flash the firmware in the future with the ST-Link.
pcb-reverse-engineering/ folder contains very rough schematic of the motor board in PDF and KiCad project format.
All the commands described below are almost identical to the ones used with original firmware, and try to mimic their functionality as well as possible. In addition there are few custom commands that extend further the usability of the motor module. Commands are sent to the motor module via UART line. They consist of 6 bytes and must follow this pattern:
(0x00 0xff 0x9a) DATA1 DATA2 CHECKSUM
The first 3 bytes are the header, DATA1 and DATA2 are the command/argument bytes and CHECKSUM is a bitwise XOR of the DATA1 and DATA2 bytes.
00 ff 9a dd XX CHECKSUM
00 ff 9a 0a dd d7
00 ff 9a 0a ee e4
00 ff 9a 0a 0d 07
00 ff 9a 0a 0e 04
00 ff 9a 0a cc c6
00 ff 9a cc cc 00
The motor module response consists of 8 bytes and follows this pattern:
0x00 0xff 0xd8 BATTERY_LEVEL VOLTAGE SPEED POSITION CHECKSUM
Example: 00 ff d8 16 d8 19 32 e5
These commands can move the curtains past the maximum (or full) curtain length (see Position chapter below)
00 ff 9a fa d1 2b
00 ff 9a fa d2 28
00 ff 9a fa d3 29
00 ff 9a fa d4 2e
See Position chapter below for more information
00 ff 9a fa ee 14
00 ff 9a fa cc 36
00 ff 9a fa 00 fa
00 ff 9a d6 00 d6
00 ff 9a d5 00 d5
00 ff 9a 1X YZ CHECKSUM
00 ff 9a fa da 20
00 ff 9a 20 XX CHECKSUM
00 ff 9a 30 XX CHECKSUM
00 ff 9a 40 XX CHECKSUM
00 ff 9a 60 XX CHECKSUM
00 ff 9a 61 XX CHECKSUM
00 ff 9a cc dc 10
The motor module response consists of 8 bytes and follows this pattern:
0x00 0xff 0xd0 VERSION_MAJOR VERSION_MINOR MINIMUM_VOLTAGE CHECKSUM
00 ff 9a cc de 12
The motor module response consists of 8 bytes and follows this pattern:
0x00 0xff 0xda MODULE_STATUS MOTOR_CURRENT RPM POSITION_DEC POSITION_FRAC MOTOR_PWM RESERVED CHECKSUM
00 ff 9a cc df 13
The motor module response consists of 8 bytes and follows this pattern:
0x00 0xff 0xbb CALIBRATING MCL_1 MCL_2 FCL_1 FCL_2 CHECKSUM
00 ff 9a ff 00 ff
00 ff 9a 62 XX CHECKSUM
There are also few debugging and fine-tuning related commands which are not documented here. Please see the code and know what you're doing if you're fiddling with them :)
Normally the motor unit reports the curtain position (after CMD_GET_STATUS command) as percentage of maximum operating distance or curtain length (0 for upper limit and 100 for lower limit). If more accurate positioning is needed, use CMD_EXT_GET_STATUS command for sub-percent resolution.
When lowering the curtain the motor automatically stops when either the maximum (user defined) or full (factory defined) curtain length has been reached. The default value for factory setting is 13 turns + 265 degrees.
Curtain can be raised above the top limit or lowered below the lower limit with overriding move commands. In these cases the motor module will keep track of its position with internal counters. It will however NOT announce the position in STATUS message, but instead the position is truncated between 0 and 100.
The maximum and full curtain lengths can be changed by using CMD_SET_MAX_CURTAIN_LENGTH / CMD_SET_FULL_CURTAIN_LENGTH commands. These will set the current curtain position to 100 and will restrict normal movements below this lower limit. Reported curtain position data is now scaled accordingly.
Maximum (user-defined) curtain length can be reset to full ('factory defined') length via CMD_RESET_CURTAIN_LENGTH command. This will also start the calibration procedure, which causes motor to ignore the current curtain position and allow uninhibited movement regardless of the user/factory defined curtain lengths. Position will be reported as 50 regardless of motor movement until calibration procedure is complete (see Position calibration chapter below). Position is then reset to 0 and normal operation can continue as described above.
Difference between maximum (or 'user-defined') and full (factory-set) curtain lengths is that the maximum curtain length is intended to be used to limit curtain movement past installation restrictions (e.g. window board or alcove), whereas full (factory defined) is supposed to be the absolute maximum curtain length restricted by the actual physical curtain length (movement past this limit would cause the curtain to rewind in opposite direction). The default full curtain length is measured in curtain rod rotations and has value of 13 turns + 265 degrees.
The user-defined curtain length can be set only between 0 and full length, whereas the full (factory-set) is unrestricted in this way. It is assumed that the full curtain length is programmed by manufacturer (or a distributor, such as Ikea in this case) to match the curtain length of the end product. Changing the full curtain length will also reset the user-defined length to the same value. In original firmware, the full curtain length setting is stored into EEPROM/FLASH. In addition to this, our custom firmware saves also the user-defined max length to non-volatile memory. Since the full curtain length cannot be set to or restored to any default value via UART command in the original firmware, the only way to extend this limit further is to use overriding movement commands to lower the curtain to the desired position and then call CMD_SET_FULL_CURTAIN_LENGTH again. In our custom firmware, it's also possible set the full length value programmatically by using CMD_SET_LOCATION followed by CMD_SET_FULL_CURTAIN_LENGTH. With CMD_EXT_OVERRIDE_DOWN command the user can also roll smoothly below the lower limit.
When the module is first powered up the curtain position is uncalibrated (position is set to full length). If auto-calibration is enabled (the default setting) the calibration is done automatically by rewinding the curtain upwards. During this time position byte stands at 50 (sometimes at 100 in the original firmware). Rewinding continues until sufficient resistance occurs and motor begins stalling. Now it is assumed that the curtain has been lifted to upmost position and position is now reset to 0. This position calibration is done also every time the curtain is rolled up until motor stalls, as well as after the CMD_RESET_CURTAIN_LENGTH command (although in the latter case the movement itself has to be started by a separate command).
Auto-calibration can be configured with CMD_EXT_SET_AUTO_CAL.
If you want to install the curtain rod backwards (a.k.a. "front roll") instead of the normal configuration ("back roll"), you can use CMD_EXT_SET_ORIENTATION command with "1" as parameter. The steps for doing this is to first roll up the blinds, then switch orientation with the command, followed by powering off and the actual physical flipping of the curtain rod. The order is important because normally auto calibration starts rolling blinds up after power on, but if the rod is reversed first, then the blinds start rolling in opposite direcion (i.e. down) without any limits..
Battery level: (0x00=empty, >0x45 = full) Voltage follows roughly the formula: V = (voltage byte) / 31.
Below are the results of battery and voltage bytes from original firmware, experimented with external DC voltage source using various voltage levels (did not want to go above 7.5..)
volt 3.7: 0x00 0x7c
volt 4.5: 0x00 0x89
...
volt 4.7: 0x00 0x90
volt 5.0: 0x00 0x99
volt 5.5: 0x00 0xa9
volt 5.6: 0x00 0xac
..
volt 6.0: 0x00 0xb8
volt 6.4: 0x00 0xc5
volt 6.5: 0x01 0xc8
volt 6.8: 0x08 0xd2
volt 7.0: 0x12 0xd8
volt 7.1: 0x20 0xdb
volt 7.2: 0x2e 0xde
volt 7.3: 0x38 0xe1
volt 7.4: 0x40 0xe4
volt 7.5: 0x45 0xe7
With original firmware, the motor operates with voltages such as 6.0-6.4 volts even though battery is reported to be empty. Below 6V the motor itself would not engage, but the motor module still responds to status queries until around 3.7V is reached.
Our custom firmware reports the voltage correctly, but battery byte for now is fixed 0x12 (matching 7.0V)