/* * RCSwitch - port of the Arduino libary for remote control outlet switches. * Arduino library is Copyright (c) 2011 Suat Özgür. All right reserved. * Ported to Java by Florian Frankenberger 2013. * * Contributors for the Arduino library: * - Andre Koehler / info(at)tomate-online(dot)de * - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package de.pi3g.pi.rcswitch; import java.util.BitSet; import com.pi4j.io.gpio.GpioController; import com.pi4j.io.gpio.GpioFactory; import com.pi4j.io.gpio.GpioPinDigitalOutput; import com.pi4j.io.gpio.Pin; import com.pi4j.wiringpi.Gpio; /** * Transmittes signals to 433 MHz electrical switching units. Based on the Arduino library * but enhanced to fit a more object oriented approach in Java. * <p /> * This library is designed to be used with a RF transmitter like this * one: http://www.watterott.com/de/RF-Link-Sender-434MHz * <p /> * Just connect the DATA IN Pin with the pin provided in the constructor. The * VCC with +5V (PIN2) and GND with Ground (PIN6). * <p /> * Usage example: * <p /> * * <pre> * // our switching group address is 01011 (marked with 1 to 5 on the DIP switch * // on the switching unit itself) * BitSet address = RCSwitch.getSwitchGroupAddress("01011"); * * RCSwitch transmitter = new RCSwitch(RaspiPin.GPIO_00); * transmitter.switchOn(address, 1); // switches the switch unit A (A = 1, B = 2, ...) on * Thread.sleep(5000); // wait 5 sec. * transmitter.switchOff(address, 1); // switches the switch unit A off * </pre> * * @author Suat Özgür * @author Florian Frankenberger */ public class RCSwitch { private final GpioPinDigitalOutput transmitterPin; private final int pulseLength = 350; private final int repeatTransmit = 10; public RCSwitch(Pin transmitterPin) { final GpioController gpio = GpioFactory.getInstance(); this.transmitterPin = gpio.provisionDigitalOutputPin(transmitterPin); } /** * Switch a remote switch on (Type A with 10 pole DIP switches) * * @param switchGroupAddress Code of the switch group (refers to DIP switches 1..5 where * "1" = on and "0" = off, if all DIP switches are on it's "11111") * @param switchCode Number of the switch itself (1..4) */ public void switchOn(BitSet switchGroupAddress, int switchCode) { if (switchGroupAddress.length() > 5) { throw new IllegalArgumentException("switch group address has more than 5 bits!"); } this.sendTriState(this.getCodeWordA(switchGroupAddress, switchCode, true)); } /** * Switch a remote switch off * * @param switchGroupAddress Code of the switch group (refers to DIP switches 1..5 where * "1" = on and "0" = off, if all DIP switches are on it's "11111") * @param switchCode Number of the switch itself (1..4 for A..D) */ public void switchOff(BitSet switchGroupAddress, int switchCode) { if (switchGroupAddress.length() > 5) { throw new IllegalArgumentException("switch group address has more than 5 bits!"); } this.sendTriState(this.getCodeWordA(switchGroupAddress, switchCode, false)); } /** * Switch a remote switch on (Type B with two rotary/sliding switches) * * @param nAddressCode Number of the switch group (1..4) * @param nChannelCode Number of the switch itself (1..4) */ public void switchOn(int nAddressCode, int nChannelCode) { sendTriState(getCodeWordB(nAddressCode, nChannelCode, true)); } /** * Switch a remote switch off (Type B with two rotary/sliding switches) * * @param nAddressCode Number of the switch group (1..4) * @param nChannelCode Number of the switch itself (1..4) */ public void switchOff(int nAddressCode, int nChannelCode) { sendTriState(getCodeWordB(nAddressCode, nChannelCode, false)); } /** * Like getCodeWord (Type A) */ private String getCodeWordA(BitSet switchGroupAddress, int channelCode, boolean status) { int nReturnPos = 0; char[] sReturn = new char[12]; String[] code = new String[] { "FFFFF", "0FFFF", "F0FFF", "FF0FF", "FFF0F", "FFFF0" }; if (channelCode < 1 || channelCode > 5) { return ""; } for (int i = 0; i < 5; i++) { if (!switchGroupAddress.get(i)) { sReturn[nReturnPos++] = 'F'; } else { sReturn[nReturnPos++] = '0'; } } for (int i = 0; i < 5; i++) { sReturn[nReturnPos++] = code[channelCode].charAt(i); } if (status) { sReturn[nReturnPos++] = '0'; sReturn[nReturnPos++] = 'F'; } else { sReturn[nReturnPos++] = 'F'; sReturn[nReturnPos++] = '0'; } return new String(sReturn); } /** * Returns a char[13], representing the Code Word to be send. * A Code Word consists of 9 address bits, 3 data bits and one sync bit but in our case only the first 8 address * bits and the last 2 data bits were used. * A Code Bit can have 4 different states: "F" (floating), "0" (low), "1" (high), "S" (synchronous bit) * * +-------------------------------+--------------------------------+-----------------------------------------+----- * ------------------------------------+----------------------+------------+ * | 4 bits address (switch group) | 4 bits address (switch number) | 1 bit address (not used, so never mind) | 1 * bit address (not used, so never mind) | 2 data bits (on|off) | 1 sync bit | * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | F | F | on=FF off=F0 | S | * +-------------------------------+--------------------------------+-----------------------------------------+----- * ------------------------------------+----------------------+------------+ * * @param nAddressCode Number of the switch group (1..4) * @param nChannelCode Number of the switch itself (1..4) * @param bStatus Wether to switch on (true) or off (false) * * @return char[13] */ private String getCodeWordB(int nAddressCode, int nChannelCode, boolean bStatus) { int nReturnPos = 0; char[] sReturn = new char[13]; String[] code = new String[] { "FFFF", "0FFF", "F0FF", "FF0F", "FFF0" }; if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) { return ""; } for (int i = 0; i < 4; i++) { sReturn[nReturnPos++] = code[nAddressCode].charAt(i); } for (int i = 0; i < 4; i++) { sReturn[nReturnPos++] = code[nChannelCode].charAt(i); } sReturn[nReturnPos++] = 'F'; sReturn[nReturnPos++] = 'F'; sReturn[nReturnPos++] = 'F'; if (bStatus) { sReturn[nReturnPos++] = 'F'; } else { sReturn[nReturnPos++] = '0'; } return new String(sReturn); } /** * Sends a Code Word * * @param codeWord /^[10FS]*$/ -> see getCodeWord */ private void sendTriState(String codeWord) { for (int nRepeat = 0; nRepeat < repeatTransmit; nRepeat++) { for (int i = 0; i < codeWord.length(); ++i) { switch (codeWord.charAt(i)) { case '0': this.sendT0(); break; case 'F': this.sendTF(); break; case '1': this.sendT1(); break; } } this.sendSync(); } } /** * Sends a "Sync" Bit * _ * Waveform Protocol 1: | |_______________________________ * _ * Waveform Protocol 2: | |__________ */ private void sendSync() { this.transmit(1, 31); } /** * Sends a Tri-State "0" Bit * _ _ * Waveform: | |___| |___ */ private void sendT0() { this.transmit(1, 3); this.transmit(1, 3); } /** * Sends a Tri-State "1" Bit * ___ ___ * Waveform: | |_| |_ */ private void sendT1() { this.transmit(3, 1); this.transmit(3, 1); } /** * Sends a Tri-State "F" Bit * _ ___ * Waveform: | |___| |_ */ private void sendTF() { this.transmit(1, 3); this.transmit(3, 1); } private void transmit(int nHighPulses, int nLowPulses) { if (this.transmitterPin != null) { this.transmitterPin.high(); Gpio.delayMicroseconds(this.pulseLength * nHighPulses); this.transmitterPin.low(); Gpio.delayMicroseconds(this.pulseLength * nLowPulses); } } /** * convenient method to convert a string like "11011" to a BitSet. * * @param address * @return a bitset containing the address that can be used for switchOn()/switchOff() */ public static BitSet getSwitchGroupAddress(String address) { if (address.length() != 5) { throw new IllegalArgumentException("the switchGroupAddress must consist of exactly 5 bits!"); } BitSet bitSet = new BitSet(5); for (int i = 0; i < 5; i++) { bitSet.set(i, address.charAt(i) == '1'); } return bitSet; } }