Advertisement
cz3dtc

RGB-Keyboard-stream

Mar 26th, 2022
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.59 KB | None | 0 0
  1. #include <FastLED.h>
  2. #include "Keyboard.h"
  3.  
  4. //#define DEBUG
  5.  
  6. #define OBS_PAUSE_SPECIAL
  7. #define TOGGLE_HOLDABLE
  8.  
  9. #ifdef DEBUG
  10. #define BASE_SCANCODE 'a'
  11. #define TOGGLEMOD KEY_LEFT_SHIFT
  12. #else
  13. #define BASE_SCANCODE KEY_F13
  14. #define TOGGLEMOD KEY_LEFT_CTRL
  15. #endif
  16.  
  17. // This is the time in ms between for a toggle key between 'pressing' and 'releasing' the relevant keycode. Should be enough to register the key, but not enough to trigger autorepeat or such
  18. #define REG_DELAY 200
  19.  
  20. // Keys are multiplexed. This is how many columns and rows are there. Decoupling diode anodes (+) go to columns, cathodes (-) to rows.
  21. #define KEY_COL_N 3
  22. #define KEY_ROW_N 4
  23.  
  24. #define KEY_ACTIVE LOW
  25. #define KEY_INACTIVE HIGH
  26.  
  27. #define NO_LED 0xFF
  28. #define NO_SWITCH 0x00
  29.  
  30. //LEDs are a chain of WS2812B-MINIs, first four are signal lights, then all the keys in sequence
  31. #define DATA_PIN 4
  32. #define LED_TYPE WS2812B
  33. #define COLOR_ORDER GRB
  34. #define NUM_LEDS 14
  35. CRGB leds[NUM_LEDS];
  36.  
  37. const CRGB led_onstate[NUM_LEDS] =
  38. { CRGB::Red, CRGB::Yellow, CRGB::Purple, CRGB::Green,
  39. CRGB::Red,
  40. CRGB::Cyan, CRGB::Cyan, CRGB::Yellow,
  41. CRGB::Purple, CRGB::Purple, CRGB::CadetBlue,
  42. CRGB::Red, CRGB::Coral, CRGB::Green};
  43.  
  44. const CRGB led_offstate[NUM_LEDS] =
  45. { CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black,
  46. CRGB::Black,
  47. CRGB::Black, CRGB::Black, CRGB::Black,
  48. CRGB::Black, CRGB::Black, CRGB::Black,
  49. CRGB::Black, CRGB::Black, CRGB::Black };
  50.  
  51. // This many buttons can be connected, not all need to be populated
  52. #define NBUTTONS (KEY_COL_N*KEY_ROW_N)
  53.  
  54. // These are the pins that LED cathodes are connected to:
  55. const uint8_t key_cols[KEY_COL_N] = {2, 14, 8};
  56.  
  57. // These are the pins that LED anodes are connected to:
  58. const uint8_t key_rows[KEY_ROW_N] = {9, 16, 15, 7};
  59.  
  60. // LEDS are counted 0..(NUM_LEDS - 1). How they are physically aranged is a different story, so here's mapping from button number to LED number.
  61. // NO_LED means there's no LED associated (usually because there's no physical key in the matrix, i.e. reference design has keys 0 and 1 missing)
  62. const uint8_t led_map[NBUTTONS] = {NO_LED,NO_LED,4,5,6,7,8,9,10,11,12,13};
  63.  
  64. // There are four more LEDs in my design than I have keys, so for future use I saved their numbers here:
  65. const uint8_t led_A = 0;
  66. const uint8_t led_B = 1;
  67. const uint8_t led_C = 2;
  68. const uint8_t led_D = 3;
  69.  
  70. // For each key in sequence:
  71. // 1 means the key is a toggle, so on first press it will send e.g. F13 and its LED will light up, the following keypress will send CTRL-F13 and extinguish the LED:
  72. // 0 means it's an ordinary key behaving like a normal function key on the keyboard, i.e. pressing it each time will send a single keypress, if you hold it, your system will start autorepeating.
  73. const uint8_t toggles[NBUTTONS] = {NO_SWITCH, NO_SWITCH, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0};
  74.  
  75. // For momentary keys (where toggles[i] is zero) key groups can be defined. If a key has a non-zero group assigned, its LED will stay on after pressing (the key behaves like a normal keyboard key, it's just the LED that will stay on).
  76. // At the same time all LEDs of other keys in the same group will be extinguished. This is useful if you use the keys e.g. to switch scenes in OBS: the last selected scene is the active one, and the LED will tell you which one it was.
  77. // If a key is assigned group 0 it is in no group at all, its LED will light as long as the key is held down and will go out as soon as you release it.
  78. const uint8_t groups[NBUTTONS] = {NO_SWITCH, NO_SWITCH, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
  79.  
  80. //internal tables to save state of the keypad
  81. uint8_t t_state[NBUTTONS]; // toggle states, i. e. whether each toggle key is on or off.
  82. uint8_t key_old_state[NBUTTONS]; // key registered states, i.e. which key was pressed the last time we checked.
  83. uint8_t keys[NBUTTONS]; // key realtime state from matrix scan.
  84.  
  85.  
  86. #define KEY_VirtualCam 2
  87. #define KEY_SCR_1 3
  88. #define KEY_SCR_2 4
  89. #define KEY_REC 5
  90. #define KEY_SCR_1_VID 6
  91. #define KEY_SCR_2_VID 7
  92. #define KEY_PAUSE 8
  93. #define KEY_CAM 9
  94. #define KEY_WHITEBOARD 10
  95. #define KEY_SAFESCREEN 11
  96.  
  97. #define LED_ACTIVE(X) (leds[led_map[X]]==led_onstate[led_map[X]])
  98.  
  99. void updateSpecialLEDs()
  100. {
  101. //first LED (red) warns that a person might be visible in live feed
  102. if (LED_ACTIVE(KEY_SCR_1_VID) || LED_ACTIVE(KEY_SCR_2_VID) || LED_ACTIVE(KEY_CAM) || LED_ACTIVE(KEY_WHITEBOARD))
  103. leds[led_A] = led_onstate[led_A];
  104. else
  105. leds[led_A] = led_offstate[led_A];
  106.  
  107. //second LED (yellow) warns that the screen or whiteboard is shared
  108. if (LED_ACTIVE(KEY_SCR_1) ||LED_ACTIVE(KEY_SCR_2) ||LED_ACTIVE(KEY_SCR_1_VID) ||LED_ACTIVE(KEY_SCR_2_VID) ||LED_ACTIVE(KEY_WHITEBOARD))
  109. leds[led_B] = led_onstate[led_B];
  110. else
  111. leds[led_B] = led_offstate[led_B];
  112.  
  113. //third LED (purple) warns that a stream is either being recorded or is broadcast live
  114. if ((LED_ACTIVE(KEY_REC)&& !LED_ACTIVE(KEY_PAUSE)) || LED_ACTIVE(KEY_VirtualCam))
  115. leds[led_C] = led_onstate[led_C];
  116. else
  117. leds[led_C] = led_offstate[led_C];
  118.  
  119. //fourth LED (green) tells that a "safe screen" is active (i.e. a test pattern or a "wait for picture" screen)
  120. if (LED_ACTIVE(KEY_SAFESCREEN))
  121. leds[led_D] = led_onstate[led_D];
  122. else
  123. leds[led_D] = led_offstate[led_D];
  124. }
  125.  
  126.  
  127. void setup() {
  128. FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS);
  129. FastLED.setBrightness(100);
  130. //.setCorrection(TypicalLEDStrip);
  131. for (uint8_t i=0;i<KEY_COL_N;i++) // setup pins for keys, keypad is multiplexed with diodes to prevent any sort of ghosting
  132. {
  133. pinMode(key_cols[i], INPUT_PULLUP);
  134. }
  135. for (uint8_t i=0;i<KEY_ROW_N;i++) // setup LED matrix control - column outputs
  136. {
  137. pinMode(key_rows[i],OUTPUT);
  138. digitalWrite(key_rows[i],HIGH);
  139. }
  140.  
  141. digitalWrite(key_rows[0],LOW); //prepare the first row for reading
  142.  
  143. for (uint8_t i=0;i<NUM_LEDS;i++) // setup LED matrix control - column outputs
  144. {
  145. leds[i]=led_offstate[i];
  146. }
  147. FastLED.show();
  148.  
  149. // master clock init sequence:
  150. CLKPR = 0x80;
  151. CLKPR = 0b00000000;
  152.  
  153. //sync timers and reset their prescalers
  154. GTCCR = 0b10000011;
  155.  
  156. // timer mode initialization
  157. TCCR1A = 0b00000001;
  158. TCCR1B = 0b00001011;
  159. // 0101 F-PWM, 8-bit TOP=0x00FF Upd.OCR1x@BOT TOV1@:TOP
  160. // 00 Normal port operation, OC1A disconnected.
  161. // 00 Normal port operation, OC1B disconnected.
  162. // 011 0064 clkIO/64 (From prescaler)
  163. // 0 NC off
  164. // 0 count on falling edge
  165. // Timer 1 clock: 250000 Hz, resulting base frequency: 976.5625 Hz, resulting base period: 1.024 ms
  166.  
  167.  
  168. TIMSK1 = (1 << TOIE1);
  169. sei();
  170.  
  171. //release prescaler reset
  172. GTCCR = 0x00;
  173.  
  174. // initialize control over the keyboard:
  175. Keyboard.begin();
  176. #ifdef DEBUG
  177. Serial.begin(9600);
  178. #endif
  179.  
  180. }
  181.  
  182. void loop() {
  183. for (char i=0;i<NBUTTONS;i++) //scan through all keys
  184. {
  185. #ifdef OBS_PAUSE_SPECIAL
  186. if ( (i==KEY_PAUSE) && (t_state[KEY_REC]==0)) continue; //don't evaluate PAUSE key if not recording
  187. #endif
  188.  
  189. if (keys[i]!=key_old_state[i]) // the button has either been pressed, or released, anyway the state changed
  190. {
  191. if (toggles[i])
  192. { // the key is a toggle key, i.e. it should send alternating keycodes on alternating keypresses...
  193. if (key_old_state[i]==KEY_ACTIVE)
  194. { //the key was pressed, but isn't anymore - do nothing except register the fact...
  195. key_old_state[i] = KEY_INACTIVE;
  196.  
  197. #ifdef TOGGLE_HOLDABLE
  198. Keyboard.release(BASE_SCANCODE+i);
  199. if (t_state[i]==0)
  200. {
  201. Keyboard.release(TOGGLEMOD);
  202. }
  203. #endif
  204.  
  205. #ifdef DEBUG
  206. Serial.print("Key release: ");
  207. Serial.println(uint8_t(i));
  208. #endif
  209. }
  210. else
  211. { //the key wasn't pressed, but is now, i.e. we need to toggle the toggle...
  212. key_old_state[i] = KEY_ACTIVE;
  213. #ifdef DEBUG
  214. Serial.print("Key press: ");
  215. Serial.println(uint8_t(i));
  216. #endif
  217. if (t_state[i])
  218. { //the state was on, so now it's off
  219. t_state[i] = 0;
  220. leds[led_map[i]] = led_offstate[led_map[i]]; //turn the key LED off
  221. Keyboard.press(TOGGLEMOD);
  222. Keyboard.press(BASE_SCANCODE+i);
  223. #ifndef TOGGLE_HOLDABLE
  224. delay(REG_DELAY);
  225. Keyboard.release(BASE_SCANCODE+i);
  226. Keyboard.release(TOGGLEMOD);
  227. #endif
  228.  
  229. #ifdef OBS_PAUSE_SPECIAL
  230. //release pause state as stopping recording will reset pause state
  231. if (i==KEY_REC)
  232. {
  233. t_state[KEY_PAUSE]=0;
  234. leds[led_map[KEY_PAUSE]]=led_offstate[led_map[KEY_PAUSE]];
  235. }
  236. #endif
  237. }
  238. else
  239. { //the state was off, so now it's on
  240. t_state[i] = 1;
  241. leds[led_map[i]] = led_onstate[led_map[i]]; //turn the key LED on
  242. Keyboard.press(BASE_SCANCODE+i);
  243. #ifndef TOGGLE_HOLDABLE
  244. delay(REG_DELAY);
  245. Keyboard.release(BASE_SCANCODE+i);
  246. #endif
  247. }
  248. }
  249.  
  250. }
  251. else
  252. { //the key is an ordinary key
  253. if (key_old_state[i]==KEY_ACTIVE)
  254. { //the key was pressed, but isn't anymore
  255. key_old_state[i] = KEY_INACTIVE;
  256. Keyboard.release(BASE_SCANCODE+i);
  257. if (groups[i]==0) leds[led_map[i]] = led_offstate[led_map[i]]; // if the key isn't in any key group, switch its LED off as soon as it's released
  258. #ifdef DEBUG
  259. Serial.print("Key release: ");
  260. Serial.println(uint8_t(i));
  261. #endif
  262. }
  263. else
  264. { //the key wasn't pressed, but is now
  265. key_old_state[i] = KEY_ACTIVE;
  266. Keyboard.press(BASE_SCANCODE+i);
  267. if (groups[i])
  268. { // if the key is in a key group, switch all other group key's LEDs off first
  269. for (uint8_t cancel = 0; cancel<NBUTTONS; cancel++)
  270. if ((groups[cancel] == groups[i]) && (toggles[cancel]==0)) leds[led_map[cancel]] = led_offstate[led_map[cancel]]; //kill all other LEDs in the group, except if they are toggles
  271. }
  272.  
  273. leds[led_map[i]] = led_onstate[led_map[i]]; //then light up the LED of the key that was pressed - this regardless of whether it's in a group or not
  274. #ifdef DEBUG
  275. Serial.print("Key press: ");
  276. Serial.println(uint8_t(i));
  277. #endif
  278. }
  279. }
  280. }
  281. }
  282. updateSpecialLEDs();
  283. FastLED.show();
  284. FastLED.delay(20);
  285. }
  286.  
  287. ISR(TIMER1_OVF_vect)
  288. {
  289. //do the key matrix scanning:
  290. static uint8_t row = 0;
  291. static uint8_t keycounter = 0;
  292.  
  293. //before initializing the interrupt the first row was set active. At the end of each ISR call the row will get set for the next reading, so let's read the keys now:
  294. for (uint8_t col = 0; col<KEY_COL_N; col++) // set up column outputs for the current row
  295. {
  296. keys[keycounter++]=digitalRead(key_cols[col]);
  297. }
  298.  
  299. digitalWrite(key_rows[row],HIGH); // release last active row
  300. row++;
  301. if (row == KEY_ROW_N) //if we scanned all rows already, restart the scan from the beginning
  302. {
  303. row = 0;
  304. keycounter = 0;
  305. }
  306. digitalWrite(key_rows[row],LOW); // activate current row for the next readout (allow inputs to stabilize)
  307. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement