Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue while reading SPI data #405

Open
beulenbilly opened this issue Oct 31, 2024 · 10 comments
Open

Issue while reading SPI data #405

beulenbilly opened this issue Oct 31, 2024 · 10 comments

Comments

@beulenbilly
Copy link

beulenbilly commented Oct 31, 2024

At the moment I am using the version 2.7.0 of your great library with raspberry pi and pigio.

My implementation reads the data from MCP3208:

ByteBuffer writeBuffer = ByteBuffer.wrap(data);
ByteBuffer readBuffer = ByteBuffer.allocate(writeBuffer.capacity());
final int bytesRead = spi.transfer(writeBuffer, readBuffer, writeBuffer.capacity());

If it was successful the logging looks like (three bytes sent, three bytes received):

2024-10-31 08:58:48,464 [Thread-6] TRACE d.b.rpi.mqtt.service.SpiAdcService - sending data for channel: 7 are 7, -64, 0 
2024-10-31 08:58:48,465 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes] 
2024-10-31 08:58:48,466 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [TX] -> CMD=SPIX(75); P1=0; P2=0; P3=3; PAYLOAD=[0x07 C0 00] 
2024-10-31 08:58:48,469 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [RX] <- CMD=SPIX(75); P1=0; P2=0; P3=3; PAYLOAD=[0x00 0C C5] 
2024-10-31 08:58:48,470 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=3 
2024-10-31 08:58:48,471 [Thread-6] TRACE d.b.rpi.mqtt.service.SpiAdcService - received data for channel: 7 are 0, 12, -59 

But something something different happens (first transfer return 0 bytes, second transfer return 6 bytes):

2024-10-31 08:58:49,473 [Thread-6] TRACE d.b.rpi.mqtt.service.SpiAdcService - sending data for channel: 7 are 7, -64, 0 
2024-10-31 08:58:49,474 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes] 
2024-10-31 08:58:49,475 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [TX] -> CMD=SPIX(75); P1=0; P2=0; P3=3; PAYLOAD=[0x07 C0 00] 
2024-10-31 08:58:49,477 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [RX] <- CMD=SPIX(75); P1=0; P2=0; P3=3; PAYLOAD=[0x] 
2024-10-31 08:58:49,478 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=0 
2024-10-31 08:58:49,479 [Thread-6] TRACE d.b.rpi.mqtt.service.SpiAdcService - received data for channel: 7 are 0, 0, 0 
2024-10-31 08:58:49,479 [Thread-6] DEBUG d.b.rpi.mqtt.service.SpiAdcService - received value is: 0 

2024-10-31 08:58:50,480 [Thread-6] TRACE d.b.rpi.mqtt.service.SpiAdcService - sending data for channel: 7 are 7, -64, 0 
2024-10-31 08:58:50,481 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes] 
2024-10-31 08:58:50,483 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [TX] -> CMD=SPIX(75); P1=0; P2=0; P3=3; PAYLOAD=[0x07 C0 00] 
2024-10-31 08:58:50,486 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketBase - [RX] <- CMD=UNKNOWN(-1); P1=0; P2=0; P3=6; PAYLOAD=[0x00 00 00 00 0C C5] 
2024-10-31 08:58:50,487 [Thread-6] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=6 
2024-10-31 08:58:50,488 [Thread-6] WARN  d.b.rpi.mqtt.service.SpiAdcService - could not read data java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 6 out of bounds for byte[3]
	at java.base/java.lang.System.arraycopy(Native Method)
	at [email protected]/com.pi4j.library.pigpio.impl.PiGpioSocketImpl.spiXfer(PiGpioSocketImpl.java:1477)
	at [email protected]/com.pi4j.plugin.pigpio.provider.spi.PiGpioSpi.transfer(PiGpioSpi.java:167)
	at [email protected]/com.pi4j.io.spi.Spi.transfer(Spi.java:253)
	at [email protected]/com.pi4j.io.spi.Spi.transfer(Spi.java:314)

At the moment I don't see any possibility to handle it in my implementation because I expect three bytes and the readBuffer.postion is three as well as spi.transfer returns three.

If I look into com.pi4j.library.pigpio.impl.PiGpioSocketImpl.spiXfer I wonder if int actual = rx.result(); is really correct or if it should be int actual = rx.dataLength();.

Is the there a relation to #16?

What do you think is the best solution to get this issue solved?

@taartspi
Copy link
Collaborator

taartspi commented Nov 6, 2024

I ordered this chip to see if I can duplicate your error. A couple things I will/would try with the chip.

  1. On the first error, some transfers do not return data. In the data sheet it mentions:
    If necessary, it is possible to bring CS low and clock in
    leading zeros on the DIN line before the start bit. This is
    often done when dealing with microcontroller-based
    SPI ports that must send 8 bits at a time. Refer to
    Section 6.1 “Using the MCP3204/3208 with Microcontroller (MCU) SPI Ports” for more details on using
    the MCP3204/3208 devices with hardware SPI ports.

I believe the pigpio spi always send 8 bits, have you tried as suggested ?

  1. Does the SPI frequency alter the behavior ?
    Section 6.2 must not exceed 1.2 ms (effective clock
    frequency of 10 kHz)

  2. Reading six bytes of data. Not sure on your question whether the code is incorrect. I need to dig and see if this condition could actually happen, you requested 3 bytes and get 6, and should be an error.

If you solve this please update the post, otherwise when I have a chip I will do some testing myself.

@beulenbilly
Copy link
Author

Yes I always send 3 bytes and as far as I can see there no other option.

I altered baud rate but I couldn't see any difference.

I used pigpioj-java which is based on netty and it works without any problems. When I looked into the library there is wait for response implemented. This approach I applied into your code (just for testing):

com.pi4j.library.pigpio.PiGpioPacket:

switch (packet.cmd) {
            case I2CRI:
            case I2CRD:
				return packet.p3;
			case SPIX:
				while (stream.available() == 0) {
					logger.info("have spi xfer without data ... waiting");
					try {
						Thread.sleep(50);
					} catch (InterruptedException ex) {
						ex.printStackTrace();
					}
				}
            default:
                return stream.available();
        }

In my tests the change resulted in the data being able to be read.

Assuming this error is not caused by the MCP3208 but in the availability of the data from the socket's InputStream. In the PIGPIO documentation I couldn't find that an end character is used in communication, because I would use that to detect the end of the data together with a read timeout.

Do you know if there is end character?

@taartspi
Copy link
Collaborator

I do not know if there is an end character, and prefer we not inspect for a character to know the data was received. I was looking at your change that fixed your problem. Would you please paste your code where you create the SPI device, and when you call the transfer. Thanks much

@beulenbilly
Copy link
Author

I used for my tests this implementation:

import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiProvider;
import com.pi4j.plugin.pigpio.provider.spi.PiGpioSpiProvider;
import java.nio.ByteBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Pi4jTest {

	private static final Logger logger = LoggerFactory.getLogger(Pi4jTest.class);

	public static void main(String[] args) throws InterruptedException {
		Context pi4JContext = Pi4J.newAutoContext();
		SpiProvider spiProvider = pi4JContext.provider(PiGpioSpiProvider.class);
		Spi spi = spiProvider.create(Spi.newConfigBuilder(pi4JContext)
				.id("my-spi-device")
				.name("My SPI Device")
				.address(0)
				.baud(2 * Spi.DEFAULT_BAUD)
				.build());

		final SpiReader spiReader = new SpiReader(spi);

		ExecutorService executor = Executors.newSingleThreadExecutor();//newFixedThreadPool(10);

		for (int i = 0; i < 60; i++) {
			executor.submit(new Callable<Object>() {
				@Override
				public Object call() throws Exception {
					return spiReader.read(0);
				}
			});

			Thread.sleep(1000);
		}
		executor.shutdown();
		pi4JContext.shutdown();
	}

	private static class SpiReader {

		private final Spi spi;

		public SpiReader(Spi spi) {
			this.spi = spi;
		}

		public synchronized Object read(int channel) {
			byte[] rxBuffer = new byte[]{-1, -1, -1};
			ByteBuffer readBuffer = ByteBuffer.allocate(3);
			spi.transfer(createWriteBuffer(channel), readBuffer, 3);
			logData(readBuffer.array());
			return readBuffer;
		}

		private ByteBuffer createWriteBuffer(int channel) {
			return ByteBuffer.wrap(createWriteBytes(channel));
		}

		private byte[] createWriteBytes(int channel) {
			return new byte[]{
				(byte) (0b00000110 | ((channel & 0x0007) >> 2)), // first byte, start bit
				(byte) (((channel & 0x0007) << 6)), // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
				(byte) 0b00000000 // third byte transmitted....don't care
			};
		}

		private void logData(byte[] result) {
			int value = ((result[1] & 0x0f) << 8);
			value |= (result[2] & 0xff);

			logger.info("received value: {}", (value & 0x0fff));
		}
	}

}

Some explanations:
In my application I use threads to read the SPI values. To avoid any issues while reading the method is synchronized.
If I remove these parts the errors are still there, sometimes just fewer.

import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiProvider;
import com.pi4j.plugin.pigpio.provider.spi.PiGpioSpiProvider;
import java.nio.ByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Pi4jTest {

	private static final Logger logger = LoggerFactory.getLogger(Pi4jTest.class);

	public static void main(String[] args) throws InterruptedException {
		Context pi4JContext = Pi4J.newAutoContext();
		SpiProvider spiProvider = pi4JContext.provider(PiGpioSpiProvider.class);
		Spi spi = spiProvider.create(Spi.newConfigBuilder(pi4JContext)
				.id("my-spi-device")
				.name("My SPI Device")
				.address(0)
				.baud(2 * Spi.DEFAULT_BAUD)
				.build());

		final SpiReader spiReader = new SpiReader(spi);

//		ExecutorService executor = Executors.newSingleThreadExecutor();

		for (int i = 0; i < 60; i++) {
//			executor.submit(new Callable<Object>() {
//				@Override
//				public Object call() throws Exception {
//					return spiReader.read(0);
//				}
//			});
			spiReader.read(0);
			Thread.sleep(1000);
		}
//		executor.shutdown();
		pi4JContext.shutdown();
	}

	private static class SpiReader {

		private final Spi spi;

		public SpiReader(Spi spi) {
			this.spi = spi;
		}

		public /*synchronized*/ Object read(int channel) {
			byte[] rxBuffer = new byte[]{-1, -1, -1};
			ByteBuffer readBuffer = ByteBuffer.allocate(3);
			spi.transfer(createWriteBuffer(channel), readBuffer, 3);
			logData(readBuffer.array());
			return readBuffer;
		}

		private ByteBuffer createWriteBuffer(int channel) {
			return ByteBuffer.wrap(createWriteBytes(channel));
		}

		private byte[] createWriteBytes(int channel) {
			return new byte[]{
				(byte) (0b00000110 | ((channel & 0x0007) >> 2)), // first byte, start bit
				(byte) (((channel & 0x0007) << 6)), // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
				(byte) 0b00000000 // third byte transmitted....don't care
			};
		}

		private void logData(byte[] result) {
			int value = ((result[1] & 0x0f) << 8);
			value |= (result[2] & 0xff);

			logger.info("received value: {}", (value & 0x0fff));
		}
	}

}

@taartspi
Copy link
Collaborator

You create the provider as spiProvider the upper interface class rather than pigpio-spi. I will need to run your code to validate but i suspect you follow a different path than my transfer. https://github.com/Pi4J/pi4j-example-devices/blob/master/src/main/java/com/pi4j/devices/mcp3008/MCP3008.java. I need to try your code before i have a suggestion. Maybe someone following this thread has time/ideas

@beulenbilly
Copy link
Author

I think I didn't mention it: I run it with -Dpi4j.remote=true -Dpi4j.host=localhost -Dpi4j.port=8888

@taartspi
Copy link
Collaborator

I mentioned your provider create using the parent class. My code does not enter that function you were modifying for a fix. I ran your code and the transfer runs spi class functions prior to the PigpioSpi class. My implementation directly enters the Pigpio classes. I would suggest you try using the provider create as pigpio-spi as I do in that example. Please let me know what happens. Tom

@beulenbilly
Copy link
Author

I changed my test implementation to:

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.util.StatusPrinter;
import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.exception.IOException;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiBus;
import com.pi4j.io.spi.SpiChipSelect;
import com.pi4j.io.spi.SpiMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Pi4jTest {

	private static final Logger logger = LoggerFactory.getLogger(Pi4jTest.class);

	public static void main(String[] args) throws InterruptedException {
		configureConsoleLogging();

		Context pi4JContext = Pi4J.newAutoContext();
		SpiChipSelect chipSelect = SpiChipSelect.CS_0;
		SpiBus spiBus = SpiBus.BUS_0;

		MCP3008 spiCls = new MCP3008(pi4JContext, spiBus, chipSelect);

		for (int i = 0; i < 60; i++) {
			short channel = 0;
			spiCls.getConversionValue(channel);
			Thread.sleep(1000);
		}
		pi4JContext.shutdown();
	}

	private static class MCP3008 {

		private static final Logger logger = LoggerFactory.getLogger(MCP3008.class);
		// SPI device
		//  public SpiDevice spi;
		// file
		private Spi spi;

		private final SpiChipSelect chipSelect;
		private final SpiBus spiBus;

		private final Context pi4j;

		public MCP3008(Context pi4j, SpiBus spiBus, SpiChipSelect chipSelect) {
			this.pi4j = pi4j;
			this.chipSelect = chipSelect;
			this.spiBus = spiBus;
			this.init();

		}

		private void init() {
			var spiConfig = Spi.newConfigBuilder(pi4j)
					.id("SPI" + spiBus + " " + chipSelect)
					.name("A/D converter")
					.bus(spiBus)
					.chipSelect(chipSelect)
					.flags(0b0000000000000000000000L)
					.baud(Spi.DEFAULT_BAUD)
					.mode(SpiMode.MODE_0)
					.provider("pigpio-spi")
					.build();
			this.spi = this.pi4j.create(spiConfig);

		}

		/**
		 * Communicate to the ADC chip via SPI to get single-ended conversion
		 * value for a specified channel.
		 *
		 * @param channel analog input channel on ADC chip
		 * @return conversion value for specified analog input channel
		 * @throws IOException
		 */
		public int getConversionValue(short channel) throws IOException {
			logger.trace(">>> Enter getConversionValue  channel : " + channel);

			// create a data buffer and initialize a conversion request payload
			byte[] data = new byte[]{(byte) 0b00000001, // first byte, start bit
				(byte) (0b10000000 | (((channel & 7) << 4))), // second byte
				// transmitted
				// -> (SGL/DIF =
				// 1,
				// D2=D1=D0=0)
				(byte) 0b00000000 // third byte transmitted....don't care
		};

			// send conversion request to ADC chip via SPI channel
			//int bytesWritten = this.spi.write(data);
			byte[] value = new byte[3];
			int bytesRead = this.spi.transfer(data, 0, value, 0, 3);

			// calculate and return conversion value from result bytes
			int result = (value[1] << 8) & 0b1100000000; // merge value[1] & value[2]
			// to get 10-bit result
			result |= (value[2] & 0xff);
			logger.info("Channel : " + channel + "   Bytes read : " + bytesRead + "  Value : " + result);
			logger.trace("<<< Exit getConversionValue ");

			return result;
		}

	}

	public static void configureConsoleLogging() {
		LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory();
		PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
		logEncoder.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
		logEncoder.setContext(logCtx);
		logEncoder.start();

		ConsoleAppender logConsoleAppender = new ConsoleAppender();
		logConsoleAppender.setContext(logCtx);
		logConsoleAppender.setName("console");
		logConsoleAppender.setEncoder(logEncoder);
		logConsoleAppender.start();

		ch.qos.logback.classic.Logger log = logCtx.getLogger("ROOT");
		log.setAdditive(false);
		log.setLevel(ch.qos.logback.classic.Level.TRACE);
		log.addAppender(logConsoleAppender);

		StatusPrinter.printInCaseOfErrorsOrWarnings(logCtx);

	}
}

The result is the same:

07:39:31.676 [main] TRACE de.billu.pi4j.test.Pi4jTest$MCP3008 -- >>> Enter getConversionValue  channel : 0
2024-11-30 07:39:31,676 TRACE [main] d.b.p.t.Pi4jTest$MCP3008 [Pi4jTest.java:83] >>> Enter getConversionValue  channel : 0
07:39:31.676 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] -> [16]; Serial Transfer [3 bytes]
2024-11-30 07:39:31,676 TRACE [main] c.p.l.p.i.PiGpioSocketImpl [PiGpioSocketImpl.java:1465] [SPI::XFER] -> [16]; Serial Transfer [3 bytes]
07:39:31.676 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketBase -- [TX] -> CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x01 80 00]
2024-11-30 07:39:31,676 TRACE [main] c.p.l.p.i.PiGpioSocketBase [PiGpioSocketBase.java:210] [TX] -> CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x01 80 00]
07:39:31.680 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketBase -- [RX] <- CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x]
2024-11-30 07:39:31,680 TRACE [main] c.p.l.p.i.PiGpioSocketBase [PiGpioSocketBase.java:216] [RX] <- CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x]
07:39:31.680 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] <- HANDLE=16; SUCCESS=true; BYTES-READ=0
2024-11-30 07:39:31,680 TRACE [main] c.p.l.p.i.PiGpioSocketImpl [PiGpioSocketImpl.java:1472] [SPI::XFER] <- HANDLE=16; SUCCESS=true; BYTES-READ=0
07:39:31.680 [main] INFO de.billu.pi4j.test.Pi4jTest$MCP3008 -- Channel : 0   Bytes read : 3  Value : 0
2024-11-30 07:39:31,680 INFO [main] d.b.p.t.Pi4jTest$MCP3008 [Pi4jTest.java:104] Channel : 0   Bytes read : 3  Value : 0
07:39:31.680 [main] TRACE de.billu.pi4j.test.Pi4jTest$MCP3008 -- <<< Exit getConversionValue 
2024-11-30 07:39:31,680 TRACE [main] d.b.p.t.Pi4jTest$MCP3008 [Pi4jTest.java:105] <<< Exit getConversionValue 
07:39:32.684 [main] TRACE de.billu.pi4j.test.Pi4jTest$MCP3008 -- >>> Enter getConversionValue  channel : 0
2024-11-30 07:39:32,684 TRACE [main] d.b.p.t.Pi4jTest$MCP3008 [Pi4jTest.java:83] >>> Enter getConversionValue  channel : 0
07:39:32.684 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] -> [16]; Serial Transfer [3 bytes]
2024-11-30 07:39:32,684 TRACE [main] c.p.l.p.i.PiGpioSocketImpl [PiGpioSocketImpl.java:1465] [SPI::XFER] -> [16]; Serial Transfer [3 bytes]
07:39:32.684 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketBase -- [TX] -> CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x01 80 00]
2024-11-30 07:39:32,684 TRACE [main] c.p.l.p.i.PiGpioSocketBase [PiGpioSocketBase.java:210] [TX] -> CMD=SPIX(75); P1=16; P2=0; P3=3; PAYLOAD=[0x01 80 00]
07:39:32.714 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketBase -- [RX] <- CMD=UNKNOWN(-1); P1=268435456; P2=0; P3=6; PAYLOAD=[0x00 00 00 00 01 74]
2024-11-30 07:39:32,714 TRACE [main] c.p.l.p.i.PiGpioSocketBase [PiGpioSocketBase.java:216] [RX] <- CMD=UNKNOWN(-1); P1=268435456; P2=0; P3=6; PAYLOAD=[0x00 00 00 00 01 74]
07:39:32.714 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] <- HANDLE=16; SUCCESS=true; BYTES-READ=6
2024-11-30 07:39:32,714 TRACE [main] c.p.l.p.i.PiGpioSocketImpl [PiGpioSocketImpl.java:1472] [SPI::XFER] <- HANDLE=16; SUCCESS=true; BYTES-READ=6
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 6 out of bounds for byte[3]
	at java.base/java.lang.System.arraycopy(Native Method)
	at com.pi4j.library.pigpio.impl.PiGpioSocketImpl.spiXfer(PiGpioSocketImpl.java:1477)
	at com.pi4j.plugin.pigpio.provider.spi.PiGpioSpi.transfer(PiGpioSpi.java:167)
	at de.billu.pi4j.test.Pi4jTest$MCP3008.getConversionValue(Pi4jTest.java:98)
	at de.billu.pi4j.test.Pi4jTest.main(Pi4jTest.java:32)

Let me know if I removed some essential parts from your example.

@taartspi
Copy link
Collaborator

taartspi commented Dec 2, 2024

I received an MCP3208 to test. I ran your original example to see it returned 0 most the time. I changed your code a bit in the SPI creation, see below. The chip vdd is 3.3v. Now with the vref 3.3 v. Grounding channel 0 the reading is zero, connecting channel 0 to 3.3v the reading is 4093

2024-12-01 20:27:33:673 -0600 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::OPEN] -> Open SPI Channel [0] at Baud Rate [1000000]; Flags=[0]
2024-12-01 20:27:33:674 -0600 [main] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::OPEN] <- HANDLE=0; SUCCESS=true
2024-12-01 20:27:33:681 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes]
2024-12-01 20:27:33:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=3
2024-12-01 20:27:33:684 -0600 [pool-1-thread-1] INFO com.pi4j.test.devices.dht22.Pi4jTest - received value: 4093
2024-12-01 20:27:34:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes]
2024-12-01 20:27:34:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=3
2024-12-01 20:27:34:682 -0600 [pool-1-thread-1] INFO com.pi4j.test.devices.dht22.Pi4jTest - received value: 4091
2024-12-01 20:27:35:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes]
2024-12-01 20:27:35:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=3
2024-12-01 20:27:35:683 -0600 [pool-1-thread-1] INFO com.pi4j.test.devices.dht22.Pi4jTest - received value: 4093
2024-12-01 20:27:36:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] -> [0]; Serial Transfer [3 bytes]
2024-12-01 20:27:36:682 -0600 [pool-1-thread-1] TRACE com.pi4j.library.pigpio.impl.PiGpioNativeImpl - [SPI::XFER] <- HANDLE=0; SUCCESS=true; BYTES-READ=3
2024-12-01 20:27:36:683 -0600 [pool-1-thread-1] INFO com.pi4j.test.devices.dht22.Pi4jTest - received value: 4094

Your first example modified by me. Commented out your creation and added mine, see the use of Pi4J Context, and I did not test the manner in which you created the provider, I used the mechanism I usually use.

package com.pi4j.test.devices.dht22;
import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiChipSelect;
import com.pi4j.io.spi.SpiMode;
import com.pi4j.io.spi.SpiProvider;
//import com.pi4j.plugin.pigpio.*;
//import com.pi4j.plugin.pigpio.provider.spi.PiGpioSpiProvider;
import java.nio.ByteBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.pi4j.plugin.pigpio.provider.spi.PiGpioSpiProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Pi4jTest {

    private static final Logger logger = LoggerFactory.getLogger(Pi4jTest.class);

    public static void main(String[] args) throws InterruptedException {
        Context pi4JContext = Pi4J.newAutoContext();
      /*  SpiProvider spiProvider = pi4JContext.provider(PiGpioSpiProvider.class);
        Spi spi = spiProvider.create(Spi.newConfigBuilder(pi4JContext)
                .id("my-spi-device")
                .name("My SPI Device")
                .address(0)
                .baud(2 * Spi.DEFAULT_BAUD)
                .build());*/

        var spiConfig = Spi.newConfigBuilder(pi4JContext)
                .id("SPI" + 0 + "CE  " +0)
                .name("A/D converter")
                .bus(0)
                .chipSelect(SpiChipSelect.CS_0)
                .flags(0b0000000000000000000000L)
                .baud(Spi.DEFAULT_BAUD)
                .mode(SpiMode.MODE_0)
                .provider("pigpio-spi")
                .build();
        var spi = pi4JContext.create(spiConfig);
        final SpiReader spiReader = new SpiReader(spi);

        ExecutorService executor = Executors.newSingleThreadExecutor();//newFixedThreadPool(10);

        for (int i = 0; i < 60; i++) {
            executor.submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    return spiReader.read(0);
                }
            });

            Thread.sleep(1000);
        }
        executor.shutdown();
        pi4JContext.shutdown();
    }

    private static class SpiReader {

        private final Spi spi;

        public SpiReader(Spi spi) {
            this.spi = spi;
        }

        public synchronized Object read(int channel) {
            byte[] rxBuffer = new byte[]{-1, -1, -1};
            ByteBuffer readBuffer = ByteBuffer.allocate(3);
            spi.transfer(createWriteBuffer(channel), readBuffer, 3);
            logData(readBuffer.array());
            return readBuffer;
        }

        private ByteBuffer createWriteBuffer(int channel) {
            return ByteBuffer.wrap(createWriteBytes(channel));
        }

        private byte[] createWriteBytes(int channel) {
            return new byte[]{
                    (byte) (0b00000110 | ((channel & 0x0007) >> 2)), // first byte, start bit
                    (byte) (((channel & 0x0007) << 6)), // second byte transmitted -> (SGL/DIF = 1, D2=D1=D0=0)
                    (byte) 0b00000000 // third byte transmitted....don't care
            };
        }

        private void logData(byte[] result) {
            int value = ((result[1] & 0x0f) << 8);
            value |= (result[2] & 0xff);

            logger.info("received value: {}", (value & 0x0fff));
        }
    }

}

@beulenbilly
Copy link
Author

It looks like that your way of configuration results in less errors but it is present:

07:25:00.597 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] -> [19]; Serial Transfer [3 bytes]
07:25:00.597 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketBase -- [TX] -> CMD=SPIX(75); P1=19; P2=0; P3=3; PAYLOAD=[0x06 00 00]
07:25:00.606 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketBase -- [RX] <- CMD=SPIX(75); P1=19; P2=0; P3=3; PAYLOAD=[0x]
07:25:00.606 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] <- HANDLE=19; SUCCESS=true; BYTES-READ=0
07:25:00.606 [pool-1-thread-1] INFO  de.billu.pi4j.test.Pi4jTest -- received value: 0
07:25:01.597 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] -> [19]; Serial Transfer [3 bytes]
07:25:01.598 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketBase -- [TX] -> CMD=SPIX(75); P1=19; P2=0; P3=3; PAYLOAD=[0x06 00 00]
07:25:01.619 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketBase -- [RX] <- CMD=UNKNOWN(-1); P1=318767104; P2=0; P3=6; PAYLOAD=[0x00 00 00 00 05 C1]
07:25:01.619 [pool-1-thread-1] TRACE c.p.l.pigpio.impl.PiGpioSocketImpl -- [SPI::XFER] <- HANDLE=19; SUCCESS=true; BYTES-READ=6

I don't if the difference comes from different java versions:
For running the test code I use:
java -version
openjdk version "21.0.5" 2024-10-15
OpenJDK Runtime Environment (build 21.0.5+11-Ubuntu-1ubuntu124.04)
OpenJDK 64-Bit Server VM (build 21.0.5+11-Ubuntu-1ubuntu124.04, mixed mode, sharing)

I realized the error on a Raspberry PI4:
java -version
openjdk version "17.0.12" 2024-07-16
OpenJDK Runtime Environment (build 17.0.12+7-Raspbian-2deb12u1rpt1)
OpenJDK Client VM (build 17.0.12+7-Raspbian-2deb12u1rpt1, mixed mode, emulated-client)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants