Skip to content

Commit

Permalink
Pi4J#32 Initial implementation for 1 Wire based devices using /sys/bu…
Browse files Browse the repository at this point in the history
…s/w1
  • Loading branch information
schup committed May 26, 2015
1 parent 8ead7c7 commit b76d065
Show file tree
Hide file tree
Showing 30 changed files with 876 additions and 0 deletions.
54 changes: 54 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/w1/W1Device.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.pi4j.io.w1;

/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Library (Core)
* FILENAME : W1Device.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2015 Pi4J
* %%
* This program 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 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/


import java.io.File;
import java.io.IOException;

/**
* @author Peter Schuebl
*/
public interface W1Device {

/**
* Returns the name (id/serial number) of the device e.g. 28-00000698ebb1.
* @return the unique device name.
*/
String getName();

String getId();

/**
* Gets the gurrent Value = content of w1_slave file
* @return
*/
String getValue() throws IOException;
}
65 changes: 65 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/w1/W1DeviceType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.pi4j.io.w1;

/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Library (Core)
* FILENAME : W1DeviceType.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2015 Pi4J
* %%
* This program 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 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/


import java.io.File;

/**
*
* http://en.wikipedia.org/wiki/1-Wire
* @author Peter Schuebl
*/
public interface W1DeviceType {

/**
* Returns the FID of the W1 device e.g. 0x28 for DS18B20
*
* Each device has 48 bit (six bytes) globally unique address where last eight bits are
* CRC of first 56 bits. First byte stores a device family code, that identifies device type.
*
* @return the family id of the device
*/
int getDeviceFamilyCode();

/**
* Gets the implementation class of the device which must be a sub-class of W1Device
* @return the implementation class
*/
Class<? extends W1Device> getDeviceClass();

/**
* Creates a new instance of a concrete device.
* @param deviceDir
* @return
*/
W1Device create(File deviceDir);

}
157 changes: 157 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/w1/W1Master.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.pi4j.io.w1;

import java.io.File;
import java.io.FilenameFilter;
import java.util.*;
import java.util.logging.Logger;

/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Library (Core)
* FILENAME : W1Master.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2015 Pi4J
* %%
* This program 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 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/
public class W1Master {

private final Logger log = Logger.getLogger(W1Master.class.getName());

private final List<W1DeviceType> deviceTypes = new ArrayList<>();

private final Map<String, W1DeviceType> deviceTypeMap = new LinkedHashMap<>();

private File masterDir = new File("/sys/bus/w1/devices");

/**
* Create an instance of the W1 master.
* Typically there should only be one master.
*
* java.util.ServiceLoader is used to add device support for individual devices.
*/
public W1Master() {
init();
}

/**
* Create a master with a different default dir e.g. for tests.
* @param masterDir
*/
public W1Master(final String masterDir) {
this.masterDir = new File(masterDir);
init();
}

private void init() {
final ServiceLoader<W1DeviceType> w1DeviceTypes = ServiceLoader.load(W1DeviceType.class);
final Iterator<W1DeviceType> w1DeviceTypeIterator = w1DeviceTypes.iterator();
while (w1DeviceTypeIterator.hasNext()) {
final W1DeviceType w1DeviceType = w1DeviceTypeIterator.next();
deviceTypes.add(w1DeviceType);
final String deviceFamily = Integer.toHexString(w1DeviceType.getDeviceFamilyCode()).toUpperCase();
deviceTypeMap.put(deviceFamily, w1DeviceType);
}
}

/**
* Gets a list of the available device types.
* @return
*/
public Collection<W1DeviceType> getDeviceTypes() {
return deviceTypes;
}

private List<File> getDeviceDirs() {
final File[] slaveDevices = masterDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !name.contains("w1_bus_master");
}
});
return Arrays.asList(slaveDevices);
}

/**
* Gets a list of all registered slave device ids.
* @return list of slave ids, can be empty, never null.
*/
public List<String> getDeviceIDs() {
List<String> ids = new ArrayList<>();
for (File deviceDir: getDeviceDirs()) {
ids.add(deviceDir.getName());
}
return ids;
}

public List<W1Device> getDevices() {
return getDevices((String)null);
}

public List<W1Device> getDevices(int deviceFamilyId) {
return getDevices(Integer.toHexString(deviceFamilyId));
}

public <T extends W1Device> List<T> getDevices(String deviceFamilyId) {
List<W1Device> devices = new ArrayList<>();
for (File deviceDir: getDeviceDirs()) {
String id = deviceDir.getName().substring(0, 2).toUpperCase();
if (deviceFamilyId == null || deviceFamilyId.toUpperCase().equals(id)) {
final W1DeviceType w1DeviceType = deviceTypeMap.get(id);
if (w1DeviceType != null) {
final W1Device w1Device = w1DeviceType.create(deviceDir);
devices.add(w1Device);
} else {
log.info("no device type for [" + id + "] found - ignoring");
}
}
}
return (List<T>) devices;
}

public <T extends W1Device> List<T> getDevices(Class<T> type) {
for (W1DeviceType deviceType : deviceTypes) {
if (deviceType.getDeviceClass().equals(type)) {
return (List<T>) getDevices(deviceType.getDeviceFamilyCode());
}
}
return Collections.emptyList();
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("W1 Master: ").append(masterDir).append("\n");
builder.append("Device Types: \n");
for (W1DeviceType deviceType : deviceTypeMap.values()) {
builder.append(" - ").append(Integer.toHexString(deviceType.getDeviceFamilyCode()));
builder.append(" = ").append(deviceType.getDeviceClass());
builder.append("\n");
}
builder.append("Devices:\n");
for (W1Device device : getDevices()) {
builder.append(" - ").append(device.getId()).append(": ").append(device.getName());
builder.append(" = ").append(device.getClass().getName()).append("\n");
}
return builder.toString();
}
}
35 changes: 35 additions & 0 deletions pi4j-core/src/main/java/com/pi4j/io/w1/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* http://lxr.free-electrons.com/source/drivers/w1/slaves/w1_therm.c
* http://lxr.free-electrons.com/source/drivers/w1/w1_family.h
* @author Peter Schuebl
*/
package com.pi4j.io.w1;

/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Library (Core)
* FILENAME : package-info.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2015 Pi4J
* %%
* This program 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 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/
70 changes: 70 additions & 0 deletions pi4j-core/src/test/java/com/pi4j/io/w1/W1DummyDevice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.pi4j.io.w1;

/*
* #%L
* **********************************************************************
* ORGANIZATION : Pi4J
* PROJECT : Pi4J :: Java Library (Core)
* FILENAME : W1DummyDevice.java
*
* This file is part of the Pi4J project. More information about
* this project can be found here: http://www.pi4j.com/
* **********************************************************************
* %%
* Copyright (C) 2012 - 2015 Pi4J
* %%
* This program 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 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
* #L%
*/


import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

/**
* @author Peter Schuebl
*/
public class W1DummyDevice implements W1Device {

private String name;

private File deviceDir;

@Override
public String getName() {
return name;
}

@Override
public String getId() {
return deviceDir.getName();
}

public W1DummyDevice(final File deviceDir) {
try {
name = new String(Files.readAllBytes(new File(deviceDir, "name").toPath()));
} catch (IOException e) {
// FIXME logging
name = deviceDir.getName();
}
this.deviceDir = deviceDir;
}

@Override
public String getValue() throws IOException {
return new String(Files.readAllBytes(new File(deviceDir, "w1_slave").toPath()));
}
}
Loading

2 comments on commit b76d065

@DataWorm
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! What do you think about adding a WatchService to auto-detect added/removed 1-wire devices and send events to listeners?

@schup
Copy link
Owner Author

@schup schup commented on b76d065 Sep 13, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.
Yes, that sounds good. The code could do with some more documentation as well.

I don't have any other W1 devices but the ServiceLoader approach should allow adding new devices in library jars without adding to the core.

I'll have a look at the WatchService - should be relatively easy to implement.

Please sign in to comment.