1. /* Control Logitech G29 wheel with intervals and amplitude
  2. # Install
  3. npm init
  4. npm install logitech-g29
  5. # Run
  6. npm start
  7. */
  8. const g = require('logitech-g29')
  9. const readline = require('readline');
  10. const os = require('os')
  11. readline.emitKeypressEvents(process.stdin);
  12. process.stdin.setRawMode(true);
  13. // Initialization
  14. const options = {
  15. autocenter: false,
  16. range: 270
  17. }
  18. const defaults = {
  19. forceFriction: 0 // 0 = no friction, 0.5 = half strength, 1 = full strength
  20. }
  21. let lastFriction = defaults.forceFriction
  22. const min_interval = 50;
  23. const max_interval = 4000;
  24. // Variables
  25. let interval = 500;
  26. let nextInterval = false;
  27. let frq = 25;
  28. let amplitude = 0.1
  29. let nextAmplitude = false;
  30. let drift = 0.02
  31. // Helper function
  32. function mathRound(number) {
  33. return Math.round(number * 100) / 100;
  34. }
  35. // Friction
  36. function setForceFriction(val) {
  37. if (val === 0) {
  38. lastFriction = 0
  39. } else if (val > 0) {
  40. lastFriction += 0.1
  41. } else {
  42. lastFriction -= 0.1
  43. }
  44. lastFriction = mathRound(lastFriction)
  45. if (lastFriction < 0) lastFriction = 0
  46. if (lastFriction > 1) lastFriction = 1
  47. console.log('forceFriction(' + lastFriction + ')')
  48. g.forceFriction(lastFriction)
  49. }
  50. // Amplitude
  51. function setAmplitude(val) {
  52. if (amplitude == val)
  53. return
  54. if (val < 0)
  55. val = 0
  56. else if ((interval > 400) && val > 0.2)
  57. val = 0.2
  58. else if ((interval > 200) && val > 0.3)
  59. val = 0.3
  60. else if (val > 0.4)
  61. val = 0.4
  62. nextAmplitude = mathRound(val)
  63. }
  64. function increaseAmplitude() {
  65. setAmplitude(amplitude + 0.01)
  66. }
  67. function decreaseAmplitude() {
  68. setAmplitude(amplitude - 0.01)
  69. }
  70. // Drift
  71. function setDrift(val) {
  72. if (val < -0.5)
  73. val = -0.5
  74. else if (val > 0.5)
  75. val = 0.5
  76. drift = mathRound(val)
  77. console.log(color.cyan('drift'), drift)
  78. }
  79. function increaseDrift() {
  80. setDrift(drift + 0.01)
  81. }
  82. function decreaseDrift() {
  83. setDrift(drift - 0.01)
  84. }
  85. // Interval
  86. function updateInterval(val) {
  87. if ((val > max_interval) || (val < min_interval)) {
  88. console.log(color.red("WARN: Invalid interval ") + val)
  89. return
  90. }
  91. nextInterval = val
  92. // Sanity check for amplitued with the new interval
  93. setAmplitude(amplitude)
  94. }
  95. function increaseInterval() {
  96. let step = 500
  97. if (interval < 300)
  98. step = 50
  99. else if (interval < 1000)
  100. step = 100
  101. let val = interval + step
  102. if (val > max_interval)
  103. return
  104. updateInterval(val)
  105. }
  106. function decreaseInterval() {
  107. let step = 50
  108. if (interval > 1000)
  109. step = 500
  110. else if (interval > 300)
  111. step = 100
  112. let val = interval - step
  113. if (val < min_interval)
  114. return
  115. updateInterval(val)
  116. }
  117. // Position
  118. let increase = false;
  119. let extreme = false;
  120. let last = 0;
  121. // Wait until end position before changing amplitude and interval
  122. function delayChange(x) {
  123. if (x > last && !increase) {
  124. extreme = true
  125. increase = true
  126. } else if (x < last && increase) {
  127. extreme = true
  128. increase = false
  129. }
  130. if (extreme) {
  131. if (nextAmplitude) {
  132. amplitude = nextAmplitude
  133. nextAmplitude = false
  134. console.log(color.magenta('amplitude'), amplitude)
  135. }
  136. if (nextInterval) {
  137. interval = nextInterval
  138. nextInterval = false
  139. console.log(color.green('interval ') + interval)
  140. }
  141. extreme = false
  142. }
  143. last = x
  144. }
  145. function updatePosition() {
  146. let x = Math.cos(Math.PI * 2 * new Date().getTime() / interval); // Value between 0 and 1
  147. x *= amplitude // Multiplied by amplitude to decrease movement
  148. x += 0.5 // Middle is 0.5 which is no movement
  149. x += drift // Add extra weight towards one side
  150. x = mathRound(x)
  151. //console.log(x)
  152. delayChange(x)
  153. g.forceConstant(x)
  154. }
  155. // Bind buttons
  156. g.connect(options, function(err) {
  157. g.on('wheel-button_plus', function(val) {
  158. if (val == 1)
  159. increaseInterval()
  160. })
  161. g.on('wheel-button_minus', function(val) {
  162. if (val == 1)
  163. decreaseInterval()
  164. })
  165. g.on('wheel-spinner', function(val) {
  166. if (val !== 0) {
  167. setForceFriction(val)
  168. }
  169. })
  170. g.on('wheel-button_spinner', function(val) {
  171. if (val === 1) {
  172. setForceFriction(0)
  173. }
  174. })
  175. process.stdin.on('keypress', (str, key) => {
  176. if (key.ctrl && key.name === 'c') {
  177. g.forceConstant(0.5)
  178. clearInterval(loop)
  179. process.exit();
  180. } else {
  181. // console.log(`You pressed the "${str}" key`);
  182. // console.log();
  183. //console.log(key);
  184. // console.log();
  185. if (key.name == 'up') {
  186. decreaseInterval()
  187. } else if (key.name == 'down') {
  188. increaseInterval()
  189. } else if (key.name == 'left') {
  190. decreaseAmplitude()
  191. } else if (key.name == 'right') {
  192. increaseAmplitude()
  193. } else if (key.name == 'q') {
  194. increaseDrift()
  195. } else if (key.name == 'a') {
  196. decreaseDrift()
  197. } else if (key.name == '1') {
  198. updateInterval(4000)
  199. setAmplitude(0.13)
  200. } else if (key.name == '2') {
  201. updateInterval(2000)
  202. setAmplitude(0.16)
  203. } else if (key.name == '3') {
  204. updateInterval(1000)
  205. setAmplitude(0.18)
  206. } else if (key.name == '4') {
  207. updateInterval(500)
  208. setAmplitude(0.2)
  209. } else if (key.name == '5') {
  210. updateInterval(350)
  211. setAmplitude(0.25)
  212. } else if (key.name == '6') {
  213. updateInterval(250)
  214. setAmplitude(0.3)
  215. } else if (key.name == '7') {
  216. updateInterval(200)
  217. setAmplitude(0.4)
  218. } else if (key.name == 'escape') {
  219. setAmplitude(0.01)
  220. }
  221. }
  222. });
  223. console.log(color.cyan('Wheel ready.'))
  224. console.log()
  225. console.log(color.green('Increase speed with up, decrease with down.'))
  226. console.log(color.magenta('Increase amplitude/movement with right, decrease with left.'))
  227. console.log(color.cyan('Increase drift/weight right with q, left with a.'))
  228. console.log(color.yellow('Number keys are quick settings, 1-7, slow to fast'))
  229. console.log()
  230. console.log(color.grey('Play with forceFriction() by using the Red Spinner. Rotate right for more, left for less, and press the spinner button to reset.'))
  231. console.log()
  232. console.log('Quit with Ctrl + C')
  233. // Start interval that changes wheel position
  234. let loop = setInterval(updatePosition, frq)
  235. })
  236. // Terminal Colors
  237. let color = {}
  238. const colors = {
  239. black: [30, 39],
  240. red: [31, 39],
  241. green: [32, 39],
  242. yellow: [33, 39],
  243. blue: [34, 39],
  244. magenta: [35, 39],
  245. cyan: [36, 39],
  246. white: [37, 39],
  247. gray: [90, 39],
  248. grey: [90, 39],
  249. // bright colors
  250. redBright: [91, 39],
  251. greenBright: [92, 39],
  252. yellowBright: [93, 39],
  253. blueBright: [94, 39],
  254. magentaBright: [95, 39],
  255. cyanBright: [96, 39],
  256. whiteBright: [97, 39]
  257. }
  258. const platform = os.platform()
  259. function showColor(hue, info = '') {
  260. // ` signifies a template literal
  261. return `\u001B[${colors[hue][0]}m` + info + `\u001B[${colors[hue][1]}m`
  262. } // showColor
  263. function setupColors() {
  264. for (let item in colors) {
  265. const hue = item
  266. color[hue] = function (info) {
  267. return showColor(hue, info)
  268. }
  269. }
  270. if (platform === 'win32' || platform === 'win64') {
  271. // use brigher versions of these colors
  272. color.red = color.redBright
  273. color.green = color.greenBright
  274. color.yellow = color.yellowBright
  275. color.blue = color.blueBright
  276. color.magenta = color.magentaBright
  277. color.cyan = color.cyanBright
  278. color.white = color.whiteBright
  279. }
  280. } // setupColors
  281. setupColors()

Control Logitech G29 wheel with intervals and amplitude

Requires node.js