Skip to content

Commit

Permalink
Merge pull request #5 from rpitv/add-examples
Browse files Browse the repository at this point in the history
  • Loading branch information
robere2 authored Mar 14, 2022
2 parents 397694f + c6d9b5f commit ac387a1
Show file tree
Hide file tree
Showing 18 changed files with 392 additions and 12 deletions.
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,49 @@
## Features

- Control different types of LEDs from your application.
- Control different types of LED and LED arrays from your application.
- Single channel
- RGB/tri-channel
- Arbitrary channel count
- Diode mode, allowing you to remove a dedicated ground.
- Multi channel (e.g. RGB)
- LED segment displays
- Flash LEDs at any frequency.
- Animate your LEDs using your own custom functions.

## Necessary supplies

- Raspberry Pi (any should do, as long as it has GPIO).
- LED(s) w/ necessary resistors.
- Diodes for the ground connection, if using diode mode.
- LED(s).
- Any necessary resistors/transistors.

Hardware instructions coming sometime in the future.

## Usage

TODO Coming soon
### Basic Example

```js
const { LED } = require("pi-led-control");
// Other imports: LEDArray, Animation, Curves

const led = new LED(3);
led.write(true);
led.off();
```

### More Examples

More examples of how to use all the available imports are available in the [`/examples`](./examples) folder.

- [Basic writing](./examples/basic-write.ts)
- [Basic animation](./examples/basic-animate.ts)
- [Custom animation](./examples/custom-animate.ts)
- [Basic LEDArray writing](./examples/basic-write-array.ts)
- [Basic LEDArray animation](./examples/basic-animate-array.ts)
- [Custom LEDArray animation](./examples/custom-animate-array.ts)
- [Signal inverting](./examples/invert.ts)

### Specifications

TODO. For now, please refer to the examples and JSDocs within the code.

## Development

Expand All @@ -50,6 +74,16 @@ npm run prepare

You are now ready to write code. All application code is located within [/src](./src). Begin writing in your `.ts` files. It is presumed you will not be developing on a Raspberry Pi. If you do, then you may run the application using `npm start`. Otherwise, use `npm test` to run unit tests on your code.

### Building

The library can be built with the following command:

```shell
npm run build
```

Building and deployment is handled by CI, if you wish to use the main NPM package.

### Testing

A unit test suite is available for the full API. You may run the test suite by executing:
Expand Down
18 changes: 18 additions & 0 deletions examples/basic-animate-array.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./basic-animate-array";

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
// LED animation is turned off before it even draws its first frame.
expect(digitalSpy).toHaveBeenCalledTimes(6);
expect(pwmSpy).toHaveBeenCalledTimes(0);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[2] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[3] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[4] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[5] as any)[0]).toEqual(0);
});
31 changes: 31 additions & 0 deletions examples/basic-animate-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Animation, Curves, LEDArray } from "../src";

function run() {
// Create your LED
const led = new LEDArray([3, 4, 5]);

// Create an animation with a provided Curve function and options.
// There are multiple overloads of the LEDArray#animate() function. Check
// docs for more options. This one will animate the LEDs so that the ratio
// of brightness between each one is always the same. I.e., when the
// animation is at 30% brightness, the brightness of each LED value in the
// array is multiplied by 0.3.
led.animate(new Animation(Curves.Sawtooth(500)), [255, 50, 170]);

// Start the animation. This creates a Node.js timer.
led.startAnimation();

// This write() will succeed, but it will be immediately overwritten on the
// next frame of the animation.
led.write(true, false, true);

// Stop the animation. Does not turn off the LEDs, and whatever the state
// they were in on the last refresh is persisted until overwritten.
led.stopAnimation();

// Turn off the LED. Also stops the animation, if we hadn't previously
// called stopAnimation()
led.off();
}

export default run;
14 changes: 14 additions & 0 deletions examples/basic-animate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./basic-animate";

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
// LED animation is turned off before it even draws its first frame.
expect(digitalSpy).toHaveBeenCalledTimes(2);
expect(pwmSpy).toHaveBeenCalledTimes(0);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(0);
});
36 changes: 36 additions & 0 deletions examples/basic-animate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { LED, Animation, Curves } from "../src";

function run() {
// Create your LED
const led = new LED(3);

// Refresh rate in milliseconds for which the Animation should refresh at.
// Optional, defaults to 60fps.
const refreshRate = Math.floor((1 / 15) * 1000);
// Whether the Animation should automatically start upon construction.
// Optional, defaults to true.
const autoStart = false;
// Period of the Curve function along the X axis (milliseconds). Required.
const period = 1000;
// Create an animation with a provided Curve function and options.
led.animate(new Animation(Curves.Sine(period), refreshRate), autoStart);
// Without the optional values:
// led.animate(new Animation(Curves.Sine(1000)));

// Start the animation. This creates a Node.js timer.
led.startAnimation();

// This write() will succeed, but it will be immediately overwritten on the
// next frame of the animation.
led.write(true);

// Stop the animation. Does not turn off the LEDs, and whatever the state
// they were in on the last refresh is persisted until overwritten.
led.stopAnimation();

// Turn off the LED. Also stops the animation, if we hadn't previously
// called stopAnimation()
led.off();
}

export default run;
20 changes: 20 additions & 0 deletions examples/basic-write-array.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./basic-write-array";

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
expect(digitalSpy).toHaveBeenCalledTimes(6);
expect(pwmSpy).toHaveBeenCalledTimes(3);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[2] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[3] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[4] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[5] as any)[0]).toEqual(0);
expect((pwmSpy.mock.calls[0] as any)[0]).toEqual(255);
expect((pwmSpy.mock.calls[1] as any)[0]).toEqual(128);
expect((pwmSpy.mock.calls[2] as any)[0]).toEqual(40);
});
17 changes: 17 additions & 0 deletions examples/basic-write-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { LEDArray } from "../src";

function run() {
// Create your LEDArray. Technically each pin can appear multiple times in
// the same array, but there are probably not many cases where you would
// ever want this.
const led = new LEDArray([3, 4, 5]);
// Turn on all three LEDs.
led.write(true, true, true);
// Turn off three LEDs.
led.off();
// Write different PWM values for each LED. These PWMs will immediately stop
// as soon as the program exits, as mentioned in easier examples.
led.write(255, 128, 40);
}

export default run;
16 changes: 16 additions & 0 deletions examples/basic-write.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./basic-write";

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
// 1 for digital write, 2 for off()
expect(digitalSpy).toHaveBeenCalledTimes(3);
expect(pwmSpy).toHaveBeenCalledTimes(1);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[2] as any)[0]).toEqual(0);
expect((pwmSpy.mock.calls[0] as any)[0]).toEqual(153);
});
26 changes: 26 additions & 0 deletions examples/basic-write.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { LED } from "../src";

function run() {
// Create your LED
const led = new LED(3);
// Turn on the LED
led.write(true);
// Turn off the LED. Note, if you use led.animate(), this will stop the
// animation while using write(false) will not.
led.off();

// Write a PWM value of 153. This does not mean the LED will be on at 60%
// brightness 100% of the time. Instead, it means the LED will be at
// 100% brightness 60% of the time, and 0% brightness (off) 40% of the
// time. To the naked eye, these will look the same anyway.
led.write(153);
// Turn off the LED. Note, since pulse modulation requires the program to
// be running, if you do not set your LEDs to be either off or on before
// your program exits, whatever the last pulse they received will persist
// until overwritten by another process. E.g., with our last write() call,
// 60% of the time when the program stops, our LED will remain on. The
// other 40% of the time it will remain off.
led.off();
}

export default run;
23 changes: 23 additions & 0 deletions examples/custom-animate-array.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./custom-animate-array";

jest.useFakeTimers();

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
jest.runAllTimers();

// LED animation is turned off before it even draws its first frame.
expect(digitalSpy).toHaveBeenCalledTimes(3);
expect(pwmSpy).toHaveBeenCalledTimes(3);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[2] as any)[0]).toEqual(0);

expect((pwmSpy.mock.calls[0] as any)[0]).toEqual(130);
expect((pwmSpy.mock.calls[1] as any)[0]).toEqual(130);
expect((pwmSpy.mock.calls[2] as any)[0]).toEqual(134);
});
40 changes: 40 additions & 0 deletions examples/custom-animate-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Animation, LEDArray } from "../src";

function run() {
// Create your LED
const led = new LEDArray([3, 4, 5]);

// This is another way to write animations for LEDArrays. This allows a lot
// of flexibility as you can create an animation for each individual LED.
// Since each one now has its own animation, you no longer need the array
// of values for proportions like we did in the basic-animate-array
// example.
led.animate([
new Animation((t: number) => {
return (Math.sin(t / 1000) + 1) / 2; // Sin wave w/ period 1000ms
}),
new Animation((t: number) => {
return (Math.sin(t / 750) + 1) / 2; // Sin wave w/ period 750ms
}),
new Animation((t: number) => {
return (Math.sin(t / 333) + 1) / 2; // Sin wave w/ period 333ms
}),
]);

// Start the animation. This creates a Node.js timer.
led.startAnimation();

setTimeout(() => {
// Stop the animation. Does not turn off the LEDs, and whatever the
// state they were in on the last refresh is persisted until
// overwritten.
led.stopAnimation();

// Turn off the LEDs. Also stops the animation, if we hadn't previously
// called stopAnimation()
led.off();
}, 17); // 60 fps is once every 16ms. This will allow our LEDs to
// refresh exactly once before turning off.
}

export default run;
19 changes: 19 additions & 0 deletions examples/custom-animate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./custom-animate";

jest.useFakeTimers();

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
jest.runAllTimers();

// LED animation is turned off before it even draws its first frame.
expect(digitalSpy).toHaveBeenCalledTimes(1);
expect(pwmSpy).toHaveBeenCalledTimes(1);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(0);
// Math.floor(16 / 400 * 255)
expect((pwmSpy.mock.calls[0] as any)[0]).toEqual(10);
});
36 changes: 36 additions & 0 deletions examples/custom-animate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { LED, Animation } from "../src";

function run() {
// Create your LED
const led = new LED(3);

// Create an animation with a custom Curve function and options.
// For every time this animation refreshes, it calls our function and uses
// the output as the PWM to write to the LED. In this case, say the
// animation ends up refreshing at 750ms after construction.
// (750 / 400) % 1 = 0.875. The LED will pulse modulate such that it is lit
// up ~87.5% of the time, and turned off ~12.5% of the time until the
// program exits, or it is overwritten.
led.animate(
new Animation((time: number) => {
return (time / 400) % 1;
})
);

// Start the animation. This creates a Node.js timer.
led.startAnimation();

setTimeout(() => {
// Stop the animation. Does not turn off the LEDs, and whatever the
// state they were in on the last refresh is persisted until
// overwritten.
led.stopAnimation();

// Turn off the LED. Also stops the animation, if we hadn't previously
// called stopAnimation()
led.off();
}, 17); // 60 fps is once every 16ms. This will allow our LED to refresh
// exactly once before turning off.
}

export default run;
16 changes: 16 additions & 0 deletions examples/invert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Gpio } from "../__mocks__/pigpio";
import run from "./invert";

const digitalSpy = jest.spyOn(Gpio.prototype, "digitalWrite");
const pwmSpy = jest.spyOn(Gpio.prototype, "pwmWrite");
it("Should write the correct values to the output pin", () => {
run();
// 1 for digital write, 2 for off()
expect(digitalSpy).toHaveBeenCalledTimes(3);
expect(pwmSpy).toHaveBeenCalledTimes(1);

expect((digitalSpy.mock.calls[0] as any)[0]).toEqual(0);
expect((digitalSpy.mock.calls[1] as any)[0]).toEqual(1);
expect((digitalSpy.mock.calls[2] as any)[0]).toEqual(1);
expect((pwmSpy.mock.calls[0] as any)[0]).toEqual(102);
});
Loading

0 comments on commit ac387a1

Please sign in to comment.