; ------------------------------------------------------------------------------ ; Title: Somfy RF remote control procedures ; Author: Rob Jansen, Copyright (c) 2021..2021, all rights reserved. ; Compiler: 2.5r5 ; ; Description: Somfy remote control procedures for transmitting data according to the ; somfy protocol. The data is transmitted on an interrupt basis. ; ; Sources: https://pushstack.wordpress.com/somfy-rts-protocol/ ; ; Pin aliases. alias rf_out is pin_c4 ; Pin 6. pin_c4_direction = OUTPUT alias transmit_led is pin_c5 ; Pin 5. pin_c5_direction = OUTPUT ; Remote control settings. These to your needs or reprogram them later. ; All data is stored in little endian format in the HEF. const byte REMOTE_ADDRESS[3] = { 0x12, 0x13, 0x00 } ; 0x001312 const word RF_ROLLING_INIT = 101 ; Rolling counter 0x65. const byte RF_REPEAT_INIT = 8 ; Number of repeat frames. const byte FIRST_FRAME = 2 ; Number used in the transmission ... const byte REPEAT_FRAME = 7 ; ... see Somfy protocol. const byte FRAME_BUFFER_SIZE = 7 ; Timing. Symbol time is 640 us. const SYMBOL_TIME = 640 ; Buffer that holds the remote control frame to be transmitted. var byte frame_buffer[FRAME_BUFFER_SIZE] var bit rf_debug ; ---------------------- Procedures --------------------------------------- ; Enable debugging procedure rf_debug_on() is rf_debug = TRUE end procedure ; Disable debugging procedure rf_debug_off() is rf_debug = FALSE end procedure ; Intialize the RF transmitter. It will also intialize the timer used for tranmitting ; the individual bits. Timer 2 is set too 50 us. procedure rf_transmitter_init() is ; Intialize the RF output and the state. rf_out = LOW rf_debug = FALSE transmit_led = OFF end procedure ; Build up the remote control frame. Get the rolling code from HEF and store ; the new rolling code after the frame is build. procedure build_frame(byte in somfy_command) is var word rolling_code var byte index, checksum rolling_code = data_hef_word(HEF_ROLLING_ADDRESS) ; Encryption key, only the A seems relevant. frame_buffer[0] = 0xA7 ; Command in the 4 MSB's, the 4 LSB's will become the checksum. frame_buffer[1] = somfy_command << 4 ; Rollng code in big endian. frame_buffer[2] = byte(rolling_code >> 8) frame_buffer[3] = byte(rolling_code & 0x00FF) ; 24 bit remote control address as little endian. frame_buffer[4] = data_hef(HEF_REMOTE_ADDRESS) frame_buffer[5] = data_hef(HEF_REMOTE_ADDRESS + 1) frame_buffer[6] = data_hef(HEF_REMOTE_ADDRESS + 2) if rf_debug then print_string(usb_serial_data, "Command (hexdecimal): ") print_byte_hex(usb_serial_data, somfy_command) print_crlf(usb_serial_data) print_string(usb_serial_data, "Rolling code (hexdecimal): ") print_word_hex(usb_serial_data, rolling_code) print_crlf(usb_serial_data) print_string(usb_serial_data, "Remote address (hexdecimal): ") print_byte_hex(usb_serial_data, frame_buffer[6]) print_byte_hex(usb_serial_data, frame_buffer[5]) print_byte_hex(usb_serial_data, frame_buffer[4]) print_crlf(usb_serial_data) print_string(usb_serial_data, "Frame (hexdecimal): ") for FRAME_BUFFER_SIZE using index loop print_byte_hex(usb_serial_data, frame_buffer[index]) print_string(usb_serial_data, " ") end loop print_crlf(usb_serial_data) end if ; Calculate the checksum of all nibbles. checksum = 0 for FRAME_BUFFER_SIZE using index loop checksum = checksum ^ frame_buffer[index] ^ (frame_buffer[index] >> 4) end loop ; Keep the lowest nibble and add it to the frame. frame_buffer[1] = frame_buffer[1] | (checksum & 0x0F) if rf_debug then print_string(usb_serial_data, "Frame with checksum (hexadecimal): ") for FRAME_BUFFER_SIZE using index loop print_byte_hex(usb_serial_data, frame_buffer[index]) print_string(usb_serial_data, " ") end loop ; Verify checksum. Not very usefull btw but a sanity check. checksum = 0 for FRAME_BUFFER_SIZE using index loop checksum = checksum ^ frame_buffer[index] ^ (frame_buffer[index] >> 4) end loop if ((checksum & 0x0F) == 0) then print_string(usb_serial_data, "--> Checksum OK!\r\n") else print_string(usb_serial_data, "--> Checksum NOT OK!\r\n") end if end if ; Obfuscation: An XOR of all the bytes in the frame buffer. for (FRAME_BUFFER_SIZE - 1) using index loop frame_buffer[index + 1] = frame_buffer[index + 1] ^ frame_buffer[index] end loop if rf_debug then print_string(usb_serial_data, "Frame obfuscated (hexadecimal): ") for FRAME_BUFFER_SIZE using index loop print_byte_hex(usb_serial_data, frame_buffer[index]) print_string(usb_serial_data, " ") end loop print_crlf(usb_serial_data) print_string(usb_serial_data, "Frame obfuscated (binary): ") for FRAME_BUFFER_SIZE using index loop print_byte_bin(usb_serial_data, frame_buffer[index]) print_string(usb_serial_data, " ") end loop print_crlf(usb_serial_data) end if ; Save new rolling code in HEF. data_hef_write_word(HEF_ROLLING_ADDRESS, rolling_code + 1) ; For analysis/debug purposes of the bit pattern that is produced it is handy ; to fill the frame buffer with all ones or all zero's. ; for FRAME_BUFFER_SIZE using index loop ; frame_buffer[index] = 0x00 ; Fill with zero (or 0xFF or 0xAA or 0x55) ; end loop end procedure ; Transmit a frame, initial frame or repeated frame. procedure transmit_frame(byte in tx_sync) is var byte tx_data, index var bit tx_bit at tx_data:7 transmit_led = ON ; Check if this is the first frame. if (tx_sync == FIRST_FRAME) then ; First frame, start with wakeup. rf_out = HIGH _usec_delay(9415) rf_out = LOW _usec_delay(89565) end if ; Hardware sync. for tx_sync loop rf_out = HIGH _usec_delay(4 * SYMBOL_TIME) rf_out = LOW _usec_delay(4 * SYMBOL_TIME) end loop ; Software sync. rf_out = HIGH _usec_delay(4550) rf_out = LOW _usec_delay(SYMBOL_TIME) ; Transmit the data. for FRAME_BUFFER_SIZE using index loop tx_data = frame_buffer[index] for 8 loop ; Manchester encoding. if tx_bit then ; A '1' is a low to high transmission. rf_out = LOW _usec_delay(SYMBOL_TIME) rf_out = HIGH _usec_delay(SYMBOL_TIME) else ; A '0' is a high to low transmission. rf_out = HIGH _usec_delay(SYMBOL_TIME) rf_out = LOW _usec_delay(SYMBOL_TIME) end if ; Data is shifted out with MSB first. tx_data = tx_data << 1 end loop end loop ; Frame silence. rf_out = LOW _usec_delay(30415) transmit_led = OFF end procedure