inital commit
This commit is contained in:
commit
3b67975ac9
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
wav
|
|
@ -0,0 +1,10 @@
|
|||
# Setup
|
||||
|
||||
Wire the Pi according to the game.js pins, then run
|
||||
|
||||
```
|
||||
apt install nodejs npm
|
||||
npm install
|
||||
./convertMP3.sh
|
||||
node game.js
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
for i in `ls mp3`; do
|
||||
echo Converting $i to wav/`echo $i | sed "s/mp3$/wav/"`
|
||||
test -e wav/`echo $i | sed "s/mp3$/wav/"` || ffmpeg -loglevel quiet -i mp3/$i wav/`echo $i | sed "s/mp3$/wav/"`
|
||||
# && rm wav/`echo $i | sed "s/mp3$/wav/"`
|
||||
done
|
|
@ -0,0 +1,273 @@
|
|||
const { QLog, undefinedOrNull } = require('quanto-commons');
|
||||
const player = require('node-wav-player');
|
||||
const { Telegraf } = require('telegraf')
|
||||
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
|
||||
|
||||
const telegramBotToken = '5472909337:AAGH269wNGa9u99ekJqHqXEHfi0knpV7IFU';
|
||||
const GlobalLog = QLog.scope('Global');
|
||||
const TelLog = QLog.scope('Telegram');
|
||||
|
||||
GlobalLog.enableLogs(['warn']);
|
||||
TelLog.enableLogs(['warn']);
|
||||
|
||||
GlobalLog.headPadding = 30;
|
||||
TelLog.headPadding = 30;
|
||||
|
||||
const bot = new Telegraf(telegramBotToken);
|
||||
|
||||
// debounce der buttons in ms
|
||||
const bounceMS = 400;
|
||||
|
||||
// ist an wenn im kind mode (erw LED is aus)
|
||||
const childLED = 37;
|
||||
// ist and wenn im erwachsenen Mode (kind LED ist aus)
|
||||
const erwLED = 38;
|
||||
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
|
||||
const statusLED = 40;
|
||||
// setze auf HIGH wenn fail (klingel relais)
|
||||
const failSwitch = 35;
|
||||
|
||||
// warte auf spielstart wenn auf HIGH
|
||||
const startButton = 32;
|
||||
// beende spiel wenn auf HIGH
|
||||
// const finishButton = 31;
|
||||
const finishButton = 22;
|
||||
// faile spiel wenn auf HIGH
|
||||
// const failButton = 29;
|
||||
const failButton = 21;
|
||||
// wechsle kind/erwachen Modus + LED
|
||||
const toggleButton = 33;
|
||||
|
||||
// wird je nach modus gesetzt und abgespielt wenn start
|
||||
const backgroundGrown = "northsouth.wav";
|
||||
const backgroundChild = "northsouth.wav";
|
||||
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
|
||||
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
|
||||
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
|
||||
const stresserSound = "sharks.wav";
|
||||
// wird bei fail abgespielt (zonk)
|
||||
const failSound = "zonk.wav";
|
||||
// hurra wird bei finish abgespielt
|
||||
const finishSound = "kidscheering.wav";
|
||||
|
||||
const GROWN=1;
|
||||
const CHILD=2;
|
||||
|
||||
const STATES = {
|
||||
STARTED: 'started',
|
||||
FINISH: 'finshed',
|
||||
FAIL: 'failed'
|
||||
};
|
||||
|
||||
const best = {
|
||||
grown: 25,
|
||||
child: 37
|
||||
}
|
||||
|
||||
const bounce = {};
|
||||
|
||||
var STATE = STATES.FINISH;
|
||||
var MODE = GROWN;
|
||||
var GAME = {};
|
||||
|
||||
var botCTX;
|
||||
|
||||
async function sendBotMsg(msg) {
|
||||
if(botCTX) {
|
||||
botCTX.reply(msg);
|
||||
}
|
||||
}
|
||||
async function sendBotImg(img) {
|
||||
if(botCTX) {
|
||||
botCTX.replyWithPhoto({ source: img});
|
||||
}
|
||||
}
|
||||
|
||||
async function setup() {
|
||||
GlobalLog.start('Running');
|
||||
bot.start((ctx) => {
|
||||
let message = `Bot aktiv und beobachtet. Benutze /stats um die Bestenliste zu sehen`;
|
||||
ctx.reply(message);
|
||||
TelLog.note('Bot started');
|
||||
botCTX=ctx;
|
||||
});
|
||||
bot.command('stats', async (ctx) => {
|
||||
await ctx.replyWithPhoto({ source: '/root/hotwire-js/img/bestgrown.png' });
|
||||
await ctx.reply(`Bester Erwachsener mit nur ${best.grown} sek`);
|
||||
await ctx.replyWithPhoto({ source: '/root/hotwire-js/img/bestchild.png' })
|
||||
await ctx.reply(`Bestes Kind mit nur ${best.child} sek`);
|
||||
});
|
||||
bot.launch();
|
||||
|
||||
GlobalLog.note('Setting up GPIOs');
|
||||
|
||||
if(process.platform === 'darwin') {
|
||||
setTimeout(start, 500);
|
||||
setTimeout(fail, 3500);
|
||||
setTimeout(start, 4000);
|
||||
setTimeout(finish, 9000);
|
||||
setTimeout(fail, 10000);
|
||||
setTimeout(finish, 11000);
|
||||
return;
|
||||
}
|
||||
gpio.setup(failSwitch, gpio.OUT, () => {
|
||||
gpio.output(failSwitch, false);
|
||||
});
|
||||
gpio.setup(statusLED, gpio.OUT, () => {
|
||||
gpio.output(statusLED, true);
|
||||
});
|
||||
gpio.setup(childLED, gpio.OUT, () => {
|
||||
gpio.output(childLED, true);
|
||||
});
|
||||
gpio.setup(erwLED, gpio.OUT, () => {
|
||||
gpio.output(erwLED, false);
|
||||
});
|
||||
|
||||
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
|
||||
gpio.on('change', (channel, value) => {
|
||||
if(bounce[channel] === true) return;
|
||||
GlobalLog.debug('Change',channel, value);
|
||||
bounce[channel] = true;
|
||||
setTimeout(() => { bounce[channel] = false; GlobalLog.debug('Debounced', channel); }, bounceMS);
|
||||
switch(channel) {
|
||||
case startButton:
|
||||
start(value);
|
||||
break;
|
||||
case finishButton:
|
||||
finish(value);
|
||||
break;
|
||||
case failButton:
|
||||
fail(value);
|
||||
break;
|
||||
case toggleButton:
|
||||
toggle(value);
|
||||
break;
|
||||
default:
|
||||
GlobalLog.warn('Alert! Unknown button change',channel);
|
||||
}
|
||||
});
|
||||
GlobalLog.note('Done. Game logic started. Waiting for input');
|
||||
}
|
||||
|
||||
function gameTimer() {
|
||||
GlobalLog.debug('game timer called');
|
||||
GAME.events.forEach((el) => {
|
||||
if(el.start === GAME.runTime) {
|
||||
GlobalLog.debug('Event :', el);
|
||||
playSound(el.sound);
|
||||
}
|
||||
});
|
||||
GAME.runTime++;
|
||||
}
|
||||
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function start(value) {
|
||||
GAME = {
|
||||
events: [],
|
||||
runTime: 0
|
||||
};
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
STATE = STATES.STARTED;
|
||||
// set all params for next game, then start counter and event timer
|
||||
if(MODE === GROWN) {
|
||||
GAME.events.push({ sound: backgroundGrown, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
|
||||
GAME.mode = GROWN;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
if(MODE === CHILD) {
|
||||
GAME.events.push({ sound: backgroundChild, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
|
||||
GAME.mode = CHILD;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
GlobalLog.note('Starting new game', JSON.stringify(GAME, 2, null));
|
||||
GAME.countTimer = setInterval(gameTimer, 1000);
|
||||
sendBotMsg('new game startet');
|
||||
} else {
|
||||
GlobalLog.warn('START not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
async function finish(value) {
|
||||
GlobalLog.debug('Finish');
|
||||
if(STATE === STATES.STARTED) {
|
||||
GlobalLog.note('You made it in', GAME.runTime, 'seconds');
|
||||
playSound(GAME.finish);
|
||||
STATE = STATES.FINISH;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
await sendBotImg('/tmp/img.png');
|
||||
await sendBotMsg(`Gewinner in nur ${GAME.runTime} Sekunden!`);
|
||||
if(MODE === GROWN) {
|
||||
if(GAME.runTime < best.grown) {
|
||||
await sendBotMsg(`Neue Bestzeit bei den Erwachsenen!`);
|
||||
best.grown = GAME.runTime
|
||||
// todo copy best img
|
||||
}
|
||||
} else {
|
||||
if(GAME.runTime < best.child) {
|
||||
await sendBotMsg(`Neue Bestzeit bei den Kindern!`);
|
||||
best.child = GAME.runTime
|
||||
// todo copy best img
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GlobalLog.warn('FINISH not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(value) {
|
||||
if(STATE === STATES.STARTED) {
|
||||
GlobalLog.note('You failed after', GAME.runTime, 'seconds');
|
||||
playSound(GAME.fail);
|
||||
STATE = STATES.FAIL;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
GlobalLog.debug('FAIL not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(value) {
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
GlobalLog.info('Toggle');
|
||||
if(MODE === CHILD) {
|
||||
MODE = GROWN;
|
||||
// set all values for GROWN mode
|
||||
}
|
||||
if(MODE === GROWN) {
|
||||
MODE = CHILD;
|
||||
// set all values for CHILD mode
|
||||
}
|
||||
} else {
|
||||
GlobalLog.debug('Toggle not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(file){
|
||||
GlobalLog.info("Play sound : ", file);
|
||||
if(GAME.playing) {
|
||||
GlobalLog.debug("Stopping old soundfile", GAME.playing);
|
||||
player.stop();
|
||||
}
|
||||
GAME.playing = file;
|
||||
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete GAME.playing })
|
||||
}
|
||||
|
||||
setup();
|
|
@ -0,0 +1,218 @@
|
|||
|
||||
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
|
||||
|
||||
const player = require('node-wav-player');
|
||||
|
||||
// debounce der buttons in ms
|
||||
const bounceMS = 400;
|
||||
|
||||
// ist an wenn im kind mode (erw LED is aus)
|
||||
const childLED = 37;
|
||||
// ist and wenn im erwachsenen Mode (kind LED ist aus)
|
||||
const erwLED = 38;
|
||||
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
|
||||
const statusLED = 40;
|
||||
// setze auf HIGH wenn fail (klingel relais)
|
||||
const failSwitch = 35;
|
||||
|
||||
// warte auf spielstart wenn auf HIGH
|
||||
const startButton = 32;
|
||||
// beende spiel wenn auf HIGH
|
||||
const finishButton = 31;
|
||||
// faile spiel wenn auf HIGH
|
||||
const failButton = 29;
|
||||
// wechsle kind/erwachen Modus + LED
|
||||
const toggleButton = 33;
|
||||
|
||||
// wird je nach modus gesetzt und abgespielt wenn start
|
||||
const backgroundGrown = "jeopardy.wav";
|
||||
const backgroundChild = "northsouth.wav";
|
||||
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
|
||||
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
|
||||
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
|
||||
const stresserSound = "sharks.wav";
|
||||
// wird bei fail abgespielt (zonk)
|
||||
const failSound = "zonk.wav";
|
||||
// hurra wird bei finish abgespielt
|
||||
const finishSound = "kidscheering.wav";
|
||||
|
||||
const GROWN=1;
|
||||
const CHILD=2;
|
||||
|
||||
const STATES = {
|
||||
STARTED: 'started',
|
||||
FINISH: 'finshed',
|
||||
FAIL: 'failed'
|
||||
};
|
||||
|
||||
const bounce = {};
|
||||
|
||||
var STATE = STATES.FINISH;
|
||||
var MODE = GROWN;
|
||||
var GAME = {};
|
||||
|
||||
function setup() {
|
||||
console.log('Setting up GPIOs');
|
||||
|
||||
if(process.platform === 'darwin') {
|
||||
setTimeout(start, 500);
|
||||
setTimeout(fail, 3500);
|
||||
setTimeout(start, 4000);
|
||||
setTimeout(finish, 9000);
|
||||
setTimeout(fail, 10000);
|
||||
setTimeout(finish, 11000);
|
||||
return;
|
||||
}
|
||||
gpio.setup(failSwitch, gpio.OUT, () => {
|
||||
gpio.output(failSwitch, false);
|
||||
});
|
||||
gpio.setup(statusLED, gpio.OUT, () => {
|
||||
gpio.output(statusLED, true);
|
||||
});
|
||||
gpio.setup(childLED, gpio.OUT, () => {
|
||||
gpio.output(childLED, true);
|
||||
});
|
||||
gpio.setup(erwLED, gpio.OUT, () => {
|
||||
gpio.output(erwLED, false);
|
||||
});
|
||||
|
||||
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
|
||||
gpio.on('change', (channel, value) => {
|
||||
if(bounce[channel] === true) return;
|
||||
console.log('Change',channel, value);
|
||||
bounce[channel] = true;
|
||||
setTimeout(() => { bounce[channel] = false; console.log('Debounced', channel); }, bounceMS);
|
||||
switch(channel) {
|
||||
case startButton:
|
||||
start(value);
|
||||
break;
|
||||
case finishButton:
|
||||
finish(value);
|
||||
break;
|
||||
case failButton:
|
||||
fail(value);
|
||||
break;
|
||||
case toggleButton:
|
||||
toggle(value);
|
||||
break;
|
||||
default:
|
||||
console.log('Alert! Unknown button change',channel);
|
||||
}
|
||||
});
|
||||
console.log('Done. Game logic started. Waiting for input');
|
||||
}
|
||||
|
||||
function gameTimer() {
|
||||
console.log('game timer called');
|
||||
GAME.events.forEach((el) => {
|
||||
if(el.start === GAME.runTime) {
|
||||
console.log('Event :', el);
|
||||
playSound(el.sound);
|
||||
}
|
||||
});
|
||||
GAME.runTime++;
|
||||
}
|
||||
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function start(value) {
|
||||
GAME = {
|
||||
events: [],
|
||||
runTime: 0
|
||||
};
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
STATE = STATES.STARTED;
|
||||
};
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
STATE = STATES.STARTED;
|
||||
};
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
STATE = STATES.STARTED;
|
||||
// set all params for next game, then start counter and event timer
|
||||
if(MODE === GROWN) {
|
||||
GAME.events.push({ sound: backgroundGrown, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
|
||||
GAME.mode = GROWN;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
if(MODE === CHILD) {
|
||||
GAME.events.push({ sound: backgroundChild, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
|
||||
GAME.mode = CHILD;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
console.log('Starting new game', JSON.stringify(GAME, 2, null));
|
||||
console.log(GAME);
|
||||
GAME.countTimer = setInterval(gameTimer, 1000);
|
||||
} else {
|
||||
console.log('START not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function finish(value) {
|
||||
console.log('Finish');
|
||||
if(STATE === STATES.STARTED) {
|
||||
console.log('You made it in', GAME.runTime, 'seconds');
|
||||
playSound(GAME.finish);
|
||||
STATE = STATES.FINISH;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
console.log('FINISH not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(value) {
|
||||
console.log('Fail');
|
||||
if(STATE === STATES.STARTED) {
|
||||
console.log('You failed after', GAME.runTime, 'seconds');
|
||||
playSound(GAME.fail);
|
||||
STATE = STATES.FAIL;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
console.log('FAIL not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(value) {
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
console.log('Toggle');
|
||||
if(MODE === CHILD) {
|
||||
MODE = GROWN;
|
||||
// set all values for GROWN mode
|
||||
}
|
||||
if(MODE === GROWN) {
|
||||
MODE = CHILD;
|
||||
// set all values for CHILD mode
|
||||
}
|
||||
} else {
|
||||
console.log('Toggle not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(file){
|
||||
console.log("Play sound : ", file);
|
||||
if(global.GAME.playing) {
|
||||
console.log("Stopping old soundfile", global.GAME.playing);
|
||||
player.stop();
|
||||
}
|
||||
global.GAME.playing = file;
|
||||
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete global.GAME.playing })
|
||||
}
|
||||
|
||||
setup();
|
|
@ -0,0 +1,209 @@
|
|||
|
||||
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
|
||||
|
||||
const player = require('node-wav-player');
|
||||
|
||||
// ist an wenn im kind mode (erw LED is aus)
|
||||
const childLED = 37;
|
||||
// ist and wenn im erwachsenen Mode (kind LED ist aus)
|
||||
const erwLED = 38;
|
||||
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
|
||||
const statusLED = 40;
|
||||
// setze auf HIGH wenn fail (klingel relais)
|
||||
const failSwitch = 35;
|
||||
|
||||
// warte auf spielstart wenn auf HIGH
|
||||
const startButton = 32;
|
||||
// beende spiel wenn auf HIGH
|
||||
const finishButton = 31;
|
||||
// faile spiel wenn auf HIGH
|
||||
const failButton = 29;
|
||||
// wechsle kind/erwachen Modus + LED
|
||||
const toggleButton = 33;
|
||||
|
||||
// wird je nach modus gesetzt und abgespielt wenn start
|
||||
const backgroundGrown = "jeopardy.wav";
|
||||
const backgroundChild = "northsouth.wav";
|
||||
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
|
||||
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
|
||||
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
|
||||
const stresserSound = "sharks.wav";
|
||||
// wird bei fail abgespielt (zonk)
|
||||
const failSound = "zonk.wav";
|
||||
// hurra wird bei finish abgespielt
|
||||
const finishSound = "kidscheering.wav";
|
||||
|
||||
const GROWN=1;
|
||||
const CHILD=2;
|
||||
|
||||
global.STATES = {
|
||||
STARTED: 'started',
|
||||
FINISH: 'finshed',
|
||||
FAIL: 'failed'
|
||||
};
|
||||
|
||||
const bounce = {};
|
||||
|
||||
global.STATE = STATES.FINISH;
|
||||
global.MODE = GROWN;
|
||||
global.GAME = {};
|
||||
|
||||
function setup() {
|
||||
console.log('Setting up GPIOs');
|
||||
|
||||
if(process.platform === 'darwin') {
|
||||
setTimeout(start, 500);
|
||||
setTimeout(fail, 3500);
|
||||
setTimeout(start, 4000);
|
||||
setTimeout(finish, 9000);
|
||||
setTimeout(fail, 10000);
|
||||
setTimeout(finish, 11000);
|
||||
return;
|
||||
}
|
||||
gpio.setup(failSwitch, gpio.OUT, () => {
|
||||
gpio.output(failSwitch, false);
|
||||
});
|
||||
gpio.setup(statusLED, gpio.OUT, () => {
|
||||
gpio.output(statusLED, true);
|
||||
});
|
||||
gpio.setup(childLED, gpio.OUT, () => {
|
||||
gpio.output(childLED, true);
|
||||
});
|
||||
gpio.setup(erwLED, gpio.OUT, () => {
|
||||
gpio.output(erwLED, false);
|
||||
});
|
||||
|
||||
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
|
||||
gpio.on('change', (channel, value) => {
|
||||
if(bounce[value] === true) return;
|
||||
console.log('Change',channel, value);
|
||||
bounce[value] = true;
|
||||
setTimeout(() => { bounce[value] = false; console.log('Debounced', value); }, 1000);
|
||||
switch(channel) {
|
||||
case startButton:
|
||||
start(value);
|
||||
break;
|
||||
case finishButton:
|
||||
finish(value);
|
||||
break;
|
||||
case failButton:
|
||||
fail(value);
|
||||
break;
|
||||
case toggleButton:
|
||||
toggle(value);
|
||||
break;
|
||||
default:
|
||||
console.log('Alert! Unknown button change',channel);
|
||||
}
|
||||
});
|
||||
console.log('Done. Game logic started. Waiting for input');
|
||||
}
|
||||
|
||||
function gameTimer() {
|
||||
console.log('game timer called');
|
||||
global.GAME.events.forEach((el) => {
|
||||
if(el.start === global.GAME.runTime) {
|
||||
console.log('Event :', el);
|
||||
playSound(el.sound);
|
||||
}
|
||||
});
|
||||
global.GAME.runTime++;
|
||||
}
|
||||
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function start(value) {
|
||||
global.GAME = {
|
||||
events: [],
|
||||
runTime: 0
|
||||
};
|
||||
if(global.STATE === global.STATES.FINISH || global.STATE === STATES.FAIL) {
|
||||
global.STATE = global.STATES.STARTED;
|
||||
// set all params for next game, then start counter and event timer
|
||||
if(global.MODE === GROWN) {
|
||||
global.GAME.events.push({ sound: backgroundGrown, start: 0});
|
||||
global.GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
|
||||
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
|
||||
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
|
||||
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
|
||||
global.GAME.mode = GROWN;
|
||||
global.GAME.fail = failSound;
|
||||
global.GAME.finish = finishSound;
|
||||
global.GAME.runtime = 0;
|
||||
}
|
||||
if(global.MODE === CHILD) {
|
||||
global.GAME.events.push({ sound: backgroundChild, start: 0});
|
||||
global.GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
|
||||
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
|
||||
global.GAME.mode = CHILD;
|
||||
global.GAME.fail = failSound;
|
||||
global.GAME.finish = finishSound;
|
||||
global.GAME.runtime = 0;
|
||||
}
|
||||
console.log('Starting new game', JSON.stringify(global.GAME, 2, null));
|
||||
console.log(global.GAME);
|
||||
global.GAME.countTimer = setInterval(gameTimer, 1000);
|
||||
} else {
|
||||
console.log('START not in state', global.STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function finish(value) {
|
||||
console.log('Finish');
|
||||
if(global.STATE === global.STATES.STARTED) {
|
||||
console.log('You made it in', global.GAME.runTime, 'seconds');
|
||||
playSound(global.GAME.finish);
|
||||
global.STATE = global.STATES.FINISH;
|
||||
clearInterval(global.GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
console.log('FINISH not in state', global.STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(value) {
|
||||
console.log('Fail');
|
||||
if(global.STATE === global.STATES.STARTED) {
|
||||
console.log('You failed after', global.GAME.runTime, 'seconds');
|
||||
playSound(global.GAME.fail);
|
||||
global.STATE = global.STATES.FAIL;
|
||||
clearInterval(global.GAME.countTimer)
|
||||
global.GAME.events = [];
|
||||
} else {
|
||||
console.log('FAIL not in state', global.STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(value) {
|
||||
if(global.STATE === global.STATES.FINISH || global.STATE === global.STATES.FAIL) {
|
||||
console.log('Toggle');
|
||||
if(global.MODE === CHILD) {
|
||||
global.MODE = GROWN;
|
||||
// set all values for GROWN mode
|
||||
}
|
||||
if(global.MODE === GROWN) {
|
||||
global.MODE = CHILD;
|
||||
// set all values for CHILD mode
|
||||
}
|
||||
} else {
|
||||
console.log('Toggle not in state', global.STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(file){
|
||||
console.log("Play sound : ", file);
|
||||
if(global.GAME.playing) {
|
||||
console.log("Stopping old soundfile", global.GAME.playing);
|
||||
player.stop();
|
||||
}
|
||||
global.GAME.playing = file;
|
||||
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete global.GAME.playing })
|
||||
}
|
||||
|
||||
setup();
|
|
@ -0,0 +1,209 @@
|
|||
|
||||
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
|
||||
|
||||
const player = require('node-wav-player');
|
||||
|
||||
// ist an wenn im kind mode (erw LED is aus)
|
||||
const childLED = 37;
|
||||
// ist and wenn im erwachsenen Mode (kind LED ist aus)
|
||||
const erwLED = 38;
|
||||
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
|
||||
const statusLED = 40;
|
||||
// setze auf HIGH wenn fail (klingel relais)
|
||||
const failSwitch = 35;
|
||||
|
||||
// warte auf spielstart wenn auf HIGH
|
||||
const startButton = 32;
|
||||
// beende spiel wenn auf HIGH
|
||||
const finishButton = 31;
|
||||
// faile spiel wenn auf HIGH
|
||||
const failButton = 29;
|
||||
// wechsle kind/erwachen Modus + LED
|
||||
const toggleButton = 33;
|
||||
|
||||
// wird je nach modus gesetzt und abgespielt wenn start
|
||||
const backgroundGrown = "jeopardy.wav";
|
||||
const backgroundChild = "northsouth.wav";
|
||||
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
|
||||
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
|
||||
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
|
||||
const stresserSound = "sharks.wav";
|
||||
// wird bei fail abgespielt (zonk)
|
||||
const failSound = "zonk.wav";
|
||||
// hurra wird bei finish abgespielt
|
||||
const finishSound = "kidscheering.wav";
|
||||
|
||||
const GROWN=1;
|
||||
const CHILD=2;
|
||||
|
||||
const STATES = {
|
||||
STARTED: 'started',
|
||||
FINISH: 'finshed',
|
||||
FAIL: 'failed'
|
||||
};
|
||||
|
||||
const bounce = {};
|
||||
|
||||
var STATE = STATES.FINISH;
|
||||
var MODE = GROWN;
|
||||
var GAME = {};
|
||||
|
||||
function setup() {
|
||||
console.log('Setting up GPIOs');
|
||||
|
||||
if(process.platform === 'darwin') {
|
||||
setTimeout(start, 500);
|
||||
setTimeout(fail, 3500);
|
||||
setTimeout(start, 4000);
|
||||
setTimeout(finish, 9000);
|
||||
setTimeout(fail, 10000);
|
||||
setTimeout(finish, 11000);
|
||||
return;
|
||||
}
|
||||
gpio.setup(failSwitch, gpio.OUT, () => {
|
||||
gpio.output(failSwitch, false);
|
||||
});
|
||||
gpio.setup(statusLED, gpio.OUT, () => {
|
||||
gpio.output(statusLED, true);
|
||||
});
|
||||
gpio.setup(childLED, gpio.OUT, () => {
|
||||
gpio.output(childLED, true);
|
||||
});
|
||||
gpio.setup(erwLED, gpio.OUT, () => {
|
||||
gpio.output(erwLED, false);
|
||||
});
|
||||
|
||||
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
|
||||
|
||||
gpio.on('change', (channel, value) => {
|
||||
if(bounce[channel] === true) return;
|
||||
console.log('Change',channel, value);
|
||||
bounce[channel] = true;
|
||||
setTimeout(() => { bounce[channel] = false; console.log('Debounced', channel); }, 500);
|
||||
switch(channel) {
|
||||
case startButton:
|
||||
start(value);
|
||||
break;
|
||||
case finishButton:
|
||||
finish(value);
|
||||
break;
|
||||
case failButton:
|
||||
fail(value);
|
||||
break;
|
||||
case toggleButton:
|
||||
toggle(value);
|
||||
break;
|
||||
default:
|
||||
console.log('Alert! Unknown button change',channel);
|
||||
}
|
||||
});
|
||||
console.log('Done. Game logic started. Waiting for input');
|
||||
}
|
||||
|
||||
function gameTimer() {
|
||||
console.log('game timer called');
|
||||
GAME.events.forEach((el) => {
|
||||
if(el.start === GAME.runTime) {
|
||||
console.log('Event :', el);
|
||||
playSound(el.sound);
|
||||
}
|
||||
});
|
||||
GAME.runTime++;
|
||||
}
|
||||
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function start(value) {
|
||||
GAME = {
|
||||
events: [],
|
||||
runTime: 0
|
||||
};
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
STATE = STATES.STARTED;
|
||||
// set all params for next game, then start counter and event timer
|
||||
if(MODE === GROWN) {
|
||||
GAME.events.push({ sound: backgroundGrown, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
|
||||
GAME.mode = GROWN;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
if(MODE === CHILD) {
|
||||
GAME.events.push({ sound: backgroundChild, start: 0});
|
||||
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
|
||||
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
|
||||
GAME.mode = CHILD;
|
||||
GAME.fail = failSound;
|
||||
GAME.finish = finishSound;
|
||||
GAME.runtime = 0;
|
||||
}
|
||||
console.log('Starting new game', JSON.stringify(GAME, 2, null));
|
||||
console.log(GAME);
|
||||
GAME.countTimer = setInterval(gameTimer, 1000);
|
||||
} else {
|
||||
console.log('START not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function finish(value) {
|
||||
console.log('Finish');
|
||||
if(STATE === STATES.STARTED) {
|
||||
console.log('You made it in', GAME.runTime, 'seconds');
|
||||
playSound(GAME.finish);
|
||||
STATE = STATES.FINISH;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
console.log('FINISH not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(value) {
|
||||
console.log('Fail');
|
||||
if(STATE === STATES.STARTED) {
|
||||
console.log('You failed after', GAME.runTime, 'seconds');
|
||||
playSound(GAME.fail);
|
||||
STATE = STATES.FAIL;
|
||||
clearInterval(GAME.countTimer)
|
||||
GAME.events = [];
|
||||
} else {
|
||||
console.log('FAIL not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function toggle(value) {
|
||||
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
|
||||
console.log('Toggle');
|
||||
if(MODE === CHILD) {
|
||||
MODE = GROWN;
|
||||
// set all values for GROWN mode
|
||||
}
|
||||
if(MODE === GROWN) {
|
||||
MODE = CHILD;
|
||||
// set all values for CHILD mode
|
||||
}
|
||||
} else {
|
||||
console.log('Toggle not in state', STATE);
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(file){
|
||||
console.log("Play sound : ", file);
|
||||
if(GAME.playing) {
|
||||
console.log("Stopping old soundfile", GAME.playing);
|
||||
player.stop();
|
||||
}
|
||||
GAME.playing = file;
|
||||
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete GAME.playing })
|
||||
}
|
||||
|
||||
setup();
|
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
After Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"node-telegram-bot-api": "^0.30.0",
|
||||
"node-wav-player": "^0.2.0",
|
||||
"quanto-commons": "^1.0.46",
|
||||
"rpi-gpio": "^2.1.7",
|
||||
"telegraf": "^4.8.6"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue