fivdi / pigpio

Fast GPIO, PWM, servo control, state change notification and interrupt handling with Node.js on the Raspberry Pi
MIT License
943 stars 89 forks source link

Maximum PWM channels in use at one time #121

Open briancampo opened 3 years ago

briancampo commented 3 years ago

First off, thanks so much for this project!!

I'm trying to control several PWM channels concurrently and seem to have hit a max of 4 in use at any one time. Granted this is on a RasPi 4, but hoping you may be able to help or give some insight on some challenges with this board. I've tried a few different scenarios and get some mixed results depending on the scenario. The most peculiar is this...

I ramp up the PWMDutyCycle from 0 to 500 (range is 1000) across 5 total channels. The ramp is done sequentially with each channel ramping up to 500 via a while loop with a delay based on the duration of the ramp. When it hits 500 I end the loop and leave the channel at 500 and move on to the next. When I reach the 5th channel it does not send a signal, and what is more peculiar is that as it rises the other channels PWMDutyCycle begins to drop and when the 5th channel reaches 500 all channels are at 0 observed duty cycle. When running an led array it appears as all lights completely dark.

I can interchange the order of the channel being ramped up and it is always the 5th channel that causes this behavior.

When I try to ramp the PWMDutyCycle of the channels concurrently I can only get 3 channels working at one time before I get anomolous behavior, although different. When I try 4 or more channels concurrently I get blinking of the lights at a low duty cycle (~200) and as the duty cycle increases the PWM output goes to 0 for all channels once the duty cycle reaches around 250.

Not sure if the GPIO channels in use matter but the ones I have used for most of my testing are: 5, 6, 13, 19, 26

Let me know if any other info would be helpful..

fivdi commented 3 years ago

I am not aware of anything that would restrict the number of GPIOs that can use PWM to four.

What would help is an example program that is complete and as short as possible that can be used to reproduce the problem. Without such an example program I probably can't provide any help.

briancampo commented 3 years ago

Thanks for the super quick response. Below is a very basic implementation that is also exhibiting the issues I'm seeing. Strangely with simpler script I am seeing the first issue happen when the third channel starts ramping up. Works fine with two.

When I run all three channels simultaneously I am getting the strange flicker. On the below just remove the startDelay incrementer and you should see.

import { Gpio } from 'pigpio';

async function sleep(delay: number) {
  return new Promise(resolve => setTimeout(resolve, delay));
}

async function gpioTest(job) {
  const { targetIntensity, channel, duration, pin, startDelay } = job;
  await sleep(startDelay);
  let currentIntensity = channel.getPwmDutyCycle();
  const brighten = currentIntensity <= targetIntensity;
  const modifier = brighten ? 1 : -1;
  const steps = (targetIntensity - currentIntensity) * modifier;
  const delay = duration / steps;
  let currentStep = 0;
  while (brighten ? (currentIntensity < targetIntensity) : (targetIntensity < currentIntensity)) {
    brighten ? currentIntensity++ : currentIntensity--;
    channel.pwmWrite(currentIntensity);
    currentStep++;
    const progress = Math.floor(currentStep / steps * 100);
    // job.progress(progress);
    console.info(`Set PWM duty to ${currentIntensity} on channel ${pin}`, '   ChannelProcessor');
    // if (currentIntensity === targetIntensity) {
    //   this.cleanupJob(job);
    // }

    await sleep(delay - 10);  //TODO if this is too long of a delay we could create another while to run and update progress
  }
}

function createChannel(pin: number) {
  const gpio = new Gpio(pin, { mode: Gpio.OUTPUT, pullUpDown: Gpio.PUD_DOWN });
  gpio.pwmWrite(0);
  gpio.pwmFrequency(200);
  gpio.pwmRange(1000);
  return gpio;
}

async function run() {
  let startDelay = 0;
  const jobs = [6, 4, 22].map(pin => {
    const channel = createChannel(pin);
    // startDelay += 12 * 1000;              //<<<<< comment this for simultaneous
    return { channel, pin, startDelay, targetIntensity: 500, duration: 10 * 1000 };
  });
  await sleep(5 * 1000);
  jobs.map(async job => await gpioTest(job));
  await sleep(60 * 1000);
}
run();
fivdi commented 3 years ago

I can't reproduce the error. The below JavaScript program which is a slightly modified version of the above TypeScript program was used but in theory this shouldn't make a difference. Note that the program here used GPIOs 16, 20 and 21 rather than 6, 4 and 22.

This is what I see when the program is run:

The test was performed on a Raspberry Pi 4 using Raspberry Pi OS (32-bit) Lite:

$ uname -a
Linux raspberrypi 5.4.51-v7l+ #1333 SMP Mon Aug 10 16:51:40 BST 2020 armv7l GNU/Linux

Here are some additional details related to the setup:

$ node --version
v14.11.0
$ pigpiod -v
71
$ npm list | grep pigpio
/home/pi/pigpio
└─┬ pigpio@3.2.4
const Gpio = require('pigpio').Gpio; // <=== ********** CHANGED *******

async function sleep(delay) { // <=== ********** CHANGED *******
  return new Promise(resolve => setTimeout(resolve, delay));
}

async function gpioTest(job) {
  const { targetIntensity, channel, duration, pin, startDelay } = job;
  await sleep(startDelay);
  let currentIntensity = channel.getPwmDutyCycle();
  const brighten = currentIntensity <= targetIntensity;
  const modifier = brighten ? 1 : -1;
  const steps = (targetIntensity - currentIntensity) * modifier;
  const delay = duration / steps;
  let currentStep = 0;
  while (brighten ? (currentIntensity < targetIntensity) : (targetIntensity < currentIntensity)) {
    brighten ? currentIntensity++ : currentIntensity--;
    channel.pwmWrite(currentIntensity);
    currentStep++;
    const progress = Math.floor(currentStep / steps * 100);
    // job.progress(progress);
    console.info(`Set PWM duty to ${currentIntensity} on channel ${pin}`, '   ChannelProcessor');
    // if (currentIntensity === targetIntensity) {
    //   this.cleanupJob(job);
    // }

    await sleep(delay - 10);  //TODO if this is too long of a delay we could create another while to run and update progress
  }
}

function createChannel(pin) { // <=== ********** CHANGED *******
  const gpio = new Gpio(pin, { mode: Gpio.OUTPUT, pullUpDown: Gpio.PUD_DOWN });
  gpio.pwmWrite(0);
  gpio.pwmFrequency(200);
  gpio.pwmRange(1000);
  return gpio;
}

async function run() {
  let startDelay = 0;
  const jobs = [16, 20, 21].map(pin => {// <=== ********** CHANGED *******
    const channel = createChannel(pin);
    // startDelay += 12 * 1000;              //<<<<< comment this for simultaneous
    return { channel, pin, startDelay, targetIntensity: 500, duration: 10 * 1000 };
  });
  await sleep(5 * 1000);
  jobs.map(async job => await gpioTest(job));
  await sleep(60 * 1000);
}
run();

Questions:

fivdi commented 3 years ago

@briancampo were you able to make any progress here?

briancampo commented 3 years ago

@fivdi ,

Initially I was getting the same result when running your .js code as when I ran the .ts code. That got me thinking that maybe the issue is that I am sending the signal to drive some MeanWell LDDs which provide power to some high power LED arrays. I went back to my breadboard with bulb type LEDs and it seems to be ok.

With that I tried rebuilding everything (new drivers, new wiring, new LEDs, new Pi load, and a bare bones install. My Pi is running only node (via NVM), pigpio, Python and Python3, and Raspbian core with all updates avaialble as of 11/4. With this I am able to get 6 of the 8 channels I need running without issue, but when I try to bring in any additional channels I run into the previous issues. I tried a variety of different combos and rewiring to different channels to see if I could isolate anything but seem to come back to this.

Right now I'm trying to wire in an I2C PWM MC (PCA 9685) to see if I can rule out the software pwm or anything on the software suite as the issue, or if there might be something somewhere in the driver that is causing the issue.

Will follow up with what I get when I run the I2C controller.

Thanks again for the help and for checking in.

ChrisCates commented 3 years ago

@briancampo, @fivdi,

I'm having the same issue. I'm trying to run 12 servos concurrently. Can only directly run 4 at a time.

Very doubtful it has anything to do with pigpio library itself, but instead an issue with the amount of amperes the RPi can tolerate.

Running servos with a separate power supply. Have added shrink wrap on servo pins just in case leads are touching each other.

Still no avail.

I've been scouring the internet all over for a solution and to figure out how to mitigate this capacitor issue. Will update you if I find a proper solution.

ChrisCates commented 3 years ago

@briancampo, @fivdi,

Another update here.

Turns out it's servo related.

Mind you, trying to run 12 servos concurrent can have some latency issues. I found having a small 10ms delay before each servoWrite command helps improve latency issues.

Looks like my issue was completely hardware related. Not software.

Cheers.