/node-divoom-timebox-evo

A node module to generate messages for the Divoom Timebox Evo compatible with the module bluetooth-serial-port (https://github.com/eelcocramer/node-bluetooth-serial-port)

Primary LanguageTypeScriptMIT LicenseMIT

node-divoom-timebox-evo

This module helps you generate the appropriate message to use against the divoom timebox evo.

The communication part is not implemented here and you can use the bluetooth-serial-port module to communicate with the timebox.

Here's how you could do it:

const TIMEBOX_ADDRESS = "11:22:33:44:55:66";
var btSerial = new (require('bluetooth-serial-port')).BluetoothSerialPort();
var Divoom = require('node-divoom-timebox-evo');

btSerial.findSerialPortChannel(TIMEBOX_ADDRESS, function(channel) {
  btSerial.connect(address, channel, function() {
    console.log('connected');

    btSerial.on('data', function(buffer) {
      console.log(buffer.toString('ascii'));
    });
  }, function () {
    console.log('cannot connect');
  });
}, function() {
  console.log('found nothing');
});


var d = (new Divoom.TimeboxEvo()).createRequest('animation');
d.read('animation.gif').then(result => {
  result.asBinaryBuffer().forEach(elt => {
    btSerial.write(elt,
      function(err, bytesWritten) {
        if (err) console.log(err);
      }
    );
  })
}).catch(err => {
  throw err;
});

Protocol Documentation

See Protocol

Install

npm i node-divoom-timebox-evo

Some Examples

See the complete documentation here

Create a request

You'll always have to create a request first:

var Divoom = require('node-divoom-timebox-evo');
var d = (new Divoom.TimeboxEvo()).createRequest('REQUEST_TYPE');

// d.messages.asBinaryBuffer() is an array of messages you have to send to your Timebox Evo over bluetooth as is. It returns an array of Buffers with Binary content.
console.log(d.messages.asBinaryBuffer());

The different REQUEST_TYPE are:

RAW Command

There's no need to calculate the size or the CRC of the messages. This will be done automatically.

var Divoom = require('node-divoom-timebox-evo');

var d = (new Divoom.TimeboxEvo()).createRequest('raw');
d.push("4505");
d.messages.forEach(m => {
  console.log(m.message);
}) // Will display the list of messages as hexadecimals strings
console.log(d.messages.asBinaryBuffer());

Displaying an animation or a picture

var Divoom = require('node-divoom-timebox-evo');

var d = (new Divoom.TimeboxEvo()).createRequest('animation');
d.read('file.png').then(result => {
  console.log(result.asBinaryBuffer());
  // send toSend to the Divoom
  // use https://github.com/eelcocramer/node-bluetooth-serial-port
}).catch(err => {
  throw err;
});

Displaying Text

You have a number of baked in palettes:

  • PALETTE_TEXT_ON_BACKGROUND(text?: ColorInput, background?: ColorInput): Sets the text color and the background color
  • PALETTE_BLACK_ON_RAINBOW: Black text on a rainbow background
  • PALETTE_BLACK_ON_CMY_RAINBOW: Black text on a CMY rainbow background

And a number of baked in animations:

  • ANIM_STATIC_BACKGROUND: Background will not change color (useful with PALETTE_TEXT_ON_BACKGROUND)
  • ANIM_UNI_GRADIANT_BACKGROUND: Uniform background which will loop over all the colors of your palette
  • ANIM_VERTICAL_GRADIANT_BACKGROUND: Vertical gradient background which will loop over all the colors of your palette
  • ANIM_HORIZONTAL_GRADIANT_BACKGROUND: Horizontal gradient background which will loop over all the colors of your palette
var Divoom = require('node-divoom-timebox-evo');

var d = (new Divoom.TimeboxEvo()).createRequest('text', {text: "Hi friends!"});
d.paletteFn = d.PALETTE_BLACK_ON_CMY_RAINBOW; // Baked in color palette, but you can define your own
d.animFn = d.ANIM_HORIZONTAL_GRADIANT_BACKGROUND; // Baked in animation, but you can define your own

// This contains what is required to bootstrap the display on the Timebox
console.log(d.messages.asBinaryBuffer());

// Then you have to send your animation frame by frame, I suggest that you do no go over 30 message per second, if you do, the timebox will disconnect.
// This would generate 512 animation frames.
for (i = 0; i < 512; i++){
    console.log(d.getNextAnimationFrame().asBinaryBuffer());
}

You can define your own palette, the function has to return an array of 256 colors in Hex.
The text's color will be the item (background_color + 127) % 256. This is the code which generates the PALETTE_BLACK_ON_RAINBOW:

d.paletteFn = function() {
  function number2HexString(int: number): string {
    return Math.round(int).toString(16).padStart(2, "0");
  }

  let palette: string[] = [];
  const size = 127;
  function sin_to_hex(i: number, phase: number) {
    let sin = Math.sin(Math.PI / size * 2 * i + phase);
    let int = Math.floor(sin * 127) + 128;
    return number2HexString(int);
  }

  for (let i = 0; i < size; i++) {
    let red = sin_to_hex(i, 0 * Math.PI * 2 / 3); // 0   deg
    let blue = sin_to_hex(i, 1 * Math.PI * 2 / 3); // 120 deg
    let green = sin_to_hex(i, 2 * Math.PI * 2 / 3); // 240 deg
    palette.push(red + green + blue);
  }

  // Completes the palette (everything after the 127th item in the array) with black color so that the text is actually only black
  for (let i = palette.length; i < 256; i++) {
    palette.push("000000");
  }
  return palette;
}

You can also define your own animation. It should be an array of 256 entries each one representing a pixel and referencing the index of the color to be displayed, taken from the palette. The function takes a frame number as a parameter.
Example which generated the horizontal gradient background (ANIM_HORIZONTAL_GRADIANT_BACKGROUND):

d.animFn = function(frame) {
  let pixelArray = [];
  for (let y = 0; y < 16; y++) {
    for (let x = 0; x < 16; x++) {
      pixelArray.push((x + frame) % 127)
    }
  }
  return pixelArray;
}