Skip to content

Instantly share code, notes, and snippets.

@awstanley
Created June 13, 2014 06:32
Show Gist options
  • Save awstanley/9d411c27ef47a92611ba to your computer and use it in GitHub Desktop.
Save awstanley/9d411c27ef47a92611ba to your computer and use it in GitHub Desktop.
Clean room hidapi based blink1 miniature library implementation. I didn't even realise hidapi was used in the official tool until someone mentioned it; my general disdain for dealing with the compile issues usually boiled down to dynamic library imports (dlopen, etc.) instead of trying to fix it.
/*
Copyright (c) 2014, A.W. Stanley
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-----------------------------------------------------------------------
hidapi is used in this code under the BSD Licence.
This was built and designed because I wanted something lightweight,
portable, and got tired of compilation issues whenever I updated the
repository.
The filename (blinkdll_main.cpp) stems from its original purpose, and
its occasional use.
Built from:
https://github.com/todbot/blink1/blob/master/docs/blink1-hid-commands.md
*/
#if defined _WIN32 || defined _WIN64
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <blink1.hpp>
#include <vector>
#include <thread>
#include <chrono>
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include <hidapi.h>
#define THINGM_VENDOR_ID 0x27B8
#define BLINK1M_PRODUCT_ID 0x1ED
std::vector<wchar_t*> gBlinkStore;
namespace blink1
{
enum eMk
{
eMk1 = 0,
eMk2
};
eMk get_build(wchar_t *s)
{
static const char hex[] = "0123456789ABCDEF";
unsigned char g = hex[*s % 16];
eMk rv = eMk1;
if (g >= 2){rv = eMk2;}
return rv;
}
// Configure based on the WS2812 RGB LED:
// http://rgb-123.com/ws2812-color-output/
// Output (as provided) from: GammaE=255*(res/255).^(1/.45)
int GammaE[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8,
8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14,
15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22,
23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32,
33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 44,
45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89, 90, 91,
93, 94, 95, 96, 98, 99, 100, 102, 103, 104, 106, 107, 109,
110, 111, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126,
128, 129, 131, 132, 134, 135, 137, 138, 140, 142, 143, 145,
146, 148, 150, 151, 153, 155, 157, 158, 160, 162, 163, 165,
167, 169, 170, 172, 174, 176, 178, 179, 181, 183, 185, 187,
189, 191, 193, 194, 196, 198, 200, 202, 204, 206, 208, 210,
212, 214, 216, 218, 220, 222, 224, 227, 229, 231, 233, 235,
237, 239, 241, 244, 246, 248, 250, 252, 255 };
bool blink1_write(wchar_t* s, void* b, int l)
{
int rc = 0;
hid_device *D = hid_open(THINGM_VENDOR_ID, BLINK1M_PRODUCT_ID, s);
if (D != NULL)
{
rc = hid_send_feature_report(D,(unsigned char*)b,l);
hid_close(D);
}
return (rc != -1);
}
bool blink1_read(wchar_t* s, void* b, int l)
{
int rc = 0;
hid_device *D = hid_open(THINGM_VENDOR_ID, BLINK1M_PRODUCT_ID, s);
if (D != NULL)
{
//rc = hid_send_feature_report(D, (unsigned char*)b, l);
rc = hid_get_feature_report(D, (unsigned char*)b, l);
hid_close(D);
}
return (rc != -1);
}
void MilliSleep(unsigned short sleep)
{
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
}
void empty_store()
{
while (gBlinkStore.begin() != gBlinkStore.end())
{
gBlinkStore.erase(gBlinkStore.begin());
}
}
void update_store()
{
static bool init = false;
if (!init)
{
hid_init();
}
struct hid_device_info *devs, *cur_dev;
empty_store();
devs = hid_enumerate(THINGM_VENDOR_ID, BLINK1M_PRODUCT_ID);
cur_dev = devs;
int length, i;
while (cur_dev)
{
length = wcslen(cur_dev->serial_number);
wchar_t *c = new wchar_t[length+1];
wmemcpy(c, cur_dev->serial_number, length);
c[length] = 0;
gBlinkStore.push_back(c);
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
}
void print_device_serials()
{
update_store();
for (int i = 0; i < gBlinkStore.size(); i++)
{
printf("Serial (%i): %ls\r\n",
i, gBlinkStore[i]);
}
}
// Copy out
wchar_t** get_blink1_devices(int &serial_count)
{
update_store();
serial_count = gBlinkStore.size();
wchar_t **serials = new wchar_t*[gBlinkStore.size()];
int length;
for (int i = 0; i < gBlinkStore.size(); i++)
{
length = wcslen(gBlinkStore[i]);
serials[i] = new wchar_t[length+1];
wmemcpy(serials[i], gBlinkStore[i], length);
serials[i][length] = 0;
}
return serials;
}
// x Set RGB color now format: {'n', r,g,b, 0,0, 0,0 }
void set_rgb(wchar_t *s, unsigned char r, unsigned char g,
unsigned char b) {
unsigned char B[9]={1,'n',r,g,b,0,0,0,0};
blink1_write(s, B, 9);
}
// x Fade to RGB color format: {'c', r,g,b, th,tl, 0,0 }
void fade_to_rgb(wchar_t *s, int f, unsigned char r,
unsigned char g, unsigned char b) {
unsigned char B[9]={1,'c',r,g,b,((f/10)>>8),(f/10)%0xFF,0,0};
blink1_write(s, B, 9);
}
// todo: emulate count and end
void play(wchar_t *s, unsigned char o, unsigned char b,
unsigned char e, unsigned char c) {
if (get_build(s) == eMk1) { c = 0; e = 0; }
unsigned char B[9]={1,'p',o,b,e,c,0,0,0};
blink1_write(s, B, 9);
}
void servertickle(wchar_t *s, unsigned char o,
unsigned char f, unsigned char b,
unsigned char e, unsigned char m)
{
if (get_build(s) == eMk1) { m = 0; }
unsigned char B[9] = {1,'D',o,((f/10)>>8),(f/10)%0xFFc,m,b,e};
blink1_write(s, B, 9);
}
void write_pattern_step(wchar_t *s, unsigned short f, unsigned char r,
unsigned char g, unsigned char b, unsigned char p) {
unsigned char B[9]={1,'P',GammaE[r],GammaE[g],GammaE[b],((f/10)>>8),((f/10)%0xff),p,0};
blink1_write(s,B,9);
}
// x Fade to RGB color format: {'c', r,g,b, th,tl, 0,0 }
void fade_to_rgbn(wchar_t *s, int f, unsigned char r,
unsigned char g, unsigned char b, unsigned char n) {
unsigned char B[9]={1,'c',r,g,b,((f/10)>> 8),(f/10)%0xFF,n,0};
blink1_write(s, B, 9);
}
int get_rgb(wchar_t *s, unsigned char &r,
unsigned char &g, unsigned char &b)
{
unsigned char B[9] = { 1 };
if (blink1_read(s,B,9))
{
r = B[2]; g = B[3]; b = B[4];
return true;
}
return false;
}
}
/*
Copyright (c) 2014, A.W. Stanley
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-----------------------------------------------------------------------
hidapi is used in this code under the BSD Licence.
This was built and designed because I wanted something lightweight,
portable, and got tired of compilation issues whenever I updated the
repository.
The filename (blinkdll_main.cpp) stems from its original purpose, and
its occasional use.
Built from:
https://github.com/todbot/blink1/blob/master/docs/blink1-hid-commands.md
*/
#pragma once
// To prevent hidapi's inclusion directly:
#include <wchar.h>
// Windows requires this:
#if defined _WIN32 || defined _WIN64
#pragma comment (lib, "setupapi.lib")
#endif
namespace blink1
{
void print_device_serials();
wchar_t **get_blink1_devices(int &count);
void fade_to_rgb(wchar_t *serial, int fade_duration,
unsigned char red, unsigned char green, unsigned char blue);
void set_rgb(wchar_t *serial, unsigned char red,
unsigned char green, unsigned char blue);
void fade_to_rgbn(wchar_t *s, int f, unsigned char r,
unsigned char g, unsigned char b, unsigned char n);
int get_rgb(wchar_t *s, unsigned char &r,
unsigned char &g, unsigned char &b);
void servertickle(wchar_t *s, unsigned char on_off,
unsigned char timeout, unsigned char startpos,
unsigned char endpos, unsigned char maintain=0);
void write_pattern_step(wchar_t *s, unsigned short f,
unsigned char r, unsigned char g,
unsigned char b, unsigned char p);
void play(wchar_t *dev, unsigned char play,
unsigned char start, unsigned char end=0,
unsigned char count=0);
}
/*
Copyright (c) 2014, A.W. Stanley
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <blink1.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <random>
#include <thread>
int iter_count = 25;
void thread(wchar_t *s)
{
std::mt19937 rnd;
std::random_device rd;
rnd.seed(rd());
int r, g, b;
for (int j = 0; j < 25; j++)
{
r = rnd() % 255;
g = rnd() % 255;
b = rnd() % 255;
blink1::fade_to_rgb(s, 500, r,g,b);
printf("Serial: %ls (0x%02X,0x%02X,0x%02X)\n", s, r, g, b);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
int main(int argc, char **argv)
{
int count;
wchar_t **blinks = blink1::get_blink1_devices(count);
for (int i = 0; i < count; i++)
{
std::thread t(thread, blinks[i]);
t.detach();
}
std::this_thread::sleep_for(std::chrono::seconds(iter_count));
// Turn them off when we're done
for (int i = 0; i < count; i++)
{
blink1::servertickle(blinks[i], 0, 0, 0, 0, 0);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment