Roland Pelayo
Published © CC BY

Boo Nought (Raffle Device)

A motion-activated raffle device that can draw numbers or names. Users can specify the range of numbers or the list of names through WiFi.

IntermediateFull instructions provided3 days574
Boo Nought (Raffle Device)

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1
SparkFun Triple Axis Accelerometer and Gyro Breakout - MPU-6050
SparkFun Triple Axis Accelerometer and Gyro Breakout - MPU-6050
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×1
jlcpcb
×1
Slide Switch
Slide Switch
×3
Battery Holder, 18650 x 2
Battery Holder, 18650 x 2
×1
18650
×2

Story

Read more

Schematics

Boo Nought Fritzing Diagram

Fritzing diagram of Boo Nought. Switches not included. Refer to schematic for a much more complete wiring diagram.

Boo Nougth Eagle Schematic

Eagle schematic of Boo Nought. A much more complete wiring diagram.

Code

Boo Nought Sketch

Arduino
Arduino sketch for Boo Nought. Uses the ESP8266 core.
/*
 * Draw Lots Sketch
 * by Roland Pelayo
 * 
 * A random number/name generator using ESP8266
 * 
 * Number range and names can be generated using ESP8266 web server. The range of numbers and/or names are stored in EEPROM
 * 
 * Required libraries:
 *  > ESP8266 Arduino Core
 *  > MPU6050 for ESP8266
 *  > Liquid Crystal
 *  > EEPROM
 */


#include <Wire.h>
#include <ESP8266WiFi.h> 
#include <MPU6050.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <stdio.h>
#include <string.h>

//#ifndef APSSID
#define APSSID "drawlots"
#define APPSK  "123456"
//#endif

/* Set these to your desired credentials. */
const char *ssid = APSSID;
const char *password = APPSK;

ESP8266WebServer server(80);

MPU6050 mpu;
int SCL_PIN=D6;
int SDA_PIN=D5;

#include <LiquidCrystal.h>
const int rs = D7, en = D8, d4 = D1, d5 = D2, d6 = D3, d7 = D4;
int sw = D0;

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
long randNumber,previousNumber;
bool stillShaking = false;
bool serverSetupDone = false;
int activity_counter;
int old_act_counter;
int sensitivity = 4;

byte page = 0;    //for checking which screen to display
byte _mode = 0; //for checking whice mode (names or numbers)
  
const char MAIN_PAGE[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<body>
<style>
  div{
    text-align: center;
  }
  body{
    font-family: Helvetica, Sans-serif;
  }
  button{
    font-size: 12;
    margin: 2px,2px,2px,2px;
  }
</style>
<div id="header">
  <h1>Draw Lots</h1>
  <h2>by Roland Pelayo</h2>
</div>
<div id="form">
 <h3>Choose option:</h3>
 <div id="buttons">
  <form action="/number">
    <button type="submit">Random Number</button>
  </form>
  &nbsp;
  <form action="/name">
   <button type="submit">Random Name</button>
  </form>
 </div>
</div>
</body>
</html>
)=====";

const char NUMBER_PAGE[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<body>

<style>
  div{
    text-align: center;
  }
  body{
    font-family: Helvetica, Sans-serif;
  }
  button{
    font-size: 12;
    margin: 2px,2px,2px,2px;
  }
</style>

<div id="header">
<h1>Random Number Generator</h1>
</div>
<div id="form">
 <form action="/save_numbers">
  <h4>Enter minimum number</h4>
  <input type="text" name="minNumber">
  <h4>Enter maximum number</h4>
  <input type="text" name="maxNumber">
  <p></p>
  <input type="submit" value="Save"></input>
 </form>
 </div>
 </body>
</html>
)=====";

const char NAMES_PAGE[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<body>

<style>
  div{
    text-align: center;
  }
  body{
    font-family: Helvetica, Sans-serif;
  }
  button{
    font-size: 12;
    margin: 2px,2px,2px,2px;
  }
</style>

 <div id="header">
 <h1>Random Names Generator</h1>
 </div>
 <div id="form">
  <form action="/save_names">
   <h4>Enter Names separated by comma</h4>
   <input type="text" name="names">
   <input type="submit" value="Submit"></input>
  </form>
 </div>
 </body>
</html>
)=====";

void handleRoot() {
  String st = MAIN_PAGE;
  server.send(200, "text/html", st);
}

void handleNumber(){
  String st = NUMBER_PAGE;
  server.send(200,"text/html", st);
}

void handleNames(){
  String st = NAMES_PAGE;
  server.send(200,"text/html", st);
}

void handleSaveNumbers(){
  String minNum = server.arg("minNumber");
  int minNumber = minNum.toInt();
  String maxNum = server.arg("maxNumber");
  int maxNumber = maxNum.toInt();
  
  EEPROM.write(0,minNumber);
  EEPROM.write(1,maxNumber);
  EEPROM.write(2,1);
  EEPROM.commit();
  String s = "<a href='/'>Home</a>";
  server.send(200,"text/html",s);
}

void handleSaveNames(){
  String names = server.arg("names");
  Serial.println(names);
  int counter;
  counter = countCommas(names)+1;
  Serial.print("Names: ");
  Serial.println(counter);
  EEPROM.write(2,0);
  EEPROM.write(3,counter);  //store number of names
  EEPROM.commit();
  String _names [20];
  for(int i = 0;i<counter;i++){
    _names[i] = getValue(names,',',i);
    writeString((i+1)*10,_names[i]);
  }

  String s2[20];
  for(int j=0;j<counter;j++){
    s2[j] = read_String((j+1)*10);
    Serial.println(s2[j]);
  }
  String s = "<a href='/'>Home</a>";
  server.send(200,"text/html",s);
}

//Function to write string to EEPROM
void writeString(char add, String data){
  int _size = data.length();
  int i;
  for(i = 0;i<_size;i++){
    EEPROM.write(add+i,data[i]);
  }
  EEPROM.write(add+_size,'\0');
  EEPROM.commit();
}

//Function to read string from EEPROM
String read_String(char add){
  int i;
  char data[100];
  int len=0;
  unsigned char k;
  k=EEPROM.read(add);
  while(k!= '\0' && len<500){
    k=EEPROM.read(add+len);
    data[len]=k;
    len++;
  }
  data[len]='\0';
  return String(data);
}

//Function to count commas, and consequently, the number of names
int countCommas(String s){
  byte counter = 0;
  int stringLength;
  byte index,newIndex;
  stringLength = s.length();
 // Serial.print("The string is: ");
 // Serial.println(s);
 // Serial.print("The string length is: ");
 // Serial.println(stringLength);
  index = s.indexOf(',');
 // Serial.print("Index of first comma is: ");
 // Serial.println(index);
  while(index < stringLength){
      newIndex = s.indexOf(',',index+1);
      if(newIndex > stringLength){
        break;
      }
      if(newIndex != -1){
   //     Serial.println("Next comma found! "); 
        counter++;
   //     Serial.print("Index of next comma is: ");
   //     Serial.println(newIndex);
   //     Serial.print("Commas found: ");
   //     Serial.println(counter);
      }else{
        break;
      }
      index = newIndex;
  }
  //Serial.print("The number of commas is: ");
  //Serial.println(counter+1);
  return counter+1;
}

//Function to split string with separator (comma, in this case)
String getValue(String data, char separator, int index){
  int found = 0;
  int strIndex[] = {0,-1};
  int maxIndex = data.length()-1;

  for(int i=0;i<=maxIndex && found <=index; i++){
    if(data.charAt(i)==separator || i==maxIndex){
      found++;
      strIndex[0]=strIndex[1]+1;
      strIndex[1]=(i==maxIndex)?i+1:i;
    }
    if(i==maxIndex && found == index){
      strIndex[0]= strIndex[1]+1;
      strIndex[1]= maxIndex;
      return data.substring(strIndex[0],strIndex[1]);
    }
  }
  return found>index ? data.substring(strIndex[0],strIndex[1]) : "";
}
  
void setup() 
{
  lcd.begin(16,2);
  delay(1);
  Serial.begin(9600);
  randomSeed(analogRead(0));

  pinMode(sw,INPUT);
  
  lcd.setCursor(0,0);
  lcd.print("Initializing");
  
  EEPROM.begin(512);

  initMPU();
}

void initMPU(){
  while(!mpu.beginSoftwareI2C(SCL_PIN,SDA_PIN,MPU6050_SCALE_2000DPS, MPU6050_RANGE_16G))
  {
    lcd.print("IMU Error!");
    delay(500);
  }
  delay(1000);
  lcd.clear();
  
  mpu.setAccelPowerOnDelay(MPU6050_DELAY_3MS);
  mpu.setIntFreeFallEnabled(false);  
  mpu.setIntZeroMotionEnabled(false);
  mpu.setIntMotionEnabled(false);
  mpu.setDHPFMode(MPU6050_DHPF_5HZ);
  mpu.setMotionDetectionThreshold(2);
  mpu.setMotionDetectionDuration(5);
  mpu.setZeroMotionDetectionThreshold(4);
  mpu.setZeroMotionDetectionDuration(2);  
}

void serverMode(){
  lcd.setCursor(0,0);
  lcd.print("Server mode     ");
  delay(1000);
  lcd.setCursor(0,0);
  lcd.print("Configuring AP  ");
  /* You can remove the password parameter if you want the AP to be open. */
  WiFi.softAP(ssid, password);

  IPAddress myIP = WiFi.softAPIP();
  
  lcd.setCursor(0,0);
  lcd.print("IP: ");
  lcd.print(myIP);
  server.on("/", handleRoot);
  server.on("/number",handleNumber);
  server.on("/name",handleNames);
  server.on("/save_numbers",handleSaveNumbers);
  server.on("/save_names",handleSaveNames);
  server.begin();
  lcd.setCursor(0,1);
  lcd.print("Server started  ");
  serverSetupDone = true;
}

void loop()
{
  if(digitalRead(sw) == 1){
    delay(500);
    if(serverSetupDone == false){
      serverMode();
    }else{ 
      server.handleClient();
    }
  }else{
    delay(500);
    gameMode();
  }
}

void gameMode(){
  WiFi.forceSleepBegin();
  Serial.print("Page: ");
  Serial.println(page);
  int stage = checkMotion();
  if(stage == 1 && page == 0){
      startCheckMode();  
  }else if(stage == 1 && page == 1){
      startRandomizer();
  }else if(stage == 0 && page == 0){
    //lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Shake device to  ");
    lcd.setCursor(0,1);
    lcd.print("start            ");
  }else if(stage == 0 && page == 1){
    lcd.setCursor(0,0);
    lcd.print("Shake device to  "); 
    lcd.setCursor(0,1);
    lcd.print("draw             ");
  }else{;}
  
  delay(500);
}

int checkMotion(){

  Activites act = mpu.readActivites();
  
  if(act.isActivity == 1){
    activity_counter++;
    Serial.print("Activity: ");
    Serial.println(activity_counter);
    if(activity_counter > 1000){
      activity_counter = 0;
    }
    if(activity_counter - old_act_counter == sensitivity){
      old_act_counter = activity_counter;
      return 1;
    }else{
      return 0;
    }
  }else{
    return 0;
  }
}
/*
int checkMotion(){
  //Vector rawAccel = mpu.readRawAccel();
  int count = 0;
  bool doneShaking = false;
  Activites act = mpu.readActivites();
  
  if(act.isActivity == 1 && act.isInactivity == 0){
    delay(500);
    Activites act = mpu.readActivites();
    if(act.isActivity == 1 && act.isInactivity == 0){
      stillShaking = true;
      lcd.clear();
      count++;
      if(count < 3){
        lcd.setCursor(0,0);
        lcd.print("Shake & shake!");
        lcd.setCursor(count,1);
        lcd.print('.');
        return 2;            
      }else if(count == 3){
        count = 0;
        return 3;
      }
    }else{
      return 0;
    }
   }else{
      return 0;
   }
  /*
  delay(1000);
  while(stillShaking == true){
      Activites act = mpu.readActivites();
      delay(500);
      if(act.isActivity && !act.isInactivity){
        stillShaking = true;
        count++;
        lcd.setCursor(0,0);
        lcd.print("Shake & shake!");
        lcd.setCursor(count,1);
        lcd.print('.');      
        if(count==3){
          doneShaking = true;
          stillShaking = false;
          count == 0;
          break;
        }   
        
      }
      delay(200);
  }
 if(doneShaking == true){
  return 1;
 } 
}*/

void startCheckMode(){
  _mode = EEPROM.read(2);
  if(_mode == 0){
    lcd.setCursor(0,0);
    lcd.print("Drawing Names    ");
    lcd.setCursor(0,1);
    lcd.print("                 ");
  }else if(_mode == 1){
    lcd.setCursor(0,0);
    lcd.print("Drawing Numbers  ");
    lcd.setCursor(0,1);
    lcd.print("                 ");
  }else{;}
  page = 1;
}

bool startRandomizer(){
  if(_mode == 1){
    Serial.println("Draw random numbers");
    byte data1,data2;
    data1 = EEPROM.read(0);
    data2 = EEPROM.read(1);
    
    int minNum = (int)data1;
    int maxNum = (int)data2;
    
    randNumber = random(minNum,maxNum);
    while(randNumber == previousNumber){
      randNumber = random(minNum,maxNum);
    }
    lcd.setCursor(0,0);
    lcd.print("Lucky number is: ");
    lcd.setCursor(0,1);
    lcd.print(randNumber);
    previousNumber = randNumber;
 
    if(randNumber > 9){
      lcd.print("               ");
    }else{
      lcd.print("              ");
    }
  }
  else if(_mode == 0){
    Serial.println("Draw random names");
    byte count;
    count = EEPROM.read(3);
    
    String names[20];

    for(int i=0;i<(int)count;i++){
      names[i] = read_String((i+1)*10);
    }
  
    randNumber = random(1,(int)count);
    while(randNumber == previousNumber){
      randNumber = random(1,(int)count);
    }
    lcd.setCursor(0,0);
    lcd.print("Lucky Person is: ");
    lcd.setCursor(0,1);
    lcd.print(names[randNumber]);
    previousNumber = randNumber;
  }
    delay(5000);
    page = 0;
}

Credits

Roland Pelayo

Roland Pelayo

5 projects • 5 followers
I am an electronics engineer working as a college professor and author of Teach Me Microcontrollers! - a blog about PICs, Arduino, etc.

Comments