Components and supplies
NPN TO-92 transistor (40V, 0.6A, 0.625W)
Insulated braided copper wire (3m)
USB 3.0 cable (male A > female A, 1,8m)
USB 2.0 cable (EASY male A > male mini-B, 0,5m)
8 Ohms 2 Watt speaker, 36mm
QTR-1RC Reflectance Sensor
Metal film resistor (20 Ohm, axial, 2W, 5%)
Arduino Nano R3
3.5mm angle/straight jack cable (AUX, 1.5 m)
3,5mm Audio Jack Socket
USB 3.0 Type A Connector "G46"
LED (generic)
Metal film resistor (220 Ohm)
Parallax Emic 2 Text-to-Speech Module
Tools and machines
Solder Wire, Lead Free
Helping Hand Tool, with Magnifying Glass
Soldering iron (generic)
Project description
Code
Speak4Me GitHub Repository
The repository contains the full source of the project including the webpage with the customization tool.
Arduino Code
arduino
Required code for the Arduino. In the lines from 72 to 232 you can customize your expressions. If you would like to use a no-code solution to edit your expressions you can use the customization tool on your project website: https://speak4me.github.io/speak4me/profile-customization-tool.html
1//sensor imports 2#include <QTRSensors.h> 3 4// For serial communication with the emic 5#include <SoftwareSerial.h> 6 7//To use timed actions -> protothreading 8#include <arduino-timer.h> 9 10// To save data to program space 11#include <avr/pgmspace.h> 12 13//Create timer to schedule the project parts 14auto timer = timer_create_default(); 15 16//definitions 17#define rxPin 6 // Connect SOUT pin of the Emic 2 module to the RX pin 18#define txPin 7 // Connect SIN pin of the Emic 2 module to the TX pin 19#define keepAlivePin 8 20#define StatusLED 9 21 22 23//CUSTOM_SETTINGS_DECLARATION_START 24#define voiceStyle 1 // Male or female voice 25#define initialVolume 1 // Update Volume Funktion einbinden, Aus dem Customizationtool kommt 1-5 26#define closedDuration 2000 // How long to close the eyes to open the menu 27#define keepAliveOpt true 28#define statusLEDActive true // Learning LED which turns on when a direction is detected // Funktion to be implemented 29#define dirDuration 500 // Duration for which a direction has to be measured to be recognized 30#define dirPause 1000 // Cooldown after dir1 has been recognized 31#define timeOutDuration 4000 // Timelimit after which direction 1 gets rejected 32#define useAutoCalibration true 33//CUSTOM_SETTINGS_DECLARATION_END 34 35 36//variables: settings 37bool muted = false; 38uint8_t volume = initialVolume; 39uint8_t activeProfile = 1; 40bool inMenu = true; 41 42// Margins for eye tracking detection 43uint8_t factorL = 87; 44uint8_t factorR = 87; 45uint8_t factorUp = 90; 46uint8_t factorDown = 83; 47 48//variables: program logic 49//Sensors 50 51QTRSensors qtr; 52uint16_t sensorValue[4]; 53uint16_t neutralSensorValue[4]; 54 55//Emic 56SoftwareSerial emicSerial = SoftwareSerial(rxPin, txPin); 57bool emicReady; 58 59//Input treatment 60int8_t dir1 = -1; //0 1 2 3 for left Up right down; 4 for closed; -1 for null 61int8_t dir2 = -1; //0 1 2 3 for left Up right down; 4 for closed; -1 for null 62uint8_t status = 0; //0: Waiting for recognition; 1: timeout after dir1; 2: waiting for recognition 2 63unsigned long signalTracker; //Variable to track for how long a certain signal has been detected 64unsigned long timeoutTracker; //Variable to track the time when a timeout is reached 65 66 67 68char textdataBuffer[100]; // To buffer PROGMEM readings - Has to be large enough for the largest string in must hold 69 70//Loading Text Data into Progmem 71// Menu 72const char textdata_0[] PROGMEM = "Leave Menu"; 73const char textdata_1[] PROGMEM = "Volume Up"; 74const char textdata_2[] PROGMEM = "Toggle Mute"; // Muted - Unmuted mglich? 75const char textdata_3[] PROGMEM = "Volume Down"; 76const char textdata_4[] PROGMEM = "No"; 77const char textdata_5[] PROGMEM = "Okay"; 78const char textdata_6[] PROGMEM = "Yes"; 79const char textdata_7[] PROGMEM = "Help"; 80const char textdata_8[] PROGMEM = "Profile 1"; 81const char textdata_9[] PROGMEM = "Profile 2"; 82const char textdata_10[] PROGMEM = "Profile 3"; 83const char textdata_11[] PROGMEM = "Profile 4"; 84const char textdata_12[] PROGMEM = "Profile 5"; 85const char textdata_13[] PROGMEM = "Profile 6"; 86const char textdata_14[] PROGMEM = "Profile 7"; 87const char textdata_15[] PROGMEM = "Profile 8"; 88 89// PLATZHALTER Profile 1 - Home 90//CUSTOM_EXPRESSION_DECLARATION_START 91const char textdata_16[] PROGMEM = "I want to wash myself"; 92const char textdata_17[] PROGMEM = "I need to go to the toilet"; 93const char textdata_18[] PROGMEM = "I would like to brush my teeth"; 94const char textdata_19[] PROGMEM = "I would like to change my clothes"; 95const char textdata_20[] PROGMEM = "Okay"; 96const char textdata_21[] PROGMEM = "Yes"; 97const char textdata_22[] PROGMEM = "Help"; 98const char textdata_23[] PROGMEM = "No"; 99const char textdata_24[] PROGMEM = "I want time for myself"; 100const char textdata_25[] PROGMEM = "I want to sleep"; 101const char textdata_26[] PROGMEM = "I need a break"; 102const char textdata_27[] PROGMEM = "I would like to stop"; 103const char textdata_28[] PROGMEM = "It is too hot"; 104const char textdata_29[] PROGMEM = "I am hungry"; 105const char textdata_30[] PROGMEM = "I am thirsty"; 106const char textdata_31[] PROGMEM = "I want sweets"; 107 108// Profile 2 - Friends 109const char textdata_32[] PROGMEM = "I dont like this"; 110const char textdata_33[] PROGMEM = "I am good, thanks"; 111const char textdata_34[] PROGMEM = "I am feeling not so good today"; 112const char textdata_35[] PROGMEM = "I am tired"; 113const char textdata_36[] PROGMEM = "Okay"; 114const char textdata_37[] PROGMEM = "Yes"; 115const char textdata_38[] PROGMEM = "Help"; 116const char textdata_39[] PROGMEM = "No"; 117const char textdata_40[] PROGMEM = "Do you want to hang out?"; 118const char textdata_41[] PROGMEM = "How are you?"; 119const char textdata_42[] PROGMEM = "What are your plans for the day??"; 120const char textdata_43[] PROGMEM = "Want to go for a walk?"; 121const char textdata_44[] PROGMEM = "Buz kz buz kz buz buz kschschsch"; 122const char textdata_45[] PROGMEM = "That is wonderful"; 123const char textdata_46[] PROGMEM = "eeeee"; 124const char textdata_47[] PROGMEM = "Haahaahaa huuuhuuu looooooooooooool"; 125 126// Profile 3 - Caretaker 127const char textdata_48[] PROGMEM = "Tighten my shoe"; 128const char textdata_49[] PROGMEM = "I am freezing, I need warmer clothes"; 129const char textdata_50[] PROGMEM = "I am warm, I need less clothes"; 130const char textdata_51[] PROGMEM = "Change my clothes"; 131const char textdata_52[] PROGMEM = "Okay"; 132const char textdata_53[] PROGMEM = "Yes"; 133const char textdata_54[] PROGMEM = "Help"; 134const char textdata_55[] PROGMEM = "No"; 135const char textdata_56[] PROGMEM = "I want to lay down"; 136const char textdata_57[] PROGMEM = "Turn me"; 137const char textdata_58[] PROGMEM = "Scratch me"; 138const char textdata_59[] PROGMEM = "I want to sit"; 139const char textdata_60[] PROGMEM = "I need a massage"; 140const char textdata_61[] PROGMEM = "I need a medical treatment"; 141const char textdata_62[] PROGMEM = "I am in pain"; 142const char textdata_63[] PROGMEM = "Something is wrong"; 143 144// Profile 4 - Doctor visit 145const char textdata_64[] PROGMEM = "I have a problem"; 146const char textdata_65[] PROGMEM = "I dont feel well today"; 147const char textdata_66[] PROGMEM = "I feel good today"; 148const char textdata_67[] PROGMEM = "I am in pain"; 149const char textdata_68[] PROGMEM = "Okay"; 150const char textdata_69[] PROGMEM = "Yes"; 151const char textdata_70[] PROGMEM = "Help"; 152const char textdata_71[] PROGMEM = "No"; 153const char textdata_72[] PROGMEM = "Ask me which part of the body is affected"; 154const char textdata_73[] PROGMEM = "Something is wrong"; 155const char textdata_74[] PROGMEM = "I need medication"; 156const char textdata_75[] PROGMEM = "I need treatment"; 157const char textdata_76[] PROGMEM = "Left"; 158const char textdata_77[] PROGMEM = "Higher"; 159const char textdata_78[] PROGMEM = "Right"; 160const char textdata_79[] PROGMEM = "Lower"; 161 162// Profile 5 - Chess 163const char textdata_80[] PROGMEM = "8"; 164const char textdata_81[] PROGMEM = "5"; 165const char textdata_82[] PROGMEM = "6"; 166const char textdata_83[] PROGMEM = "7"; 167const char textdata_84[] PROGMEM = "4"; 168const char textdata_85[] PROGMEM = "1"; 169const char textdata_86[] PROGMEM = "2"; 170const char textdata_87[] PROGMEM = "3"; 171const char textdata_88[] PROGMEM = "D"; 172const char textdata_89[] PROGMEM = "A"; 173const char textdata_90[] PROGMEM = "B"; 174const char textdata_91[] PROGMEM = "C"; 175const char textdata_92[] PROGMEM = "H"; 176const char textdata_93[] PROGMEM = "E"; 177const char textdata_94[] PROGMEM = "F"; 178const char textdata_95[] PROGMEM = "G"; 179 180// profile 6 - to be customized 181const char textdata_96[] PROGMEM = "Please use the customizer to create your own profile"; 182const char textdata_97[] PROGMEM = "Please use the customizer to create your own profile"; 183const char textdata_98[] PROGMEM = "Please use the customizer to create your own profile"; 184const char textdata_99[] PROGMEM = "Please use the customizer to create your own profile"; 185const char textdata_100[] PROGMEM = "Please use the customizer to create your own profile"; 186const char textdata_101[] PROGMEM = "Please use the customizer to create your own profile"; 187const char textdata_102[] PROGMEM = "Please use the customizer to create your own profile"; 188const char textdata_103[] PROGMEM = "Please use the customizer to create your own profile"; 189const char textdata_104[] PROGMEM = "Please use the customizer to create your own profile"; 190const char textdata_105[] PROGMEM = "Please use the customizer to create your own profile"; 191const char textdata_106[] PROGMEM = "Please use the customizer to create your own profile"; 192const char textdata_107[] PROGMEM = "Please use the customizer to create your own profile"; 193const char textdata_108[] PROGMEM = "Please use the customizer to create your own profile"; 194const char textdata_109[] PROGMEM = "Please use the customizer to create your own profile"; 195const char textdata_110[] PROGMEM = "Please use the customizer to create your own profile"; 196const char textdata_111[] PROGMEM = "Please use the customizer to create your own profile"; 197 198// profile 7 - to be customized 199const char textdata_112[] PROGMEM = "Please use the customizer to create your own profile"; 200const char textdata_113[] PROGMEM = "Please use the customizer to create your own profile"; 201const char textdata_114[] PROGMEM = "Please use the customizer to create your own profile"; 202const char textdata_115[] PROGMEM = "Please use the customizer to create your own profile"; 203const char textdata_116[] PROGMEM = "Please use the customizer to create your own profile"; 204const char textdata_117[] PROGMEM = "Please use the customizer to create your own profile"; 205const char textdata_118[] PROGMEM = "Please use the customizer to create your own profile"; 206const char textdata_119[] PROGMEM = "Please use the customizer to create your own profile"; 207const char textdata_120[] PROGMEM = "Please use the customizer to create your own profile"; 208const char textdata_121[] PROGMEM = "Please use the customizer to create your own profile"; 209const char textdata_122[] PROGMEM = "Please use the customizer to create your own profile"; 210const char textdata_123[] PROGMEM = "Please use the customizer to create your own profile"; 211const char textdata_124[] PROGMEM = "Please use the customizer to create your own profile"; 212const char textdata_125[] PROGMEM = "Please use the customizer to create your own profile"; 213const char textdata_126[] PROGMEM = "Please use the customizer to create your own profile"; 214const char textdata_127[] PROGMEM = "Please use the customizer to create your own profile"; 215 216// profile 8 - to be customized 217const char textdata_128[] PROGMEM = "Please use the customizer to create your own profile"; 218const char textdata_129[] PROGMEM = "Please use the customizer to create your own profile"; 219const char textdata_130[] PROGMEM = "Please use the customizer to create your own profile"; 220const char textdata_131[] PROGMEM = "Please use the customizer to create your own profile"; 221const char textdata_132[] PROGMEM = "Please use the customizer to create your own profile"; 222const char textdata_133[] PROGMEM = "Please use the customizer to create your own profile"; 223const char textdata_134[] PROGMEM = "Please use the customizer to create your own profile"; 224const char textdata_135[] PROGMEM = "Please use the customizer to create your own profile"; 225const char textdata_136[] PROGMEM = "Please use the customizer to create your own profile"; 226const char textdata_137[] PROGMEM = "Please use the customizer to create your own profile"; 227const char textdata_138[] PROGMEM = "Please use the customizer to create your own profile"; 228const char textdata_139[] PROGMEM = "Please use the customizer to create your own profile"; 229const char textdata_140[] PROGMEM = "Please use the customizer to create your own profile"; 230const char textdata_141[] PROGMEM = "Please use the customizer to create your own profile"; 231const char textdata_142[] PROGMEM = "Please use the customizer to create your own profile"; 232const char textdata_143[] PROGMEM = "Please use the customizer to create your own profile"; 233//CUSTOM_EXPRESSION_DECLARATION_END 234 235 236const char *const data_table[] PROGMEM = {textdata_0, textdata_1, textdata_2, textdata_3, textdata_4, textdata_5, textdata_6, textdata_7, textdata_8, textdata_9, textdata_10, 237 textdata_11, textdata_12, textdata_13, textdata_14, textdata_15, textdata_16, textdata_17, textdata_18, textdata_19, textdata_20, 238 textdata_21, textdata_22, textdata_23, textdata_24, textdata_25, textdata_26, textdata_27, textdata_28, textdata_29, textdata_30, 239 textdata_31, textdata_32, textdata_33, textdata_34, textdata_35, textdata_36, textdata_37, textdata_38, textdata_39, textdata_40, 240 textdata_41, textdata_42, textdata_43, textdata_44, textdata_45, textdata_46, textdata_47, textdata_48, textdata_49, textdata_50, 241 textdata_51, textdata_52, textdata_53, textdata_54, textdata_55, textdata_56, textdata_57, textdata_58, textdata_59, textdata_60, 242 textdata_61, textdata_62, textdata_63, textdata_64, textdata_65, textdata_66, textdata_67, textdata_68, textdata_69, textdata_70, 243 textdata_71, textdata_72, textdata_73, textdata_74, textdata_75, textdata_76, textdata_77, textdata_78, textdata_79, textdata_80, 244 textdata_81, textdata_82, textdata_83, textdata_84, textdata_85, textdata_86, textdata_87, textdata_88, textdata_89, textdata_90, 245 textdata_91, textdata_92, textdata_93, textdata_94, textdata_95, textdata_96, textdata_97, textdata_98, textdata_99, textdata_100, 246 textdata_101, textdata_102, textdata_103, textdata_104, textdata_105, textdata_106, textdata_107, textdata_108, textdata_109, textdata_110, 247 textdata_111, textdata_112, textdata_113, textdata_114, textdata_115, textdata_116, textdata_117, textdata_118, textdata_119, textdata_120, 248 textdata_121, textdata_122, textdata_123, textdata_124, textdata_125, textdata_126, textdata_127, textdata_128, textdata_129, textdata_130, 249 textdata_131, textdata_132, textdata_133, textdata_134, textdata_135, textdata_136, textdata_137, textdata_138, textdata_139, textdata_140, 250 textdata_141, textdata_142, textdata_143 251 }; //has to include all 144 strings in the end 252 253 254void setup() 255{ 256 pinMode(keepAlivePin, OUTPUT);//LED for debug purposes at current stage of the project 257 pinMode(StatusLED, OUTPUT);//LED for debug purposes at current stage of the project 258 259 // set the data rate for the hardware serial port 260 Serial.begin(9600); 261 262 // configure the sensors 263 qtr.setTypeRC(); 264 qtr.setSensorPins((const uint8_t[]) { 265 2, 3, 4, 5 266 }, 4); 267 268 qtr.setTimeout(5000); 269 270 // set the data rate for the SoftwareSerial port 271 pinMode(rxPin, INPUT); 272 pinMode(txPin, OUTPUT); 273 emicSerial.begin(9600); 274 emicSerial.print('\ 275'); // Send a CR in case the system is already up 276 while (emicSerial.read() != ':'); // When the Emic 2 has initialized and is ready, it will send a single ':' character, so wait here until we receive it 277 emicSerial.flush(); // Flush the receive buffer 278 279 // set volume of emic 280 emicSerial.print('V'); 281 emicSerial.print(String(volume)); 282 emicSerial.print('\ 283'); 284 285 // set voicestyle of emic 286 emicSerial.print('N'); 287 switch (voiceStyle) { 288 case 1: emicSerial.print('0'); break; 289 case 2: emicSerial.print('8'); break; 290 } 291 emicSerial.print('\ 292'); 293 294 emicReady = true; 295 296 if (useAutoCalibration) { 297 autocalibrateSensors(); 298 } else { 299 calibrateSensors(); 300 setupTimers(); 301 } 302} 303 304void setupTimers() { 305 // Setting up the timers 306 timer.every(100, readSensors); 307 timer.every(100, updateEmicReady); 308 timer.every(3000, printDebugInfo); 309 if (keepAlive) { 310 timer.every(5000, keepAlive); 311 } 312} 313 314void calibrateSensors() { 315 qtr.read(neutralSensorValue); 316 neutralSensorValue[0] = uint32_t(neutralSensorValue[0]) * factorL / 100; 317 neutralSensorValue[1] = uint32_t(neutralSensorValue[1]) * factorUp / 100; 318 neutralSensorValue[2] = uint32_t(neutralSensorValue[2]) * factorL / 100; 319 neutralSensorValue[3] = uint32_t(neutralSensorValue[3]) * factorDown / 100; 320 Serial.println(neutralSensorValue[0]); 321 Serial.println(neutralSensorValue[1]); 322 Serial.println(neutralSensorValue[2]); 323 Serial.println(neutralSensorValue[3]); 324} 325 326void autocalibrateSensors() { 327 qtr.read(neutralSensorValue); 328 329 letEmicSpeak("Left"); 330 while (emicSerial.read() != ':'); 331 emicReady = true; 332 delay(2000); 333 autocalibrateSensor(2); 334 335 letEmicSpeak("Up"); 336 while (emicSerial.read() != ':'); 337 emicReady = true; 338 delay(2000); 339 autocalibrateSensor(3); 340 341 letEmicSpeak("Right"); 342 while (emicSerial.read() != ':'); 343 emicReady = true; 344 delay(2000); 345 autocalibrateSensor(0); 346 347 letEmicSpeak("Down"); 348 while (emicSerial.read() != ':'); 349 emicReady = true; 350 delay(2000); 351 autocalibrateSensor(1); 352 353 letEmicSpeak("Calibration completed"); 354 while (emicSerial.read() != ':'); 355 emicReady = true; 356 setupTimers(); 357} 358 359void autocalibrateSensor(uint8_t sensornumber) { 360 qtr.read(sensorValue); 361 neutralSensorValue[sensornumber] = (4 * sensorValue[sensornumber] + neutralSensorValue[sensornumber]) / 5; 362} 363 364void setFactor(uint8_t sensornumber) { 365 366} 367bool updateEmicReady() { 368 if (emicSerial.read() == ':') { 369 emicReady = true; 370 } 371} 372 373void letEmicSpeak(char message[]) { 374 if (emicReady) { //Sollten wir bei jeder Sprachausgabe prfen, um dem emic erst eine neue Ausgabe zu senden, wenn er bereit ist 375 emicReady = false; 376 emicSerial.print('S'); 377 emicSerial.print(message); // Send the desired string to convert to speech 378 emicSerial.print('\ 379'); 380 } 381} 382 383void toggleMute() { 384 muted = !muted; 385} 386 387void bufferTextdata(uint8_t entrynumber) { 388 strcpy_P(textdataBuffer, (char *)pgm_read_word(&(data_table[entrynumber]))); 389} 390 391// Gets the detected directions as an input and performs the suitable actions depending on active profile, muted, etc. 392void performCommand(int8_t dir1, int8_t dir2) { 393 394 int8_t combinedDirs = dir1 * 10 + dir2; //combine two dirs into one variable 395 396 if (muted) { 397 if (combinedDirs == 2) { 398 toggleMute(); 399 letEmicSpeak("Unmuted"); 400 } 401 } else { 402 403 if (inMenu) { //menu commands 404 switch (combinedDirs) { 405 406 case 0: // leave menu 407 inMenu = false; 408 letEmicSpeak("Closing Menu"); 409 break; 410 411 case 1: // Volume Up 412 raiseVolume(); 413 break; 414 415 case 2: // Toggle Mute 416 toggleMute(); 417 letEmicSpeak("Muted"); 418 break; 419 420 case 3: // Volume Down 421 lowerVolume(); 422 break; 423 424 case 10: 425 bufferTextdata(4); 426 letEmicSpeak(textdataBuffer); 427 break; 428 429 case 11: 430 bufferTextdata(5); 431 letEmicSpeak(textdataBuffer); 432 break; 433 434 case 12: 435 bufferTextdata(6); 436 letEmicSpeak(textdataBuffer); 437 break; 438 439 case 13: 440 bufferTextdata(7); 441 letEmicSpeak(textdataBuffer); 442 break; 443 444 case 20: 445 changeProfile(4); 446 break; 447 448 case 21: 449 changeProfile(1); 450 break; 451 452 case 22: 453 changeProfile(2); 454 break; 455 456 case 23: 457 changeProfile(3); 458 break; 459 460 case 30: // down 461 changeProfile(8); 462 break; 463 464 case 31: 465 changeProfile(5); 466 break; 467 468 case 32: 469 changeProfile(6); 470 break; 471 472 case 33: 473 changeProfile(7); 474 break; 475 } 476 477 } else { //profile commands 478 switch (combinedDirs) { 479 480 case 0: 481 bufferTextdata(16 * activeProfile); 482 letEmicSpeak(textdataBuffer); 483 break; 484 485 case 1: 486 bufferTextdata(16 * activeProfile + 1); 487 letEmicSpeak(textdataBuffer); 488 break; 489 490 case 2: 491 bufferTextdata(16 * activeProfile + 2); 492 letEmicSpeak(textdataBuffer); 493 break; 494 495 case 3: 496 bufferTextdata(16 * activeProfile + 3); 497 letEmicSpeak(textdataBuffer); 498 break; 499 500 case 10: 501 bufferTextdata(16 * activeProfile + 4); 502 letEmicSpeak(textdataBuffer); 503 break; 504 505 case 11: 506 bufferTextdata(16 * activeProfile + 5); 507 letEmicSpeak(textdataBuffer); 508 break; 509 510 case 12: 511 bufferTextdata(16 * activeProfile + 6); 512 letEmicSpeak(textdataBuffer); 513 break; 514 515 case 13: 516 bufferTextdata(16 * activeProfile + 7); 517 letEmicSpeak(textdataBuffer); 518 break; 519 520 case 20: 521 bufferTextdata(16 * activeProfile + 8); 522 letEmicSpeak(textdataBuffer); 523 break; 524 525 case 21: 526 bufferTextdata(16 * activeProfile + 9); 527 letEmicSpeak(textdataBuffer); 528 break; 529 530 case 22: 531 bufferTextdata(16 * activeProfile + 10); 532 letEmicSpeak(textdataBuffer); 533 break; 534 535 case 23: 536 bufferTextdata(16 * activeProfile + 11); 537 letEmicSpeak(textdataBuffer); 538 break; 539 540 case 30: 541 bufferTextdata(16 * activeProfile + 12); 542 letEmicSpeak(textdataBuffer); 543 break; 544 545 case 31: 546 bufferTextdata(16 * activeProfile + 13); 547 letEmicSpeak(textdataBuffer); 548 break; 549 550 case 32: 551 bufferTextdata(16 * activeProfile + 14); 552 letEmicSpeak(textdataBuffer); 553 break; 554 555 case 33: 556 bufferTextdata(16 * activeProfile + 15); 557 letEmicSpeak(textdataBuffer); 558 break; 559 560 case 40: 561 inMenu = true; 562 letEmicSpeak("Menu"); 563 break; 564 } 565 } 566 } 567} 568 569// processes a recognized (or no recognized) eye detection direction and fires the according events depending on status, singalTracker and timeoutTracker 570void processDirection(int8_t recognizedDir) { 571 if (status == 0) { 572 573 if (recognizedDir == -1) { 574 dir1 = -1; 575 576 } else if (recognizedDir != dir1) { 577 dir1 = recognizedDir; 578 signalTracker = millis(); 579 580 } else if (dir1 == 4) { 581 if (millis() - signalTracker > closedDuration) { 582 dir1 = -1; 583 performCommand(4, 0); 584 } 585 } 586 587 else if (millis() - signalTracker > dirDuration) { 588 status = 1; 589 timeoutTracker = millis(); 590 if (!muted) { 591 letStatusLEDBlink(); 592 } 593 } 594 595 } else if (status == 1) { 596 597 if (millis() - timeoutTracker > dirPause) { 598 status = 2; 599 timeoutTracker = millis(); 600 processDirection(recognizedDir); 601 } 602 603 } else if (status == 2) { 604 605 if (millis() - timeoutTracker > timeOutDuration) { 606 status = 0; 607 dir1 = -1; 608 dir2 = -1; 609 processDirection(recognizedDir); 610 611 } else if (recognizedDir == -1) { 612 dir2 = -1; 613 614 } else if (recognizedDir != dir2) { 615 dir2 = recognizedDir; 616 signalTracker = millis(); 617 618 } else if (millis() - signalTracker > dirDuration) { 619 performCommand(dir1, dir2); 620 status = 0; 621 dir1 = -1; 622 dir2 = -1; 623 timeoutTracker = millis(); 624 } 625 } 626} 627 628// uses the hardware to read a set of sensor values and assumes in which direction the user was looking 629bool readSensors() { 630 qtr.read(sensorValue); 631 632 if (sensorValue[0] < neutralSensorValue[0] //if eyes closed 633 && sensorValue[2] < neutralSensorValue[2] 634 && sensorValue[1] < neutralSensorValue[1] 635 && sensorValue[3] < neutralSensorValue[3]) { 636 Serial.println("ReadSensors: Closed eyes"); 637 processDirection(4); 638 } else if (sensorValue[3] < neutralSensorValue[3]) { //if looking up 639 Serial.println("ReadSensors: U "); 640 processDirection(1); 641 } else if (sensorValue[1] < neutralSensorValue[1]) { //if looking down 642 Serial.println("ReadSensors: D "); 643 processDirection(3); 644 } else if (sensorValue[0] < neutralSensorValue[0]) { //if looking right 645 Serial.println("ReadSensors: R "); 646 processDirection(2); 647 } else if (sensorValue[2] < neutralSensorValue[2]) { //if looking left 648 Serial.println("ReadSensors: L "); 649 processDirection(0); 650 } else { 651 Serial.println("ReadSensors: X "); 652 processDirection(-1); 653 } 654 655 return true; 656} 657 658void changeProfile(uint8_t profilenumber) { 659 activeProfile = profilenumber; 660 inMenu = false; 661 bufferTextdata(7 + profilenumber); 662 letEmicSpeak(textdataBuffer); 663} 664 665void raiseVolume() { 666 if (volume < 5) { 667 updateVolume(volume + 1); 668 bufferTextdata(1); 669 timer.in(150, [] { letEmicSpeak(textdataBuffer); }); 670 } else { 671 letEmicSpeak("Maximal volume reached"); 672 } 673} 674 675void lowerVolume() { 676 if (volume > 1) { 677 updateVolume(volume - 1); 678 bufferTextdata(3); 679 timer.in(150, [] { letEmicSpeak(textdataBuffer); }); 680 } else { 681 letEmicSpeak("Minimal volume reached"); 682 } 683} 684 685void updateVolume(int8_t newVolume) { 686 if (emicReady) { //Should be tested at every emic output to be sure its ready to receive commands 687 emicReady = false; 688 volume = newVolume; 689 emicSerial.print('V'); 690 switch (volume) { 691 case 1: emicSerial.print("-40"); break; 692 case 2: emicSerial.print("-20"); break; 693 case 3: emicSerial.print("0"); break; 694 case 4: emicSerial.print("10"); break; 695 case 5: emicSerial.print("18"); break; 696 } 697 emicSerial.print('\ 698');; 699 } 700} 701 702void letStatusLEDBlink() { 703 if (statusLEDActive) { 704 digitalWrite(StatusLED, HIGH); 705 timer.in(200, [] { digitalWrite(StatusLED, LOW); }); 706 } 707} 708 709bool keepAlive() { //uses the keep alive wiring to consume some power so that powerbanks do not turn off 710 digitalWrite(keepAlivePin, HIGH); 711 timer.in(300, [] { digitalWrite(keepAlivePin, LOW); }); 712 return true; 713} 714 715bool printDebugInfo() { 716 Serial.print(sensorValue[0]); 717 Serial.print('\ '); 718 Serial.print(sensorValue[1]); 719 Serial.print('\ '); 720 Serial.print(sensorValue[2]); 721 Serial.print('\ '); 722 Serial.print(sensorValue[3]); 723 Serial.print('\ '); 724 Serial.println(); 725 726 return true; 727} 728 729void loop() 730{ 731 timer.tick(); 732} 733
Speak4Me GitHub Repository
The repository contains the full source of the project including the webpage with the customization tool.
Arduino Code
arduino
Required code for the Arduino. In the lines from 72 to 232 you can customize your expressions. If you would like to use a no-code solution to edit your expressions you can use the customization tool on your project website: https://speak4me.github.io/speak4me/profile-customization-tool.html
1//sensor imports 2#include <QTRSensors.h> 3 4// For serial communication with the emic 5#include <SoftwareSerial.h> 6 7//To use timed actions -> protothreading 8#include <arduino-timer.h> 9 10// To save data to program space 11#include <avr/pgmspace.h> 12 13//Create timer to schedule the project parts 14auto timer = timer_create_default(); 15 16//definitions 17#define rxPin 6 // Connect SOUT pin of the Emic 2 module to the RX pin 18#define txPin 7 // Connect SIN pin of the Emic 2 module to the TX pin 19#define keepAlivePin 8 20#define StatusLED 9 21 22 23//CUSTOM_SETTINGS_DECLARATION_START 24#define voiceStyle 1 // Male or female voice 25#define initialVolume 1 // Update Volume Funktion einbinden, Aus dem Customizationtool kommt 1-5 26#define closedDuration 2000 // How long to close the eyes to open the menu 27#define keepAliveOpt true 28#define statusLEDActive true // Learning LED which turns on when a direction is detected // Funktion to be implemented 29#define dirDuration 500 // Duration for which a direction has to be measured to be recognized 30#define dirPause 1000 // Cooldown after dir1 has been recognized 31#define timeOutDuration 4000 // Timelimit after which direction 1 gets rejected 32#define useAutoCalibration true 33//CUSTOM_SETTINGS_DECLARATION_END 34 35 36//variables: settings 37bool muted = false; 38uint8_t volume = initialVolume; 39uint8_t activeProfile = 1; 40bool inMenu = true; 41 42// Margins for eye tracking detection 43uint8_t factorL = 87; 44uint8_t factorR = 87; 45uint8_t factorUp = 90; 46uint8_t factorDown = 83; 47 48//variables: program logic 49//Sensors 50 51QTRSensors qtr; 52uint16_t sensorValue[4]; 53uint16_t neutralSensorValue[4]; 54 55//Emic 56SoftwareSerial emicSerial = SoftwareSerial(rxPin, txPin); 57bool emicReady; 58 59//Input treatment 60int8_t dir1 = -1; //0 1 2 3 for left Up right down; 4 for closed; -1 for null 61int8_t dir2 = -1; //0 1 2 3 for left Up right down; 4 for closed; -1 for null 62uint8_t status = 0; //0: Waiting for recognition; 1: timeout after dir1; 2: waiting for recognition 2 63unsigned long signalTracker; //Variable to track for how long a certain signal has been detected 64unsigned long timeoutTracker; //Variable to track the time when a timeout is reached 65 66 67 68char textdataBuffer[100]; // To buffer PROGMEM readings - Has to be large enough for the largest string in must hold 69 70//Loading Text Data into Progmem 71// Menu 72const char textdata_0[] PROGMEM = "Leave Menu"; 73const char textdata_1[] PROGMEM = "Volume Up"; 74const char textdata_2[] PROGMEM = "Toggle Mute"; // Muted - Unmuted mglich? 75const char textdata_3[] PROGMEM = "Volume Down"; 76const char textdata_4[] PROGMEM = "No"; 77const char textdata_5[] PROGMEM = "Okay"; 78const char textdata_6[] PROGMEM = "Yes"; 79const char textdata_7[] PROGMEM = "Help"; 80const char textdata_8[] PROGMEM = "Profile 1"; 81const char textdata_9[] PROGMEM = "Profile 2"; 82const char textdata_10[] PROGMEM = "Profile 3"; 83const char textdata_11[] PROGMEM = "Profile 4"; 84const char textdata_12[] PROGMEM = "Profile 5"; 85const char textdata_13[] PROGMEM = "Profile 6"; 86const char textdata_14[] PROGMEM = "Profile 7"; 87const char textdata_15[] PROGMEM = "Profile 8"; 88 89// PLATZHALTER Profile 1 - Home 90//CUSTOM_EXPRESSION_DECLARATION_START 91const char textdata_16[] PROGMEM = "I want to wash myself"; 92const char textdata_17[] PROGMEM = "I need to go to the toilet"; 93const char textdata_18[] PROGMEM = "I would like to brush my teeth"; 94const char textdata_19[] PROGMEM = "I would like to change my clothes"; 95const char textdata_20[] PROGMEM = "Okay"; 96const char textdata_21[] PROGMEM = "Yes"; 97const char textdata_22[] PROGMEM = "Help"; 98const char textdata_23[] PROGMEM = "No"; 99const char textdata_24[] PROGMEM = "I want time for myself"; 100const char textdata_25[] PROGMEM = "I want to sleep"; 101const char textdata_26[] PROGMEM = "I need a break"; 102const char textdata_27[] PROGMEM = "I would like to stop"; 103const char textdata_28[] PROGMEM = "It is too hot"; 104const char textdata_29[] PROGMEM = "I am hungry"; 105const char textdata_30[] PROGMEM = "I am thirsty"; 106const char textdata_31[] PROGMEM = "I want sweets"; 107 108// Profile 2 - Friends 109const char textdata_32[] PROGMEM = "I dont like this"; 110const char textdata_33[] PROGMEM = "I am good, thanks"; 111const char textdata_34[] PROGMEM = "I am feeling not so good today"; 112const char textdata_35[] PROGMEM = "I am tired"; 113const char textdata_36[] PROGMEM = "Okay"; 114const char textdata_37[] PROGMEM = "Yes"; 115const char textdata_38[] PROGMEM = "Help"; 116const char textdata_39[] PROGMEM = "No"; 117const char textdata_40[] PROGMEM = "Do you want to hang out?"; 118const char textdata_41[] PROGMEM = "How are you?"; 119const char textdata_42[] PROGMEM = "What are your plans for the day??"; 120const char textdata_43[] PROGMEM = "Want to go for a walk?"; 121const char textdata_44[] PROGMEM = "Buz kz buz kz buz buz kschschsch"; 122const char textdata_45[] PROGMEM = "That is wonderful"; 123const char textdata_46[] PROGMEM = "eeeee"; 124const char textdata_47[] PROGMEM = "Haahaahaa huuuhuuu looooooooooooool"; 125 126// Profile 3 - Caretaker 127const char textdata_48[] PROGMEM = "Tighten my shoe"; 128const char textdata_49[] PROGMEM = "I am freezing, I need warmer clothes"; 129const char textdata_50[] PROGMEM = "I am warm, I need less clothes"; 130const char textdata_51[] PROGMEM = "Change my clothes"; 131const char textdata_52[] PROGMEM = "Okay"; 132const char textdata_53[] PROGMEM = "Yes"; 133const char textdata_54[] PROGMEM = "Help"; 134const char textdata_55[] PROGMEM = "No"; 135const char textdata_56[] PROGMEM = "I want to lay down"; 136const char textdata_57[] PROGMEM = "Turn me"; 137const char textdata_58[] PROGMEM = "Scratch me"; 138const char textdata_59[] PROGMEM = "I want to sit"; 139const char textdata_60[] PROGMEM = "I need a massage"; 140const char textdata_61[] PROGMEM = "I need a medical treatment"; 141const char textdata_62[] PROGMEM = "I am in pain"; 142const char textdata_63[] PROGMEM = "Something is wrong"; 143 144// Profile 4 - Doctor visit 145const char textdata_64[] PROGMEM = "I have a problem"; 146const char textdata_65[] PROGMEM = "I dont feel well today"; 147const char textdata_66[] PROGMEM = "I feel good today"; 148const char textdata_67[] PROGMEM = "I am in pain"; 149const char textdata_68[] PROGMEM = "Okay"; 150const char textdata_69[] PROGMEM = "Yes"; 151const char textdata_70[] PROGMEM = "Help"; 152const char textdata_71[] PROGMEM = "No"; 153const char textdata_72[] PROGMEM = "Ask me which part of the body is affected"; 154const char textdata_73[] PROGMEM = "Something is wrong"; 155const char textdata_74[] PROGMEM = "I need medication"; 156const char textdata_75[] PROGMEM = "I need treatment"; 157const char textdata_76[] PROGMEM = "Left"; 158const char textdata_77[] PROGMEM = "Higher"; 159const char textdata_78[] PROGMEM = "Right"; 160const char textdata_79[] PROGMEM = "Lower"; 161 162// Profile 5 - Chess 163const char textdata_80[] PROGMEM = "8"; 164const char textdata_81[] PROGMEM = "5"; 165const char textdata_82[] PROGMEM = "6"; 166const char textdata_83[] PROGMEM = "7"; 167const char textdata_84[] PROGMEM = "4"; 168const char textdata_85[] PROGMEM = "1"; 169const char textdata_86[] PROGMEM = "2"; 170const char textdata_87[] PROGMEM = "3"; 171const char textdata_88[] PROGMEM = "D"; 172const char textdata_89[] PROGMEM = "A"; 173const char textdata_90[] PROGMEM = "B"; 174const char textdata_91[] PROGMEM = "C"; 175const char textdata_92[] PROGMEM = "H"; 176const char textdata_93[] PROGMEM = "E"; 177const char textdata_94[] PROGMEM = "F"; 178const char textdata_95[] PROGMEM = "G"; 179 180// profile 6 - to be customized 181const char textdata_96[] PROGMEM = "Please use the customizer to create your own profile"; 182const char textdata_97[] PROGMEM = "Please use the customizer to create your own profile"; 183const char textdata_98[] PROGMEM = "Please use the customizer to create your own profile"; 184const char textdata_99[] PROGMEM = "Please use the customizer to create your own profile"; 185const char textdata_100[] PROGMEM = "Please use the customizer to create your own profile"; 186const char textdata_101[] PROGMEM = "Please use the customizer to create your own profile"; 187const char textdata_102[] PROGMEM = "Please use the customizer to create your own profile"; 188const char textdata_103[] PROGMEM = "Please use the customizer to create your own profile"; 189const char textdata_104[] PROGMEM = "Please use the customizer to create your own profile"; 190const char textdata_105[] PROGMEM = "Please use the customizer to create your own profile"; 191const char textdata_106[] PROGMEM = "Please use the customizer to create your own profile"; 192const char textdata_107[] PROGMEM = "Please use the customizer to create your own profile"; 193const char textdata_108[] PROGMEM = "Please use the customizer to create your own profile"; 194const char textdata_109[] PROGMEM = "Please use the customizer to create your own profile"; 195const char textdata_110[] PROGMEM = "Please use the customizer to create your own profile"; 196const char textdata_111[] PROGMEM = "Please use the customizer to create your own profile"; 197 198// profile 7 - to be customized 199const char textdata_112[] PROGMEM = "Please use the customizer to create your own profile"; 200const char textdata_113[] PROGMEM = "Please use the customizer to create your own profile"; 201const char textdata_114[] PROGMEM = "Please use the customizer to create your own profile"; 202const char textdata_115[] PROGMEM = "Please use the customizer to create your own profile"; 203const char textdata_116[] PROGMEM = "Please use the customizer to create your own profile"; 204const char textdata_117[] PROGMEM = "Please use the customizer to create your own profile"; 205const char textdata_118[] PROGMEM = "Please use the customizer to create your own profile"; 206const char textdata_119[] PROGMEM = "Please use the customizer to create your own profile"; 207const char textdata_120[] PROGMEM = "Please use the customizer to create your own profile"; 208const char textdata_121[] PROGMEM = "Please use the customizer to create your own profile"; 209const char textdata_122[] PROGMEM = "Please use the customizer to create your own profile"; 210const char textdata_123[] PROGMEM = "Please use the customizer to create your own profile"; 211const char textdata_124[] PROGMEM = "Please use the customizer to create your own profile"; 212const char textdata_125[] PROGMEM = "Please use the customizer to create your own profile"; 213const char textdata_126[] PROGMEM = "Please use the customizer to create your own profile"; 214const char textdata_127[] PROGMEM = "Please use the customizer to create your own profile"; 215 216// profile 8 - to be customized 217const char textdata_128[] PROGMEM = "Please use the customizer to create your own profile"; 218const char textdata_129[] PROGMEM = "Please use the customizer to create your own profile"; 219const char textdata_130[] PROGMEM = "Please use the customizer to create your own profile"; 220const char textdata_131[] PROGMEM = "Please use the customizer to create your own profile"; 221const char textdata_132[] PROGMEM = "Please use the customizer to create your own profile"; 222const char textdata_133[] PROGMEM = "Please use the customizer to create your own profile"; 223const char textdata_134[] PROGMEM = "Please use the customizer to create your own profile"; 224const char textdata_135[] PROGMEM = "Please use the customizer to create your own profile"; 225const char textdata_136[] PROGMEM = "Please use the customizer to create your own profile"; 226const char textdata_137[] PROGMEM = "Please use the customizer to create your own profile"; 227const char textdata_138[] PROGMEM = "Please use the customizer to create your own profile"; 228const char textdata_139[] PROGMEM = "Please use the customizer to create your own profile"; 229const char textdata_140[] PROGMEM = "Please use the customizer to create your own profile"; 230const char textdata_141[] PROGMEM = "Please use the customizer to create your own profile"; 231const char textdata_142[] PROGMEM = "Please use the customizer to create your own profile"; 232const char textdata_143[] PROGMEM = "Please use the customizer to create your own profile"; 233//CUSTOM_EXPRESSION_DECLARATION_END 234 235 236const char *const data_table[] PROGMEM = {textdata_0, textdata_1, textdata_2, textdata_3, textdata_4, textdata_5, textdata_6, textdata_7, textdata_8, textdata_9, textdata_10, 237 textdata_11, textdata_12, textdata_13, textdata_14, textdata_15, textdata_16, textdata_17, textdata_18, textdata_19, textdata_20, 238 textdata_21, textdata_22, textdata_23, textdata_24, textdata_25, textdata_26, textdata_27, textdata_28, textdata_29, textdata_30, 239 textdata_31, textdata_32, textdata_33, textdata_34, textdata_35, textdata_36, textdata_37, textdata_38, textdata_39, textdata_40, 240 textdata_41, textdata_42, textdata_43, textdata_44, textdata_45, textdata_46, textdata_47, textdata_48, textdata_49, textdata_50, 241 textdata_51, textdata_52, textdata_53, textdata_54, textdata_55, textdata_56, textdata_57, textdata_58, textdata_59, textdata_60, 242 textdata_61, textdata_62, textdata_63, textdata_64, textdata_65, textdata_66, textdata_67, textdata_68, textdata_69, textdata_70, 243 textdata_71, textdata_72, textdata_73, textdata_74, textdata_75, textdata_76, textdata_77, textdata_78, textdata_79, textdata_80, 244 textdata_81, textdata_82, textdata_83, textdata_84, textdata_85, textdata_86, textdata_87, textdata_88, textdata_89, textdata_90, 245 textdata_91, textdata_92, textdata_93, textdata_94, textdata_95, textdata_96, textdata_97, textdata_98, textdata_99, textdata_100, 246 textdata_101, textdata_102, textdata_103, textdata_104, textdata_105, textdata_106, textdata_107, textdata_108, textdata_109, textdata_110, 247 textdata_111, textdata_112, textdata_113, textdata_114, textdata_115, textdata_116, textdata_117, textdata_118, textdata_119, textdata_120, 248 textdata_121, textdata_122, textdata_123, textdata_124, textdata_125, textdata_126, textdata_127, textdata_128, textdata_129, textdata_130, 249 textdata_131, textdata_132, textdata_133, textdata_134, textdata_135, textdata_136, textdata_137, textdata_138, textdata_139, textdata_140, 250 textdata_141, textdata_142, textdata_143 251 }; //has to include all 144 strings in the end 252 253 254void setup() 255{ 256 pinMode(keepAlivePin, OUTPUT);//LED for debug purposes at current stage of the project 257 pinMode(StatusLED, OUTPUT);//LED for debug purposes at current stage of the project 258 259 // set the data rate for the hardware serial port 260 Serial.begin(9600); 261 262 // configure the sensors 263 qtr.setTypeRC(); 264 qtr.setSensorPins((const uint8_t[]) { 265 2, 3, 4, 5 266 }, 4); 267 268 qtr.setTimeout(5000); 269 270 // set the data rate for the SoftwareSerial port 271 pinMode(rxPin, INPUT); 272 pinMode(txPin, OUTPUT); 273 emicSerial.begin(9600); 274 emicSerial.print('\n'); // Send a CR in case the system is already up 275 while (emicSerial.read() != ':'); // When the Emic 2 has initialized and is ready, it will send a single ':' character, so wait here until we receive it 276 emicSerial.flush(); // Flush the receive buffer 277 278 // set volume of emic 279 emicSerial.print('V'); 280 emicSerial.print(String(volume)); 281 emicSerial.print('\n'); 282 283 // set voicestyle of emic 284 emicSerial.print('N'); 285 switch (voiceStyle) { 286 case 1: emicSerial.print('0'); break; 287 case 2: emicSerial.print('8'); break; 288 } 289 emicSerial.print('\n'); 290 291 emicReady = true; 292 293 if (useAutoCalibration) { 294 autocalibrateSensors(); 295 } else { 296 calibrateSensors(); 297 setupTimers(); 298 } 299} 300 301void setupTimers() { 302 // Setting up the timers 303 timer.every(100, readSensors); 304 timer.every(100, updateEmicReady); 305 timer.every(3000, printDebugInfo); 306 if (keepAlive) { 307 timer.every(5000, keepAlive); 308 } 309} 310 311void calibrateSensors() { 312 qtr.read(neutralSensorValue); 313 neutralSensorValue[0] = uint32_t(neutralSensorValue[0]) * factorL / 100; 314 neutralSensorValue[1] = uint32_t(neutralSensorValue[1]) * factorUp / 100; 315 neutralSensorValue[2] = uint32_t(neutralSensorValue[2]) * factorL / 100; 316 neutralSensorValue[3] = uint32_t(neutralSensorValue[3]) * factorDown / 100; 317 Serial.println(neutralSensorValue[0]); 318 Serial.println(neutralSensorValue[1]); 319 Serial.println(neutralSensorValue[2]); 320 Serial.println(neutralSensorValue[3]); 321} 322 323void autocalibrateSensors() { 324 qtr.read(neutralSensorValue); 325 326 letEmicSpeak("Left"); 327 while (emicSerial.read() != ':'); 328 emicReady = true; 329 delay(2000); 330 autocalibrateSensor(2); 331 332 letEmicSpeak("Up"); 333 while (emicSerial.read() != ':'); 334 emicReady = true; 335 delay(2000); 336 autocalibrateSensor(3); 337 338 letEmicSpeak("Right"); 339 while (emicSerial.read() != ':'); 340 emicReady = true; 341 delay(2000); 342 autocalibrateSensor(0); 343 344 letEmicSpeak("Down"); 345 while (emicSerial.read() != ':'); 346 emicReady = true; 347 delay(2000); 348 autocalibrateSensor(1); 349 350 letEmicSpeak("Calibration completed"); 351 while (emicSerial.read() != ':'); 352 emicReady = true; 353 setupTimers(); 354} 355 356void autocalibrateSensor(uint8_t sensornumber) { 357 qtr.read(sensorValue); 358 neutralSensorValue[sensornumber] = (4 * sensorValue[sensornumber] + neutralSensorValue[sensornumber]) / 5; 359} 360 361void setFactor(uint8_t sensornumber) { 362 363} 364bool updateEmicReady() { 365 if (emicSerial.read() == ':') { 366 emicReady = true; 367 } 368} 369 370void letEmicSpeak(char message[]) { 371 if (emicReady) { //Sollten wir bei jeder Sprachausgabe prfen, um dem emic erst eine neue Ausgabe zu senden, wenn er bereit ist 372 emicReady = false; 373 emicSerial.print('S'); 374 emicSerial.print(message); // Send the desired string to convert to speech 375 emicSerial.print('\n'); 376 } 377} 378 379void toggleMute() { 380 muted = !muted; 381} 382 383void bufferTextdata(uint8_t entrynumber) { 384 strcpy_P(textdataBuffer, (char *)pgm_read_word(&(data_table[entrynumber]))); 385} 386 387// Gets the detected directions as an input and performs the suitable actions depending on active profile, muted, etc. 388void performCommand(int8_t dir1, int8_t dir2) { 389 390 int8_t combinedDirs = dir1 * 10 + dir2; //combine two dirs into one variable 391 392 if (muted) { 393 if (combinedDirs == 2) { 394 toggleMute(); 395 letEmicSpeak("Unmuted"); 396 } 397 } else { 398 399 if (inMenu) { //menu commands 400 switch (combinedDirs) { 401 402 case 0: // leave menu 403 inMenu = false; 404 letEmicSpeak("Closing Menu"); 405 break; 406 407 case 1: // Volume Up 408 raiseVolume(); 409 break; 410 411 case 2: // Toggle Mute 412 toggleMute(); 413 letEmicSpeak("Muted"); 414 break; 415 416 case 3: // Volume Down 417 lowerVolume(); 418 break; 419 420 case 10: 421 bufferTextdata(4); 422 letEmicSpeak(textdataBuffer); 423 break; 424 425 case 11: 426 bufferTextdata(5); 427 letEmicSpeak(textdataBuffer); 428 break; 429 430 case 12: 431 bufferTextdata(6); 432 letEmicSpeak(textdataBuffer); 433 break; 434 435 case 13: 436 bufferTextdata(7); 437 letEmicSpeak(textdataBuffer); 438 break; 439 440 case 20: 441 changeProfile(4); 442 break; 443 444 case 21: 445 changeProfile(1); 446 break; 447 448 case 22: 449 changeProfile(2); 450 break; 451 452 case 23: 453 changeProfile(3); 454 break; 455 456 case 30: // down 457 changeProfile(8); 458 break; 459 460 case 31: 461 changeProfile(5); 462 break; 463 464 case 32: 465 changeProfile(6); 466 break; 467 468 case 33: 469 changeProfile(7); 470 break; 471 } 472 473 } else { //profile commands 474 switch (combinedDirs) { 475 476 case 0: 477 bufferTextdata(16 * activeProfile); 478 letEmicSpeak(textdataBuffer); 479 break; 480 481 case 1: 482 bufferTextdata(16 * activeProfile + 1); 483 letEmicSpeak(textdataBuffer); 484 break; 485 486 case 2: 487 bufferTextdata(16 * activeProfile + 2); 488 letEmicSpeak(textdataBuffer); 489 break; 490 491 case 3: 492 bufferTextdata(16 * activeProfile + 3); 493 letEmicSpeak(textdataBuffer); 494 break; 495 496 case 10: 497 bufferTextdata(16 * activeProfile + 4); 498 letEmicSpeak(textdataBuffer); 499 break; 500 501 case 11: 502 bufferTextdata(16 * activeProfile + 5); 503 letEmicSpeak(textdataBuffer); 504 break; 505 506 case 12: 507 bufferTextdata(16 * activeProfile + 6); 508 letEmicSpeak(textdataBuffer); 509 break; 510 511 case 13: 512 bufferTextdata(16 * activeProfile + 7); 513 letEmicSpeak(textdataBuffer); 514 break; 515 516 case 20: 517 bufferTextdata(16 * activeProfile + 8); 518 letEmicSpeak(textdataBuffer); 519 break; 520 521 case 21: 522 bufferTextdata(16 * activeProfile + 9); 523 letEmicSpeak(textdataBuffer); 524 break; 525 526 case 22: 527 bufferTextdata(16 * activeProfile + 10); 528 letEmicSpeak(textdataBuffer); 529 break; 530 531 case 23: 532 bufferTextdata(16 * activeProfile + 11); 533 letEmicSpeak(textdataBuffer); 534 break; 535 536 case 30: 537 bufferTextdata(16 * activeProfile + 12); 538 letEmicSpeak(textdataBuffer); 539 break; 540 541 case 31: 542 bufferTextdata(16 * activeProfile + 13); 543 letEmicSpeak(textdataBuffer); 544 break; 545 546 case 32: 547 bufferTextdata(16 * activeProfile + 14); 548 letEmicSpeak(textdataBuffer); 549 break; 550 551 case 33: 552 bufferTextdata(16 * activeProfile + 15); 553 letEmicSpeak(textdataBuffer); 554 break; 555 556 case 40: 557 inMenu = true; 558 letEmicSpeak("Menu"); 559 break; 560 } 561 } 562 } 563} 564 565// processes a recognized (or no recognized) eye detection direction and fires the according events depending on status, singalTracker and timeoutTracker 566void processDirection(int8_t recognizedDir) { 567 if (status == 0) { 568 569 if (recognizedDir == -1) { 570 dir1 = -1; 571 572 } else if (recognizedDir != dir1) { 573 dir1 = recognizedDir; 574 signalTracker = millis(); 575 576 } else if (dir1 == 4) { 577 if (millis() - signalTracker > closedDuration) { 578 dir1 = -1; 579 performCommand(4, 0); 580 } 581 } 582 583 else if (millis() - signalTracker > dirDuration) { 584 status = 1; 585 timeoutTracker = millis(); 586 if (!muted) { 587 letStatusLEDBlink(); 588 } 589 } 590 591 } else if (status == 1) { 592 593 if (millis() - timeoutTracker > dirPause) { 594 status = 2; 595 timeoutTracker = millis(); 596 processDirection(recognizedDir); 597 } 598 599 } else if (status == 2) { 600 601 if (millis() - timeoutTracker > timeOutDuration) { 602 status = 0; 603 dir1 = -1; 604 dir2 = -1; 605 processDirection(recognizedDir); 606 607 } else if (recognizedDir == -1) { 608 dir2 = -1; 609 610 } else if (recognizedDir != dir2) { 611 dir2 = recognizedDir; 612 signalTracker = millis(); 613 614 } else if (millis() - signalTracker > dirDuration) { 615 performCommand(dir1, dir2); 616 status = 0; 617 dir1 = -1; 618 dir2 = -1; 619 timeoutTracker = millis(); 620 } 621 } 622} 623 624// uses the hardware to read a set of sensor values and assumes in which direction the user was looking 625bool readSensors() { 626 qtr.read(sensorValue); 627 628 if (sensorValue[0] < neutralSensorValue[0] //if eyes closed 629 && sensorValue[2] < neutralSensorValue[2] 630 && sensorValue[1] < neutralSensorValue[1] 631 && sensorValue[3] < neutralSensorValue[3]) { 632 Serial.println("ReadSensors: Closed eyes"); 633 processDirection(4); 634 } else if (sensorValue[3] < neutralSensorValue[3]) { //if looking up 635 Serial.println("ReadSensors: U "); 636 processDirection(1); 637 } else if (sensorValue[1] < neutralSensorValue[1]) { //if looking down 638 Serial.println("ReadSensors: D "); 639 processDirection(3); 640 } else if (sensorValue[0] < neutralSensorValue[0]) { //if looking right 641 Serial.println("ReadSensors: R "); 642 processDirection(2); 643 } else if (sensorValue[2] < neutralSensorValue[2]) { //if looking left 644 Serial.println("ReadSensors: L "); 645 processDirection(0); 646 } else { 647 Serial.println("ReadSensors: X "); 648 processDirection(-1); 649 } 650 651 return true; 652} 653 654void changeProfile(uint8_t profilenumber) { 655 activeProfile = profilenumber; 656 inMenu = false; 657 bufferTextdata(7 + profilenumber); 658 letEmicSpeak(textdataBuffer); 659} 660 661void raiseVolume() { 662 if (volume < 5) { 663 updateVolume(volume + 1); 664 bufferTextdata(1); 665 timer.in(150, [] { letEmicSpeak(textdataBuffer); }); 666 } else { 667 letEmicSpeak("Maximal volume reached"); 668 } 669} 670 671void lowerVolume() { 672 if (volume > 1) { 673 updateVolume(volume - 1); 674 bufferTextdata(3); 675 timer.in(150, [] { letEmicSpeak(textdataBuffer); }); 676 } else { 677 letEmicSpeak("Minimal volume reached"); 678 } 679} 680 681void updateVolume(int8_t newVolume) { 682 if (emicReady) { //Should be tested at every emic output to be sure its ready to receive commands 683 emicReady = false; 684 volume = newVolume; 685 emicSerial.print('V'); 686 switch (volume) { 687 case 1: emicSerial.print("-40"); break; 688 case 2: emicSerial.print("-20"); break; 689 case 3: emicSerial.print("0"); break; 690 case 4: emicSerial.print("10"); break; 691 case 5: emicSerial.print("18"); break; 692 } 693 emicSerial.print('\n');; 694 } 695} 696 697void letStatusLEDBlink() { 698 if (statusLEDActive) { 699 digitalWrite(StatusLED, HIGH); 700 timer.in(200, [] { digitalWrite(StatusLED, LOW); }); 701 } 702} 703 704bool keepAlive() { //uses the keep alive wiring to consume some power so that powerbanks do not turn off 705 digitalWrite(keepAlivePin, HIGH); 706 timer.in(300, [] { digitalWrite(keepAlivePin, LOW); }); 707 return true; 708} 709 710bool printDebugInfo() { 711 Serial.print(sensorValue[0]); 712 Serial.print('\ '); 713 Serial.print(sensorValue[1]); 714 Serial.print('\ '); 715 Serial.print(sensorValue[2]); 716 Serial.print('\ '); 717 Serial.print(sensorValue[3]); 718 Serial.print('\ '); 719 Serial.println(); 720 721 return true; 722} 723 724void loop() 725{ 726 timer.tick(); 727} 728
Downloadable files
Architecture overview
Architecture overview
Documentation
3D printing files .stl for the case and retainer of sensors
Includes required files to print the case and the retainer of the sensors.
https://github.com/speak4me/speak4me/blob/main/docs/assets/3d-printing-stl-files.zip
Circuit Board gerber and drill files
Includes all files that are required to order a professional circuit board for the main device.
https://github.com/speak4me/speak4me/blob/main/docs/assets/Gerber.zip
3D printing files .stl for the case and retainer of sensors
Includes required files to print the case and the retainer of the sensors.
https://github.com/speak4me/speak4me/blob/main/docs/assets/3d-printing-stl-files.zip
Circuit Board gerber and drill files
Includes all files that are required to order a professional circuit board for the main device.
https://github.com/speak4me/speak4me/blob/main/docs/assets/Gerber.zip
Comments
Only logged in users can leave comments