Controlling the DS2405 addressable switch

10 May

Example: Controlling the DS2405 addressable switch: So far our examples have dealt with basic concepts involving the PC ports, the software port adapters that support communications over them, and the hardware port adapters that work with the software to drive the 1-Wire bus. Now, we’re going to shift gears and concentrate more on 1-Wire devices that we want to control. The following examples assume that we are relying solely on the DS9097U port adapter in the Java 1-Wire API. We will make use of both the DS1411 COM port adapter, for use with iButtons, and the DS9097U-09 Universal COM Port Adapter, for use with non-iButton type devices. We are going to start by looking at a program to turn on an LED using a DS2405 Addressable Switch5.

We’ll start by making a Java class that takes a parameter that turns an LED on or off. Then we’ll present a program that acts as an HTTP server and allows you to control the LED with a web browser. Let’s take a high-level look at the 1-Wire API as it relates to the DS2405.

The OneWireContainer05 class
The OneWireContainer05 class represents devices in the 05(hex) family, which are single addressable switches. There are numerous methods completely documented in the 1-Wire API Javadocs, but the methods we’ll be using, for review, are:

• public String getAddressAsString()
• public byte[] readDevice()
• public boolean getLatchState(int channel, byte[] state)
• public void setLevel(int channel, byte[] state)
• public void writeDevice(byte[] state)
• public void setLatchState(int channel, boolean latchState, boolean doSmart, byte[] state)

An important consideration when using the DS2405 is that it has an internal latch that holds the value of what we set the switch to do, and a level sensor so we can determine what the switch is really doing. This allows you to use the DS2405 as a simple sensor, and it also allows you to check for faults such as the output shorted to power or ground. However, the polarity of the internal latch and the level sensor are opposite. By setting the internal latch to true, we are telling the DS2405 to make the PIO pin logic zero. By setting the latch to false, we are telling the DS2405 to let the PIO pin float (since there’s no positive voltage supply on the DS2405, it can’t drive the PIO pin high). But if the PIO pin is at logic level zero, and we sense its level, the level will come back false. If its value is above a threshold of 2.2 volts, it will come back true. To reiterate the point: setting the latch and reading the level have what can be considered opposite polarities.

Our Switch class will consist of the following:

1. an overloaded constructor which can take no arguments, the port adapter and port, or the specific ROM ID of an individual device and port adapter and port names.

2. turnOn() and turnOff() methods, which take no arguments and do exactly as their name implies, turn our switch on and off. In this context, we’re assuming that the DS2405 is controlling a small LED being pulled high by a resistor, as shown in Figure 10-35.

3. a main() method that takes a command line parameter of “on” or “off” and adjusts the switch accordingly.

The program is presented in its entirety below, then explained piece by piece.

Listing 10-4: Switch.java
import java.util.*;
import com.dalsemi.onewire.*;
import  com.dalsemi.onewire.adapter.*;
import  com.dalsemi.onewire.container.*;
public class Switch {
DSPortAdapter adapter;
String ROM_ID;
byte[] state;
boolean latchState;
boolean level;
static int speed=0;
// Use container types rather then specific container numbers
SwitchContainer container;
OneWireContainer owc;
public Switch() {
try {
// get the default adapter
adapter = (DSPortAdapter)OneWireAccessProvider.getDefaultAdapter();
Init(“”);
}
catch(Exception e) {
System.out.println(e);
}
}
public Switch( String PAadapt, String Port) {
try {
adapter=(DSPortAdapter)OneWireAccessProvider.getAdapter(PAadapt,Port);
Init(“”);
}
catch(Exception e) {
System.out.println(e);
}
}
public Switch( String ROM, String PAadapt, String Port) {
try {
adapter=(DSPortAdapter)OneWireAccessProvider.getAdapter(PAadapt,Port);
Init(ROM);
}
catch(Exception e) {
System.out.println(e);
}
}
public void Init(String ROM) {
// Get both DS2405 and DS2406 devices since they both are switches
byte[] targetfamlies = { 0×05, 0×12 };
try {
adapter.beginExclusive(true);
adapter.reset();
adapter.targetFamily(targetfamlies);
if (ROM.length()<1) {
owc = adapter.getFirstDeviceContainer();
}
else
{
container = (OneWireContainer12)adapter.getDeviceContainer(ROM);
}
// cast it to a switchContainer
container = (SwitchContainer)owc;
// get its ROM_ID
ROM_ID = owc.getAddressAsString();
// shouldn’t the container object know the adapter object?
owc.setupContainer(adapter, ROM_ID);
state = container.readDevice();
latchState = container.getLatchState(0, state);
level = container.getLevel(0, state);
adapter.setSpeed(speed);
}
catch(Exception e) {
System.out.println(e);
}
}
public void turnOn() {
try {
state = container.readDevice();
latchState = container.getLatchState(0, state);
level = container.getLevel(0, state);
if ((level == true) && (latchState == false)) {
container.setLatchState(0, true, false, state);
container.writeDevice(state);
}
}
catch (Exception e) {
System.out.println(e);
}
}
public void turnOff() {
try {
state = container.readDevice();
latchState = container.getLatchState(0, state);
level = container.getLevel(0, state);
if ((level == false) && (latchState == true)) {
container.setLatchState(0, false, false, state);
container.writeDevice(state);
}
}
catch (Exception e) {
System.out.println(e);
}
}
public void toggle() {
try {
state = container.readDevice();
level = container.getLevel(0, state);
// since the level is the opposite of the latchstate,
// set the new latchstate to the existing level
container.setLatchState(0, level, false, state);
container.writeDevice(state);
}
catch (Exception e) {
System.out.println(e);
}
}
public void blink() {
for( int i=0; i<10; i++ ) {
try {
state = container.readDevice();
level = container.getLevel(0, state);
// since the level is the opposite of the latchstate,
// set the new latchstate to the existing level
container.setLatchState(0, true, false, state);
container.writeDevice(state);
container.setLatchState(0, false, false, state);
container.writeDevice(state);
}
catch (Exception e) {
System.out.println(e);
}
}
}
public void update() {
try {
state = container.readDevice();
latchState = container.getLatchState(0, state);
level = container.getLevel(0, state);
} catch (Exception e) {}
}
public static void main(String args[]) {
String inputCommand = (args.length>0) ? args[0] : “flip”;
speed = Integer.valueOf((args.length>1) ? args[1] : “0” ).intValue();
Switch blinker = new Switch();
if (inputCommand.equalsIgnoreCase(“on”)) {
System.out.println( “Turn on switch.” );
blinker.turnOn();
} else if (inputCommand.equalsIgnoreCase(“off”)) {
System.out.println( “Turn off switch.” );
blinker.turnOff();
} else if (inputCommand.equalsIgnoreCase(“blink”)) {
System.out.println( “Blinking.” );
blinker.blink();
} else if (inputCommand.equalsIgnoreCase(“flip”)) {
System.out.println( “Toggling switch.” );
blinker.toggle();
} else {
System.out.println( “Invalid option. “ );
System.exit(0);
}
System.out.println(“Device ROM ID: “ + blinker.ROM_ID);
System.out.println(“Device Latch State: “ + blinker.latchState);
System.out.println(“Device Level: “ + blinker.level);
}
}

The program begins with the normal import statements and class declaration. We follow that by declaring all of our data members. These are the data items that each switch object contains. The member called adapter is the DSPortAdapter object we’re using to communicate with the 1-Wire bus, as in DS9097U or {DS9097U}, etc. ROM_ID is the device-unique ID code. The byte array called state is actually an array with only a single element of type byte that contains information necessary for the device to maintain its state. The boolean flag latchState is the setting of the device’s internal latch as of the last time the state byte was read, while level is the last sensed value of the PIO pin as of the last time the state byte was read. The OneWireContainer05 object called container is the object we’re using to represent, or encapsulate, our switch.

import java.util.*;
import com.dalsemi.onewire.*;
import  com.dalsemi.onewire.adapter.*;
import  com.dalsemi.onewire.container.*;
public class Switch {
DSPortAdapter adapter;
String ROM_ID;
byte[] state;
boolean latchState;
boolean level;
OneWireContainer05 container;
static int speed=0;

The first constructor takes no arguments. It attempts to use the default port adapter and then call the Init() method that attempts to find the first DS2405 on the 1-Wire bus to be identified. We use the Init() method call in the constructor because this block of code is common for all of the constructors. Most of this is creating the data members that were previously declared, but a couple of the actions are responsible for other functions. The targetFamily(targetfamlies) is actually telling the 1- Wire bus that we are going to limit the underlying 1-Wire bus searches (Search ROM passes) to devices with a family code of 0×05 and 0×12 (these are both switch family codes). Note that the byte array targetfamlies is defined as { 0×05, 0×12 }. You can place as many families in this array as you want. The setupContainer() method provides the container object with the adapter we’re communicating over and the ROM_ID of the device we’re communicating with. Since several of the methods throw exceptions, we enclose them in a try/catch block.

public Switch() {
try {
// get the default adapter
adapter = (DSPortAdapter)OneWireAccessProvider.getDefaultAdapter();
Init(“”);
}
catch(Exception e) {
System.out.println(e);
}
}
public void Init(String ROM) {
// Get both DS2405 and DS2406 devices since they both are switches
byte[] targetfamlies = { 0×05, 0×12 };
try {
adapter.beginExclusive(true);
adapter.reset();
adapter.targetFamily(targetfamlies);
if (ROM.length()<1) {
owc = adapter.getFirstDeviceContainer();
}
else
{
container = (OneWireContainer12)adapter.getDeviceContainer(ROM);
}
// cast it to a switchContainer
container = (SwitchContainer)owc;
// get its ROM_ID
ROM_ID = owc.getAddressAsString();
// shouldn’t the container object know the adapter object?
owc.setupContainer(adapter, ROM_ID);
state = container.readDevice();
latchState = container.getLatchState(0, state);
level = container.getLevel(0, state);
adapter.setSpeed(speed);
}
catch(Exception e) {
System.out.println(e);
}
}
The second constructor is almost identical to the first, except this one takes a specific
port adapter and port as arguments, as opposed to using the defaults.
public Switch( String PAadapt, String Port) {
try {
adapter=(DSPortAdapter)OneWireAccessProvider.getAdapter(PAadapt,Port);
Init(“”);
}
catch(Exception e) {
System.out.println(e);
}
}

The third and final  constructor allows you to specify all the specifics: port adapter, port name, and the ROM ID of the device you are interested in.

public Switch( String ROM, String PAadapt, String Port) {
try {
adapter=(DSPortAdapter)OneWireAccessProvider.getAdapter(PAadapt,Port);
Init(ROM);
}
catch(Exception e) {
System.out.println(e);
}
}

The turnOn() method illustrates how to communicate with the DS2405. To use any of the methods, such as getLatchState, or getLevel, you have to know the state of the device, determined by using the readDevice() method. The state byte array returned by that method can then be supplied to the other methods that parse it.

public void turnOn() {
try {
this.state = (this.container).readDevice();

The latchState and level are determined by the getLatchState and getLevel methods. The “0” in each one of them corresponds to the channel number. These methods are designed to be compatible with other switch devices, such as the DS2407 that has two switches, and hence two channels inside it. The DS2405 only has one channel and the channel number will always be 0 for it.

this.latchState = (this.container).getLatchState(0, this.state);
this.level = (this.container).getLevel(0, state);

Before turning the switch on, we actually check to see that it is off. If it is off,  we then set the latch state to true, indicating that we want it to drive the PIO pin to logic zero, thereby turning on our LED. Doing this involves using the setLatchState() method to write our changes to the state array, then sending that array to the device with the writeDevice() method. Again, the setLatchState() method is designed to be compatible with other switch devices that have more capabilities. The third argument is the doSmart flag. It refers to a capability (smart sensing) that the DS2405 does not have and will always be false for the DS2405.

if ((this.level == true) && (this.latchState == false)) {
(this.container).setLatchState(0, true, false, this.state);
(this.container).writeDevice(this.state);
}
} catch (Exception e) {System.out.println(e);}
The turnOff() method proceeds in the same fashion as the turnOn() method.
public void turnOff() {
try {
this.state = (this.container).readDevice();
this.latchState = (this.container).getLatchState(0, this.state);
this.level = (this.container).getLevel(0, this.state);
if ((this.level == false) && (this.latchState == true)) {
(this.container).setLatchState(0, false, false, this.state);
(this.container).writeDevice(this.state);
}
} catch (Exception e) {System.out.println(e);}
}
The update() method reads the device state and then supplies that to the
getLatchState() and getLevel() methods so that the latchState and level data
members can be updated. This is useful for looking at activity on the output of the
switch that might be caused by  a source other than our program.
public void update() {
try {
this.state = (this.container).readDevice();
this.latchState = (this.container).getLatchState(0, this.state);
this.level = (this.container).getLevel(0, this.state);
} catch (Exception e) {}
}

The main() method is straightforward. It creates a switch object called blinker, using the version of the constructor that takes no arguments, then looks at the command line. If we entered an “on,” it turns the switch on. If we enter an “off,” it turns the switch off. If we didn’t enter anything then it calls the toggle() method to invert the state of the switch. If we entered blink then it turns the switch on and off ten times. Also note that, using the second argument, it is possible to set the speed of the 1-Wire communication. It then prints out the ROM ID, latch state, and level.

public static void main(String args[]) {
String inputCommand = (args.length>0) ? args[0] : “flip”;
speed = Integer.valueOf( (args.length>1) ? args[1] : “0” ).intValue();
Switch blinker = new Switch();
if (inputCommand.equalsIgnoreCase(“on”)) {
System.out.println( “Turn on switch.” );
blinker.turnOn();
} else if (inputCommand.equalsIgnoreCase(“off”)) {
System.out.println( “Turn off switch.” );
blinker.turnOff();
} else if (inputCommand.equalsIgnoreCase(“blink”)) {
System.out.println( “Blinking.” );
blinker.blink();
} else if (inputCommand.equalsIgnoreCase(“flip”)) {
System.out.println( “Toggling switch.” );
blinker.toggle();
} else {
System.out.println( “Invalid option. “ );
System.exit(0);
}
System.out.println(“Device ROM ID: “ + blinker.ROM_ID);
System.out.println(“Device Latch State: “ + blinker.latchState);
System.out.println(“Device Level: “ + blinker.level);
}
}
The following sample output was performed with a DS9097U attached to COM1,
which was attached to a small circuit board with the DS2405, an LED, and resistor
via a cat 5 1-Wire bus cable.
C:\> java Switch on
Device ROM ID: 9A0000000C152305
Device Latch State: true
Device Level: false
C:\> java Switch off
Device ROM ID: 9A0000000C152305
Device Latch State: false
Device Level: true
You will have to take our word for it that the light did, in fact, turn on and then turn
off. It really did, honest.

411-Example: Measuring temperature with a DS1920

Random Posts

Comments are closed.