Celia Garrido Hidalgo
Published © GPL3+

Fox Advisor

Smart Box to improve customer satisfaction in restaurants which allows clients to rate the service via Sigfox and ThingSpeak IoT platform

AdvancedFull instructions provided12 hours6,140

Things used in this project

Hardware components

Arduino MKR Fox 1200
Arduino MKR Fox 1200
Core of Fox Advisor
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
Screen for communication
×1
Keypad
Keypad for interaction with customers
×1
Adafruit white led
For user-adjustable luminosity
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
20k potentiometer for luminosity adjustment
×1
General Purpose Transistor NPN
General Purpose Transistor NPN
For Arduino-based LED strip regulation
×1
Resistor 1k ohm
Resistor 1k ohm
×4
Resistor 221 ohm
Resistor 221 ohm
×1
9V alkaline
×1
RGB Diffused Common Cathode
RGB Diffused Common Cathode
Diet indication LED
×1
LED (generic)
LED (generic)
Peronnel calling LED (green)
×1
myDigital Protoboard for NI myDAQ & myRIO
Digilent myDigital Protoboard for NI myDAQ & myRIO
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE
Arduino Web Editor
Arduino Web Editor
Sigfox
Sigfox
ThingSpeak API
ThingSpeak API
MATLAB
MATLAB
Fusion 360
Autodesk Fusion 360
Ultimaker Cura Software

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Fox Advisor 3D model

Smart Box to improve customer satisfaction in restaurants which allows clients to rate the service via Sigfox and ThingSpeak IoT platform. This model represents the housing of Fox Advisor device.

Fox Advisor 3D box model

Smart Box to improve customer satisfaction in restaurants which allows clients to rate the service via Sigfox and ThingSpeak IoT platform. This model represents the box located near Fox Advisor device

Schematics

Fox Advisor Schematics

Smart Box to improve customer satisfaction in restaurants which allows clients to rate the service via Sigfox and ThingSpeak IoT platform.

Code

Fox Advisor code

Arduino
This code includes LCD, keypad and MKRFox1200 board to build a Smart Table able to interact with customers in restaurants: Fox Advisor.
/*Developed by Celia Garrido Hidalgo - FoxAdvisor: a rating a day keeps the failure away (with MKRFox1200)

   This code includes LCD, keypad and MKRFox1200 board to build a Smart Table able to interact with customers in restaurants.

 *  ************************** INSTRUCTIONS **************************
   //             Press (A)dvance to next question.                  //
   //             Press (B)ill to request the check.                 //
   //             Press (C)all to ask for assistance.                //
   //             Press (D)iet for special requirements.             //
   //               (1) = Gluten-free                                //
   //               (2) = Lactose-free                               //
   //               (3) = Sugar-free                                 //
   //               (4) = Vegetarian                                 //
   //               (5) = Vegan                                      //
   //               (0) = Other                                      //
   //             Press (*) to rate our restaurant.                  //
 *  ******************************************************************

   Useful libraries and examples:
    Sigfox:
      SendBoolean.ino (from <SigFox.h>)
      WeatherMonitor.ino (from <SigFox.h>)
    LCD:
      HelloWorld.ino (from <LiquidCrystal.h>)
    Keypad:
      EventKeypad.ino (from <Keypad.h>)

   Ratings from customers are sent to Sigfox Backend and an HTTP callback sends    the results to ThingSpeak cloud using:
    Custom payload config:
      restaurantID::uint:8 tableID::uint:8 allergy::uint:8 question1::uint:8          question2::uint:8 question3::uint:8
    URL pattern:
      http://api.thingspeak.com/update?api_key=yourapikeyhere&field1={customData#restaurantID}&field2={customData#tableID}&field3={customData#allergy}&field4={customData#question1}&field5={customData#question2}&field6={customData#question3}
*/

/************ LIBRARIES ************/

#include <LiquidCrystal.h>
#include <Keypad.h>
#include <SigFox.h>


/************ VARIABLES ************/

int RED = A1;
int GREEN = A2;
int BLUE = A3; // RGB LED
int questionNumber = 0; // Counter to check the current question asked
String question1 = "Service quality";
String question2 = "Food quality";
String question3 = "Did you enjoy?";
String range = "from 0 to 5: ";
boolean myFlags[] = {false, false}; // {Diet, Rating}

const byte ROWS = 4; // KeyPad: Four rows
const byte COLS = 4; // KeyPad: Four columns

char keys[ROWS][COLS] = { // Keys definition
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

typedef struct __attribute__ ((packed)) sigfox_message { // Structure to store information to be sent to sigfox-backend
  uint8_t restaurantID = 103; // Default
  uint8_t tableID = 7; // Default
  uint8_t allergy;
  uint8_t q1;
  uint8_t q2;
  uint8_t q3;
} SigfoxMessage;

SigfoxMessage msg; // Stub for message which will be sent


/********* INITIALIZATIONS *********/

LiquidCrystal lcd(0, 1, 2, 3, 4, 5);

byte rowPins[ROWS] = {13, 12, 11, 10}; // Pin connections for the keypad (ROWS)
byte colPins[COLS] = {9, 8, 7, 6}; // Pin connections for the keypad (ROWS)

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


/************* SET UP *************/

void setup() {
  if (!SigFox.begin()) {
    // Something is wrong
  }

  SigFox.end(); // Send module to standby until we need to send a message
  SigFox.debug(); // Important (for more information check the Forum http://forum.arduino.cc/index.php?topic=478950.0;nowap)

  lcd.begin(16, 2); // Setting up the LCD's number of columns and rows

  pinMode(14, OUTPUT); // Red LED to call for assitance
  pinMode(RED, OUTPUT); // RGB (red color)
  pinMode(GREEN, OUTPUT); // RGB (green color)
  pinMode(BLUE, OUTPUT); // RGB (blue color)

  pinMode(A5, OUTPUT); // To control the LED strip

  for (int i = 0; i < 3; i++) { // Display "Welcome" message
    lcd.print("    Welcome!");
    delay(500);
    lcd.clear();
    delay(500);
    lcd.setCursor(0, 0);
  }

  lcd.print(" * FoxAdvisor *"); // Default message
  lcd.setCursor(0, 1);
  lcd.print("Can we help you?");
  keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
}


/************* LOOP *************/

void loop() {
  char key = keypad.getKey();
  int rVal = analogRead(A6) / 4; // Read potentiometer voltage
  Serial.println(rVal); // Apply NPN voltage to control the LED strip
  analogWrite(A5, rVal);
}


/************ EVENTS ************/

void keypadEvent(KeypadEvent key) {
  int myCol = 0; // Column to write in the LCD
  int i;

  if (keypad.getState() == PRESSED) { // Once pressed any key...

    switch (key) {
      case 'A': /*************************   A   *************************/
        if (questionNumber == 1) { // If we were in question 1, go to question 2
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(question2);
          lcd.setCursor(0, 1);
          lcd.print(range);
        }

        else if (questionNumber == 2) { // If we were in question 2, go to question 3
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(question3);
          lcd.setCursor(0, 1);
          lcd.print(range);
        }

        else if (questionNumber == 3) { // If we were in question 3, send packet via Sigfox
          for (i = 0; i < 3; i++) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Sending result");
            lcd.setCursor(0, 1);
            lcd.print("via Sigfox");

            for (myCol = 10; myCol < 16; myCol++) {
              delay(150);
              lcd.setCursor(myCol, 1);
              lcd.print('.');
            }
          }

          SigFox.begin(); // Wait at least 30ms after first configuration (100ms before)
          delay(100);
          SigFox.status();
          delay(1);
          SigFox.beginPacket(); // Sending packet via Sigfox
          SigFox.write((uint8_t*)&msg, 6);
          SigFox.endPacket(); // We finish the packet
          SigFox.end();

          lcd.clear(); // Say goodbye to customers
          delay(50);
          lcd.setCursor(0, 0);
          lcd.print("    Done! ;)  ");
          lcd.setCursor(0, 1);
          lcd.print("Have a nice day!");

          delay(8000); // Wait 8 seconds...
          lcd.clear();
          lcd.print(" * FoxAdvisor *"); // Receive new customers
          lcd.setCursor(0, 1);
          lcd.print("Can we help you?");
          questionNumber == 0;
        }

        break;

      case 'B':  /*************************   B   *************************/
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("- Bill request -");
        lcd.setCursor(0, 1);
        lcd.print("Press *, please");

        for (i = 0; i < 3; i++) { // Turn on notification LED to call the personnel and request the bill. In the meantime, fulfill the questionnaire
          digitalWrite(14, LOW);
          delay(500);
          digitalWrite(14, HIGH);
          delay(500);
        }

        delay(2000);
        lcd.clear();
        lcd.print(" * FoxAdvisor *");
        lcd.setCursor(0, 1);
        lcd.print("Can we help you?");
        break;

      case 'C':  /*************************   C   *************************/
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("We'll be here");
        lcd.setCursor(0, 1);
        lcd.print("in a moment!");

        for (i = 0; i < 3; i++) { // Turn on notification LED to call the personnel
          digitalWrite(14, LOW);
          delay(500);
          digitalWrite(14, HIGH);
          delay(500);
        }

        delay(2000);
        lcd.clear();
        lcd.print(" * FoxAdvisor *");
        lcd.setCursor(0, 1);
        lcd.print("Can we help you?");

        break;

      case 'D':  /*************************   D   *************************/
        lcd.clear();
        lcd.setCursor(myCol, 0);
        lcd.print("Special diet");
        lcd.setCursor(myCol, 1);
        lcd.print("requirements");

        for (myCol = 12; myCol < 16; myCol++) {
          delay(200);
          lcd.setCursor(myCol, 1);
          lcd.print('.');
        }

        delay(1000);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Select option:");
        myFlags[0] = true; // Activate the diet flag.

        break;

      case '*': // Let's rate the restaurant!
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Great! Let's");
        lcd.setCursor(0, 1);
        lcd.print("start");

        for (myCol = 5; myCol < 16; myCol++) {
          delay(150);
          lcd.setCursor(myCol, 1);
          lcd.print('.');
        }

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(question1); // Asking question 1...
        lcd.setCursor(0, 1);
        lcd.print(range);

        break;

      case '#': // Nothing happens

        break;

      default: // For the numeric values of the keypad

        if (key > '5') { // The value specified is out of range
          for (i = 0; i < 3; i++) {
            lcd.clear();
            delay(500);
            lcd.setCursor(0, 0);
            lcd.print("Out of range 0-5");
            delay(500);
          }

          if (myFlags[0]) { // If we were asking for diet requirements...
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Select option:");
          }

          else if (myFlags[1]) { // If we were asking for any question of the questionnaire...

            switch (questionNumber) { // Where are we?
              case 0:
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print(question1); // Question 1
                lcd.setCursor(0, 1);
                lcd.print(range);
                break;

              case 1:
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print(question2); // Question 2
                lcd.setCursor(0, 1);
                lcd.print(range);
                break;

              case 2:
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print(question3); // Question 3
                lcd.setCursor(0, 1);
                lcd.print(range);
                break;
            }
          }
          break;
        }

        if (myFlags[0]) { // Special diet requirements
          lcd.setCursor(15, 0);
          lcd.print((char)key);
          myFlags[0] = false; // Clear the diet flag.
          delay(500);

          for (myCol = 0; myCol < 16; myCol++) {
            lcd.setCursor(myCol, 1);
            lcd.print('.');
            delay(80);
          }

          lcd.setCursor(0, 1);
          lcd.print("   Got it! ;)   ");
          analogWrite(RED, 0); // Switch off the RGB LED just in case
          analogWrite(GREEN, 0);
          analogWrite(BLUE, 0);

          switch (key) {
            case '1': // Gluten-free
              analogWrite(RED, 255);
              analogWrite(GREEN, 71);
              analogWrite(BLUE, 92);
              msg.allergy = 1;
              break;

            case '2': // Lactose-free
              analogWrite(BLUE, 200);
              msg.allergy = 2;
              break;

            case '3': // Sugar-free
              analogWrite(RED, 185);
              analogWrite(GREEN, 100);
              msg.allergy = 3;
              break;

            case '4': // Vegetarian
              analogWrite(RED, 255);
              analogWrite(GREEN, 225);
              analogWrite(BLUE, 53);
              msg.allergy = 4;
              break;

            case '5': // Vegan
              analogWrite(GREEN, 255);
              msg.allergy = 5;
              break;

            default: // Turn on notification LED to call the personnel and set the allergy type
              msg.allergy = 0;
              for (i = 0; i < 3; i++) {
                digitalWrite(14, LOW);
                delay(500);
                digitalWrite(14, HIGH);
                delay(500);
              }
              break;
          }

          delay(2000);
          lcd.clear();
          lcd.print(" * FoxAdvisor *"); // Go back to the default message
          lcd.setCursor(0, 1);
          lcd.print("Can we help you?");
        }

        else if (myFlags[1] && questionNumber == 0) { // Rating process, question 1 (getting the answer of the customer)
          questionNumber++;
          lcd.setCursor(13, 1);
          lcd.print((char)key);
          msg.q1 = key - 48; // Write in the structure the int value
        }

        else if (myFlags[1] && questionNumber == 1) { //Rating process, question 2 (getting the answer of the customer)
          lcd.setCursor(13, 1);
          lcd.print((char)key);
          questionNumber++;
          msg.q2 = key - 48; // Write in the structure the int value
        }

        else if (myFlags[1] && questionNumber == 2) { //Rating process, question 3 (getting the answer of the customer)
          lcd.setCursor(13, 1);
          lcd.print((char)key);
          questionNumber++;
          myFlags[1] = false;
          msg.q3 = key - 48; // Write in the structure the int value
        }
    }
  }
}

ThingSpeak application code

MATLAB
Code for visualization in ThingSpeak
% Celia Garrido-Hidalgo
% Channel ID to read data from 
readChannelID = 357609; 
question1 = 4; 
question2 = 5; 
question3 = 6; 

% Channel Read API Key  
% If your channel is private, then enter the read API 
% Key between the '' below:  
readAPIKey = 'WRITEHEREYOURKEY'; 

question1Data = thingSpeakRead(readChannelID, 'Fields', question1, 'NumPoints', 5, 'ReadKey', readAPIKey); 
question2Data = thingSpeakRead(readChannelID, 'Fields', question2, 'NumPoints', 5, 'ReadKey', readAPIKey); 
question3Data = thingSpeakRead(readChannelID, 'Fields', question3, 'NumPoints', 5, 'ReadKey', readAPIKey); 

bar(1:5, 33.33/5*[question1Data question2Data question3Data], 0.5, 'stack') 

axis([0 6 0 100]) 
title('Customer satisfaction') 
xlabel('Customer ID') 
ylabel('Satisfaction (%)') 
legend('Service quality', 'Food quality', 'Did you enjoy?')  

Credits

Celia Garrido Hidalgo

Celia Garrido Hidalgo

8 projects • 16 followers
Ph.D. Student at the University of Castilla-La Mancha (Albacete, Spain)

Comments