various fixes, improvements, documentation
This commit is contained in:
parent
300ca56d3b
commit
3818e5b6c3
4 changed files with 179 additions and 55 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
.pio
|
||||
include/config.h
|
||||
|
|
49
README.md
Normal file
49
README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# OBS-WebSocket Tally Light
|
||||
|
||||
Quick Facts:
|
||||
|
||||
* runs on ESP8266 and similar
|
||||
* supports Authentication (but doesn't require it)
|
||||
* uses industry-standard colours
|
||||
* automatically reconnects if disconnected
|
||||
* provides debug output over serial
|
||||
* works with and without studio mode
|
||||
|
||||
## Dependencies
|
||||
|
||||
* `platformio` set up on your machine
|
||||
* an instance of OBS running the websockets plugin
|
||||
* some wifi network over which the OBS instance is reachable
|
||||
|
||||
## Setup
|
||||
|
||||
1. copy `include/config.example,h` to `include/config.h`
|
||||
2. edit `include/config.h` to match your wifi and obs settings
|
||||
3. connect your ESP8266 board using USB
|
||||
4. run `pio run -t upload` to install the tally light onto your board
|
||||
|
||||
If flashing succeeds, your LED strip should blink twice, then switch to
|
||||
tally light mode.
|
||||
|
||||
The serial console is configured to use _115200_ baud.
|
||||
|
||||
## Colours
|
||||
|
||||
### Red
|
||||
|
||||
The configured source is currently live (visible in program).
|
||||
|
||||
### Green
|
||||
|
||||
Only available in studio mode.
|
||||
|
||||
The configured source is currenly in preview.
|
||||
|
||||
### Off
|
||||
|
||||
The configured source is currenly not visible.
|
||||
|
||||
### Purple
|
||||
|
||||
Authentication failed, either by a missing or wrong password. The light
|
||||
will automatically restart after 10 seconds.
|
53
include/config.example.h
Normal file
53
include/config.example.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
OBS-Websocket-Tally
|
||||
|
||||
Please note all pin numbers use the ESP8266 pin numbers. Take a look
|
||||
at your breakout boards documentation to find out which label on the
|
||||
board corresponds to which ESP8266 pin.
|
||||
*/
|
||||
|
||||
// How many LEDs do you have connected?
|
||||
#define LED_COUNT 10
|
||||
|
||||
// Choose anything between 0 (off) and 255 (supernova-bright)
|
||||
#define LED_BRIGHTNESS 100
|
||||
|
||||
// Which pin is your LED strip connected to. The default is labeled D6
|
||||
// on NodeMCU, D0 on Wemos D1 mini.
|
||||
#define LED_PIN 12
|
||||
|
||||
// Configure your wifi credentials in here
|
||||
#define WIFI_SSID "my wifi name"
|
||||
#define WIFI_PASS "really secure password"
|
||||
|
||||
// The IP address of the machine running OBS Studio
|
||||
#define OBS_HOST "172.19.138.143"
|
||||
|
||||
// The port on which the websockets plugin listens. Default is 4444
|
||||
#define OBS_PORT 4444
|
||||
|
||||
/*
|
||||
If your websockets plugin requires authentication, set it here.
|
||||
The Tally light will automatically restart if the password is wrong
|
||||
(or unset but needed), but silently ignores if you set a password
|
||||
without needing it.
|
||||
*/
|
||||
//#define OBS_PASS ""
|
||||
|
||||
/*
|
||||
The name of the source as set in OBS. The tally light will perform
|
||||
a exact match on that value, so please make sure you enter your
|
||||
source name exactly as spelled inside OBS.
|
||||
*/
|
||||
#define OBS_SOURCE "ATEM"
|
||||
|
||||
/*
|
||||
If defined, you'll get a status LED if the tally light is connected
|
||||
to the configured OBS instance. The light will be on if the light
|
||||
is able to receive information from OBS. If there's no connection
|
||||
or authentication has failed. the light will be off.
|
||||
|
||||
The default uses the onboard LED on the NodeMCU. On Wemos D1 mini
|
||||
this pin is labeled D6.
|
||||
*/
|
||||
#define STATUS_LED 16
|
65
src/main.cpp
65
src/main.cpp
|
@ -1,20 +1,11 @@
|
|||
#define LED_COUNT 10
|
||||
#define LED_BRIGHTNESS 100
|
||||
#define LED_PIN 12
|
||||
#include <config.h>
|
||||
|
||||
#define WIFI_SSID ""
|
||||
#define WIFI_PASS ""
|
||||
|
||||
#define OBS_HOST ""
|
||||
#define OBS_PORT 4444
|
||||
//#define OBS_PASS ""
|
||||
|
||||
#define OBS_SOURCE "ATEM"
|
||||
static_assert(sizeof(WIFI_SSID) > 1, "WIFI_SSID is empty");
|
||||
static_assert(sizeof(OBS_HOST) > 1, "OBS_HOST is empty");
|
||||
static_assert(sizeof(OBS_SOURCE) > 1, "OBS_SOURCE is empty");
|
||||
|
||||
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
||||
|
||||
// END CONFIGURATION
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Hash.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
@ -27,6 +18,8 @@ CRGB leds[LED_COUNT];
|
|||
WebSocketsClient webSocket;
|
||||
|
||||
#ifdef OBS_PASS
|
||||
static_assert(sizeof(OBS_PASS) > 1, "OBS_PASS must be non-empty if defined");
|
||||
|
||||
#include <SHA256.h>
|
||||
#define HASH_SIZE 32
|
||||
|
||||
|
@ -35,6 +28,7 @@ SHA256 sha256;
|
|||
|
||||
bool is_currently_live = false;
|
||||
bool is_currently_preview = false;
|
||||
bool is_currently_connected = false;
|
||||
|
||||
void set_program() {
|
||||
Serial.println("[Tally] PROGRAM");
|
||||
|
@ -73,23 +67,19 @@ void handleWebSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|||
We can't set the LEDs to "error", because if someone
|
||||
quits OBS, we will get disconnected.
|
||||
*/
|
||||
is_currently_connected = false;
|
||||
break;
|
||||
|
||||
case WStype_CONNECTED:
|
||||
Serial.printf("[WS] connected to %s\n", payload);
|
||||
#ifdef OBS_PASS
|
||||
// Find out if we need authentication
|
||||
webSocket.sendTXT("{\"request-type\":\"GetAuthRequired\",\"message-id\":\"1\"}");
|
||||
#else
|
||||
webSocket.sendTXT("{\"request-type\":\"GetCurrentScene\",\"message-id\":\"3\"}");
|
||||
webSocket.sendTXT("{\"request-type\":\"GetPreviewScene\",\"message-id\":\"4\"}");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case WStype_TEXT: {
|
||||
Serial.printf("[WS] %s\n", payload);
|
||||
|
||||
StaticJsonDocument<5000> doc;
|
||||
StaticJsonDocument<10000> doc;
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
|
||||
if (error) {
|
||||
|
@ -98,8 +88,9 @@ void handleWebSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef OBS_PASS
|
||||
if (doc.containsKey("authRequired")) {
|
||||
if (doc["authRequired"]) {
|
||||
#ifdef OBS_PASS
|
||||
Serial.println("[OBS] auth requested");
|
||||
|
||||
sha256.reset();
|
||||
|
@ -136,21 +127,38 @@ void handleWebSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|||
|
||||
String authRequest = String("{\"request-type\":\"Authenticate\",\"message-id\":\"2\",\"auth\":\"") + encodedAuthString + "\"}";
|
||||
webSocket.sendTXT(authRequest);
|
||||
#else
|
||||
Serial.println("[OBS] auth requested, but not configured!");
|
||||
set_error();
|
||||
delay(10000);
|
||||
ESP.restart();
|
||||
#endif
|
||||
} else {
|
||||
#ifdef OBS_PASS
|
||||
Serial.println("[OBS] auth configured, but not needed");
|
||||
#endif
|
||||
webSocket.sendTXT("{\"request-type\":\"GetCurrentScene\",\"message-id\":\"3\"}");
|
||||
webSocket.sendTXT("{\"request-type\":\"GetPreviewScene\",\"message-id\":\"4\"}");
|
||||
is_currently_connected = true;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef OBS_PASS
|
||||
} else if (doc.containsKey("message-id") && doc["message-id"] == "2") {
|
||||
if (strcmp(doc["status"], "ok") == 0) {
|
||||
Serial.println("[OBS] authentication successful");
|
||||
webSocket.sendTXT("{\"request-type\":\"GetCurrentScene\",\"message-id\":\"3\"}");
|
||||
webSocket.sendTXT("{\"request-type\":\"GetPreviewScene\",\"message-id\":\"4\"}");
|
||||
is_currently_connected = true;
|
||||
} else {
|
||||
Serial.println("[OBS] authentication FAILED");
|
||||
set_error();
|
||||
delay(10000);
|
||||
ESP.restart();
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (doc.containsKey("sources")) {
|
||||
} else if (doc.containsKey("sources")) {
|
||||
bool my_source_in_current_event = false;
|
||||
|
||||
for (uint8_t i = 0; i < doc["sources"].size(); i++) {
|
||||
|
@ -180,6 +188,7 @@ void handleWebSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|||
Serial.println("[OBS] quit");
|
||||
is_currently_preview = false;
|
||||
is_currently_live = false;
|
||||
is_currently_connected = false;
|
||||
} else if (strcmp(doc["update-type"], "StudioModeSwitched") == 0 && !doc["new-state"]) {
|
||||
Serial.println("[OBS] studio mode disabled");
|
||||
is_currently_preview = false;
|
||||
|
@ -210,6 +219,14 @@ void handleWebSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
|||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef STATUS_LED
|
||||
if (is_currently_connected) {
|
||||
digitalWrite(STATUS_LED, LOW);
|
||||
} else {
|
||||
digitalWrite(STATUS_LED, HIGH);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
@ -220,6 +237,10 @@ void setup() {
|
|||
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT);
|
||||
FastLED.setBrightness(LED_BRIGHTNESS);
|
||||
|
||||
#ifdef STATUS_LED
|
||||
pinMode(STATUS_LED, OUTPUT);
|
||||
#endif
|
||||
|
||||
set_error();
|
||||
delay(100);
|
||||
set_idle();
|
||||
|
|
Loading…
Reference in a new issue