Pedro Martin
Published © LGPL

Bluetooth RPM Letterboard v2.0

Second iteration of a Rapid Prompting Method (RPM) letterboard with capacitive touch sensors & Bluetooth connectivity

AdvancedFull instructions provided24 hours1,100
Bluetooth RPM Letterboard v2.0

Things used in this project

Hardware components

Adafruit HUZZAH32 – ESP32 Feather Board
Adafruit HUZZAH32 – ESP32 Feather Board
×1
Capacitive Touch Sensor Breakout - MPR121
Adafruit Capacitive Touch Sensor Breakout - MPR121
×3
Adafruit Small Enclosed Piezo w/Wires
×1
Adafruit Lithium Ion Polymer Battery - 3.7v 350mAh
×1
Adafruit On-Off Power Button / Pushbutton Toggle Switch
×1
Adafruit STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long
×3
Flexible 28AWG Tinned Copper Silicone Stranded Wire
×1
Copper Foil Tape with Conductive Acrylic Adhesive
×1
M2.5 Nylon Hex Spacer Standoffs Screws Nuts Assortment Kit
×1

Software apps and online services

Arduino IDE
Arduino IDE
Espressif Arduino ESP32
T-vK's ESP32 BLE Keyboard

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Dupont Crimping Tool Kit
Precision Wire Stripper, 30-20 AWG
Hot Glue Gorilla Dual Temp
LED Lighted Magnifying Third Hand - Five Arms
Digital Multimeter

Story

Read more

Schematics

Circuit Diagram

Code

RPM Letterboard Code

Arduino
//---Definitions for Capacitive Touch Sensors
#include <Wire.h>
#include <Adafruit_MPR121.h>
#ifndef _BV
#define _BV(bit) (1 << (bit))
#endif
Adafruit_MPR121 TS1 = Adafruit_MPR121();
Adafruit_MPR121 TS2 = Adafruit_MPR121();
Adafruit_MPR121 TS3 = Adafruit_MPR121();
uint16_t lastT1, currT1 = 0;
uint16_t lastT2, currT2 = 0;
uint16_t lastT3, currT3 = 0;
int capT, senT = 0;  //--capT=> Touch Sensor Modules (0,1,2), senT=> Individual sensors(0 to 11)
bool CapsResetFlag = false;

//---Definitions for BLE Keyboard
#define USE_NIMBLE
#include <BleKeyboard.h>
BleKeyboard bleKeyboard("RPM Letterboard V2.0");
bool keybON, action = false;
char L[12][3];       //--adding one for null char in char array


//---Other Definitions
int counter = 0;

void loadKeyboardMap() {
   L[2][0] = 'a'; L[3][0] = 'b';  L[2][2] = 'c'; L[3][2] = 'd';  L[2][1] = 'e'; L[3][1] = '1';
   L[1][0] = 'f'; L[4][0] = 'g';  L[1][2] = 'h'; L[4][2] = 'i';  L[1][1] = 'j'; L[4][1] = '2';
   L[0][0] = 'k'; L[5][0] = 'l';  L[0][2] = 'm'; L[5][2] = 'n';  L[0][1] = ';'; L[5][1] = '3';
  L[11][0] = 'o'; L[6][0] = 'p'; L[11][2] = 'q'; L[6][2] = 'r'; L[11][1] = 's'; L[6][1] = '4';
  L[10][0] = 't'; L[7][0] = 'u'; L[10][2] = 'v'; L[7][2] = 'w'; L[10][1] = 'x'; L[7][1] = '5';
   L[9][0] = 'y'; L[8][0] = 'z';         L[8][2] = 32;           L[9][1] = 8;   L[8][1] = 10;
}

void Buzz(int BuzzType) {
  switch (BuzzType) {
    case 1: ledcWriteTone(0,1000); delay(100); ledcWrite(0,0);
            break; //--TS1 is up or scheduled TS reset run
    case 2: ledcWriteTone(0,1500); delay(100); ledcWrite(0,0); 
            break; //--TS2 is up
    case 3: ledcWriteTone(0,2000); delay(100); ledcWrite(0,0); 
            break; //--TS3 is up 
    case 4: ledcWriteTone(0,1100); delay(10); ledcWrite(0,0); delay(1000); digitalWrite(A12, HIGH);
            ledcWriteTone(0,400); delay(10); ledcWrite(0,0); delay(1000); break; //--waiting on BLE pair
    case 5: ledcWriteTone(0,900); delay(300); ledcWriteTone(0,400);
            delay(700); ledcWrite(0,0); break; //--BLE pairing achieved
    case 6: digitalWrite(A12,HIGH); ledcWriteTone(0,100); delay(2000);
            ledcWrite(0,0); break; //--fatal error, must reboot manually
    case 7: ledcWriteTone(0,100); delay(300); ledcWriteTone(0,50);
            delay(700); ledcWrite(0,0); break; //--error, autorestarting TSs
    case 8: digitalWrite(A12,HIGH); ledcWriteTone(0,1500); delay(40);
            ledcWrite(0,0); digitalWrite(A12,LOW); break; //--low battery    
  }
}

void ResetSensors()  {
  TS1.begin(0x5A); TS2.begin(0x5B); TS3.begin(0x5C);
  TS1.setThresholds(0x19,0x05); TS2.setThresholds(0x19,0x05); TS3.setThresholds(0x19,0x05);
}

void ChkResetCapSensors() {
  int UpTime = int(millis()/1000/60); //--uptime in minutes
  int MinFactor = UpTime % 7;         //--divisor is number of minutes between TSs resets
  if (MinFactor != 3) {CapsResetFlag = false;}   //--first reset at compared minute
  else 
    if (!CapsResetFlag) {
      ResetSensors();
      // Buzz(1);
      CapsResetFlag = true;}
}

void ChkBattery() {
  float battRead = analogRead(A13); //--or analogRead(35)
  battRead = ((battRead*2) / 4095 * 3.3 * 1.096);
  if (battRead < 3.3) {  //3.3v as base for beep
    ++counter;
    if (counter > 80) {
      Buzz(8);
      counter = 0;    }}
}

void setup() {
  //Serial.begin(115200);
  pinMode(4, OUTPUT);       //--ledc for Buzzer : Arduino TONE not implemented for ESP32
  ledcSetup(0, 100000, 12); //--Parms=(channel, freq, resolution)
  ledcAttachPin(4, 0);
  pinMode(A12, OUTPUT);     //--Built in RED LED
  bleKeyboard.begin();
  Wire.begin (23, 22);     
  if (!TS1.begin(0x5A)) {Buzz(6); while (1);} else {Buzz(1);}
  if (!TS2.begin(0x5B)) {Buzz(6); while (1);} else {Buzz(2);}
  if (!TS3.begin(0x5C)) {Buzz(6); while (1);} else {Buzz(3);}
  TS1.setThresholds(0x19,0x05);
  TS2.setThresholds(0x19,0x05);
  TS3.setThresholds(0x19,0x05);
  loadKeyboardMap();
  delay(500);
}

void loop() {
  while (!bleKeyboard.isConnected()) {
    keybON = false;
    digitalWrite(A12, LOW);
    Buzz(4); }
  if (bleKeyboard.isConnected() && !keybON) {
    keybON = true;
    Buzz(5); 
    digitalWrite(A12, LOW); }
  ChkResetCapSensors();
  ChkBattery();
  currT1 = TS1.touched(); currT2 = TS2.touched(); currT3 = TS3.touched();   //--Get the currently touched sensor pads
  for (int i = 0; i < 12; i++) {
    if ((TS1.filteredData(i)>10000) or (TS2.filteredData(i)>10000) or (TS3.filteredData(i)>10000)) {
      Buzz(7); 
      ResetSensors(); } //--noise on I2C channel: must reset TSs
    if ((currT1 & _BV(i)) && !(lastT1 & _BV(i))) {capT = 0; senT = i; action = true; }
    if ((currT2 & _BV(i)) && !(lastT2 & _BV(i))) {capT = 1; senT = i; action = true; }
    if ((currT3 & _BV(i)) && !(lastT3 & _BV(i))) {capT = 2; senT = i; action = true; }}
  lastT1 = currT1; lastT2 = currT2; lastT3 = currT3;    //--reset TSs states     
  if (action) {
    action = false; 
    switch (L[senT][capT]) {
      case '1':bleKeyboard.print("si "); break;
      case '2':bleKeyboard.print("no "); break;
      case '3':bleKeyboard.print("verdad "); break;
      case '4':bleKeyboard.print("falso "); break;
      case '5':bleKeyboard.print("."); break;
      default :bleKeyboard.print(L[senT][capT]); break; }}
}

Credits

Pedro Martin

Pedro Martin

8 projects • 15 followers

Comments