pca9685-promise
Version:
controls a PCA9685 (PWM/Servo driver) in node.js using async-i2c-bus.
210 lines (209 loc) • 7.58 kB
JavaScript
"use strict";
/** A library to use the PCA9685 PWM led/servo controller. This library
* used the promisified version of i2c-bus: async-i2c-bus.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const async_i2c_bus_1 = require("async-i2c-bus");
const DEFAULT_ADDRESS = 0x40;
// Commands
const MODE1 = 0x00;
const MODE2 = 0x01;
const SUBADR1 = 0x02;
const SUBADR2 = 0x03;
const SUBADR3 = 0x04;
const ALLCALLADDR = 0x05;
const LED0_ON_L = 0x06;
const LED0_ON_H = 0x07;
const LED0_OFF_L = 0x08;
const LED0_OFF_H = 0x09;
const ALL_LED_ON_L = 0xFA;
const ALL_LED_ON_H = 0xFB;
const ALL_LED_OFF_L = 0xFC;
const ALL_LED_OFF_H = 0xFD;
const PRESCALE = 0xFE;
const TEST_MODE = 0xFF; // Do not use - impredictable.
// MODE1 bits
const RESTART = 0x80;
const EXTCLK = 0x40;
const AI = 0x20;
const SLEEP = 0x10;
const SUB1 = 0x08;
const SUB2 = 0x04;
const SUB3 = 0x02;
const ALLCALL = 0x01;
// MODE2 bits
const INVRT = 0x10;
const OCH = 0x08;
const OUTDRV = 0x04;
const OUTNE_OFF = 0x00;
const OUTNE_ON = 0x01;
const OUTNE_HI = 0x10;
// Pre-scaling.
const OSCILLATOR = 25000000;
const RESOLUTION = 4096;
function delay(millis) {
return new Promise((resolve) => setTimeout(resolve, millis));
}
/** The device class used to control a PCA9685 board. */
class PCA9685 {
/** Construct the device abstraction
* @param bus: I2c bus
* @param options: Optional option structure
*/
constructor(bus, options = {}) {
this.prescale = 0b11110; // default value
this.device = async_i2c_bus_1.Device({ bus, address: options.address || DEFAULT_ADDRESS });
this.oscillator = (options.oscillator || OSCILLATOR);
this.allcallAddress = options.allcallAddress;
this.prescale =
options.frequency ? this.compute_prescale(options.frequency) : 30;
this.flags =
(options.allcall ? ALLCALL : 0)
| (options.oscillator && options.oscillator > 0 ? EXTCLK : 0);
}
/** Initialize the device
*
* The bus must be opened. This step is necessary before using
* any other function.
* The library uses Auto Increment mode. It sets the flags defined
* at startup.
*/
init() {
return __awaiter(this, void 0, void 0, function* () {
const mode1 = yield this.sleep();
yield this.device.writeByte(MODE1, (mode1 & ~(RESTART | ALLCALL | EXTCLK)) | AI | this.flags);
const mode2 = yield this.device.readByte(MODE2);
yield this.device.writeByte(MODE2, mode2 | OUTDRV);
yield this.device.writeByte(PRESCALE, this.prescale);
if (this.allcallAddress) {
yield this.device.writeByte(ALLCALLADDR, this.allcallAddress);
}
yield this.restart();
});
}
/** Restarts (unconditionaly) the device if it was asleep.
*
* Should not be used for standard operation.
* @ignore
*/
restart() {
return __awaiter(this, void 0, void 0, function* () {
let mode1 = yield this.device.readByte(MODE1);
// Here we should not trigger restart.
mode1 &= ~SLEEP & ~RESTART;
yield this.device.writeByte(MODE1, mode1);
yield delay(1); // 500us at least
mode1 |= RESTART;
yield this.device.writeByte(MODE1, mode1);
yield delay(1);
return true;
});
}
/** Put the device in sleep mode and return MODE1 register
*
* Putting the device in sleep mode is often necessary to modify its
* control registers. Should not be used for standard operation.
* @ignore
*/
sleep() {
return __awaiter(this, void 0, void 0, function* () {
let mode1 = yield this.device.readByte(MODE1);
mode1 = (mode1 & ~RESTART) | SLEEP;
yield this.device.writeByte(MODE1, mode1);
yield delay(1); // 500us (not strictly required)
return mode1;
});
}
/** Sets the prescaler to achieve a given frequency in herz.
*
* @param freq the frequency to set
* @return an empty promise.
*/
set_frequency(freq) {
return __awaiter(this, void 0, void 0, function* () {
this.prescale = this.compute_prescale(freq);
yield this.sleep();
yield this.device.writeByte(PRESCALE, this.prescale);
yield this.restart();
});
}
/** Sets the duty cycle of a single channel.
*
* @param chan the channel to configure
* @param onValue the step where the pulse begins
* @param offValue the step where the pulse ends
* @return an empty promise.
*/
set_pwm(chan, onValue, offValue) {
return __awaiter(this, void 0, void 0, function* () {
chan &= 0xF;
onValue &= 0x0FFF;
offValue &= 0x0FFF;
const buf = Buffer.alloc(4);
buf.writeInt16LE(onValue, 0);
buf.writeInt16LE(offValue, 2);
yield this.device.writeI2cBlock(LED0_ON_L + chan * 4, 4, buf);
});
}
/** Set the pulse length in micro second.
*
* @param chan the channel to configure
* @param length the length of the pulse in microsecond
* @param start an optional offset for the start of the pulse.
* @return an empty promise.
*/
set_pwm_ms(chan, length, start = 0) {
return __awaiter(this, void 0, void 0, function* () {
const offset = length * this.oscillator / (1000000 * this.prescale);
const end = (start + offset) % RESOLUTION;
yield this.set_pwm(chan, start, end);
});
}
/** Sets the duty cycle of all channels
*
* @param onValue the step where the pulse begins
* @param offValue the step where the pulse ends
* @return an empty promise.
*/
set_all_pwm(onValue, offValue) {
return __awaiter(this, void 0, void 0, function* () {
onValue &= 0x0FFF;
offValue &= 0x0FFF;
const buf = Buffer.alloc(4);
buf.writeInt16LE(onValue, 0);
buf.writeInt16LE(offValue, 2);
yield this.device.writeI2cBlock(ALL_LED_ON_L, 4, buf);
});
}
/** Shutdown a given channel
*
* @param chan channel to shut
*/
shutdown(chan) {
return __awaiter(this, void 0, void 0, function* () {
yield this.device.writeByte(LED0_OFF_H + (chan * 4), 0x10);
});
}
/** Shutdown all channels */
shutdown_all() {
return __awaiter(this, void 0, void 0, function* () {
yield this.device.writeByte(ALL_LED_OFF_H, 0x10);
});
}
compute_prescale(freq) {
const prescale = Math.round(this.oscillator / (RESOLUTION * freq)) - 1;
if (prescale < 0x03 || prescale > 0xFF) {
throw new Error("invalid frequency");
}
return prescale;
}
}
exports.PCA9685 = PCA9685;