RiderScan - a Horse / Barn Management System

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

RiderScan - a Horse / Barn Management System

Post by miax »

I finished the prototype for my RiderScan project Today, just in time for the AAE Google-plus show and it was alot of fun!! :)

I decided I should make a forum post about it and ask for input and ideas, as I still have many challenges with the project. :?:

I spun it up as a group of detailed pictures and made a PDF out of it. I'll post the shots as individual replies here. The first 3 slides are the overview:
Slide 1
Slide 1
RiderScanPrototypePage1.jpg (373.26 KiB) Viewed 10895 times
Slide 2
Slide 2
RiderScanProtoFrontMod.jpg (601.69 KiB) Viewed 10895 times
Slide 3
Slide 3
RiderScanProtoSideMod.jpg (590.19 KiB) Viewed 10895 times
Next response will cover the Proto Shield.

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

The next shots cover the Mega Proto shield I used to combine the parts.

First the top (clean) side:
Slide 4
Slide 4
RiderScanProtoSideTop.jpg (763.22 KiB) Viewed 10892 times
Then the bottom slide:
Slide 4
Slide 4
RiderScanProtoSideTop.jpg (763.22 KiB) Viewed 10892 times
Next response will cover the Wave shield.
Attachments
Slide 5
Slide 5
RiderScanProtoSideBottom.jpg (738.12 KiB) Viewed 10892 times

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

I had to modify the Wave shield per the hack to make it work on the Mega:
Slide 6
Slide 6
RiderScanWaveShield1.jpg (656.38 KiB) Viewed 10891 times
Then some comments about the code and the sketch itself:

The RiderScan Code:

Goals:
1. To match tack to horses, which is a great source of student frustration at any big horse farm.
2. To allow riders to log-in and take or cancel a riding lesson or practice ride, providing the first digital accounting system for riding students and instructors.
3. To make Misty Brae Farm the coolest, most technologically-advanced pony club center!

State:
The current alpha software was designed using example code from Adafruit and other open source sketches provided by the community – and this code shall also be shared openly. From there combined examples one at a time and customized everything until I got it all working. I consider the current rendition of the RiderScan code as “slopware” (because it is not yet fully clean or well-enough documented. I shall make many improvements over the coming months!

Libraries:
* The WaveHC library by fat16lib is used to drive the Wave shield and attached SD card.
* The PN532 library with example code from Adafruit is used and expanded-on.
* The ST7656 LCD library and example code from Adafruit is used and expanded-on.
* The Adafruit rotary encoder example code was used and expanded-on.
* The Arduino Ethernet library is used, with Many customizations to come by Mike Bush
(my friend who is writing the SNMP trap/network code for RiderScan to talk to the server).

Design:
* All functions are defined up front, above the setup() and loop() functions at the bottom.
* Setup() starts by reading the SD card and make sure we have good files, then initializing the
wave shield, PN532 board and playing the introduction LCD and voice files.
* Loop() then looks for RFID cards, and when it finds one it matches the ID to the database
on the SD card. If a match is found, the program determines which functions to activate.
* There is one menu function that is used at all menu levels for code optimization/smallness.
* There are functions for riding lessons and practice rides that take rotary encoder input and
call the LCD menu functions and play voice files accordingly.
* Support functions for avail. Mem., playing wave files, checking the SD card and reading the
RiderScan database on the SD card are all broken out for efficiency.
* Current code compiles at ~31k with logs / ~27k without and runs with ~500 bytes free. :p

The current code (minus the ChronoDot time and Trapping code (WIP)):

Code: Select all

// ///////////////////////////////////////////////////////// 
// //   RiderScan v0.8 - by Kris Kortright and Mike Bush 
// //   Inspired by Adafruit Industries and the DIY community
// //   Release under public license - July, 2011
// /////////////////////////////////////////////////////////

#include <SPI.h>
#include <ST7565.h>
#include <PN532.h>
#include <Ethernet.h>
#include "WaveHC.h"
#include "WaveUtil.h"


// Setup the log level; 0=OFF, 1=ON
#define LOG_DEBUG 0

// =-=-= Networking Stuffs =-=-=	r
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x3F, 0x48 };
byte ip[] = { 192, 168, 1, 195 };


// =-=-= RFID PN532 Configuration =-=-=
#define SCK 15
#define MOSI 14
#define SS 33
#define MISO 25
PN532 nfc(SCK, MISO, MOSI, SS);


// =-=-= Wave shield configuration =-=-=
SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
FatReader file;   // This object represent the WAV file for a pi digit or period
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time


// =-=-= LED / Piezo Configuration =-=-=
// LED Pinouts:
// Yellow:  GreenLED (long)
// Green:   GreenLED (short)
// Blue:    BlueLED (long)
// Purple:  BlueLED (short)
// White:   RedLED (short)
// Grey:    RedLED (long)
// D. Blue: Piezo
// Black:   Pizzo
int RFIDbluePIN = 42;
int RFIDgreenPIN = 32;
int RFIDredPIN = 38;


// =-=-= Rotary Encoder Configuration =-=-=
int RotaryEncoderButtonPIN = 48;
int RotaryEncoderRightPIN = 46;
int RotaryEncoderLeftPIN = 44;

int EncoderState = 0; 
int EncoderStateR = 0;
int EncoderStateL = 0;


// =-=-= LCD ST7565 Configuration =-=-=
// the LCD backlight is connected up to a pin so you can turn it on & off
#define BACKLIGHT_LED 10
// pin 9 - Serial data out (SID) = 9
// pin 8 - Serial clock out (SCLK) = 8
// pin 7 - Data/Command select (RS or A0) = 7
// pin 6 - LCD reset (RST) = 6
// pin 5 - LCD chip select (CS) = 0
//ST7565 glcd(9, 8, 7, 6, 5);
ST7565 glcd(7, 19, 18, 17, 16);

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16


// a bitmap of a 16x16 fruit icon
static unsigned char __attribute__ ((progmem)) logo16_glcd_bmp[]={
0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0xf8, 0xbe, 0x9f, 0xff, 0xf8, 0xc0, 0xc0, 0xc0, 0x80, 0x00,
0x20, 0x3c, 0x3f, 0x3f, 0x1f, 0x19, 0x1f, 0x7b, 0xfb, 0xfe, 0xfe, 0x07, 0x07, 0x07, 0x03, 0x00, };

char buf[168];
char foofile[16];

// Define the web server
Server server(80);

// print error message and halt if SD I/O error, great for debugging!
void sdErrorCheck(void)
{
  if (!card.errorCode()) return;
#if LOG_DEBUG
  Serial.print("\r\nSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  Serial.print(", ");
  Serial.println(card.errorData(), HEX);
#endif
  while(1);
}


// Play a file and wait for it to complete
void playcomplete(char *name) {
  playfile(name);
  while (wave.isplaying);

  // see if an error occurred while playing
  sdErrorCheck();
}


// Open and start playing a WAV file 
void playfile(char *name) 
{
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
  if (!file.open(root, name)) {
#if LOG_DEBUG
    Serial.print("Couldn't open file ");
    Serial.print(name); 
#endif
    return;
  }
  if (!wave.create(file)) {
#if LOG_DEBUG
    Serial.println("Not a valid WAV");
#endif
    return;
  }
  // ok time to play!
  wave.play();

//  wave.volume=12;
}


// Define the variables we'll need to use. 
//   Strange as it sounds to an AVR newbie like me, but I have found that defining a set of universal/global 
//   variables that I re-use in every function actually saves a ton of ram. I have to remember to clear them
//   when needed though. ;) --MIAX
int c, d, e, k, choice, foundTag, takingAnyLesson;
int MenuPos = 1;
int menuLevel = 1;
int MBF_MAX_RECORDS = 250;
unsigned long MBFNFC_TagNumber;

String FileReadID = "";
char rawdata[2], data[2], myword[64];
char MBFNFC_TagCategory[9]; // GOOD, BAD, ADMIN (for humans) - HORSE (for horses) - TACK, ITEM (for stuff) and UNASSIGNED
char MBFNFC_CommonName[32];
char MBFNFC_VoiceFile[11];
char MBFNFC_LessonAvailability[2];
char MBFNFC_Notes[32];


// This function will read the contents of the MBF Horse database and count the number of horses.
void queryMBFdatabase(uint32_t RFIDcardID)
{
  foundTag = 0;
  sprintf(foofile, "MBFNFC.CSV");
  if (!file.open(root, foofile)) {
#if LOG_DEBUG
    Serial.print("Couldn't open MBF database: ");
    Serial.print(foofile); 
#endif

    glcd.clear();
    glcd.display();
    glcd.drawstring(0, 0, " Could not open MBF database! Halting...");
    glcd.display();
    while(1);
  } else {
    for(c = 0; c <= MBF_MAX_RECORDS; c++) {
      sprintf(myword, "");
      d = 1;
      e = 0;
      while((e == 0) && (foundTag == 0)) {
        file.read(rawdata, 1);
        if((!rawdata) || (rawdata == NULL)) {
          break;
        }
        sprintf(data, "%c", rawdata[0]);

        if(!strcmp (data, "\n")) {
          // do nothing
        } else if(!strcmp (data, ",")) {
          if(d == 1) {
            FileReadID = myword;
            MBFNFC_TagNumber = FileReadID.toInt();
            d = 2;
          } else if(d == 2) {
            strcpy(MBFNFC_TagCategory, myword);
            d = 3;
          } else if(d == 3) {
            strcpy(MBFNFC_CommonName, myword);
            d = 4;
          } else if(d == 4) {
            strcpy(MBFNFC_VoiceFile, myword);
            d = 5;
          } else if(d == 5) {
            strcpy(MBFNFC_LessonAvailability, myword);
            d = 6;
          }
          sprintf(myword, "");
        } else if(!strcmp (data, "|")) {
          e = 1;
          d = 1;
          strcpy(MBFNFC_Notes, myword);

          if(MBFNFC_TagNumber == RFIDcardID) {
#if LOG_DEBUG
              Serial.println("Found Tag in the MBF database!  Record Data:");
              Serial.println(MBFNFC_TagNumber);
              Serial.println(MBFNFC_CommonName);
              Serial.println(MBFNFC_VoiceFile);
              Serial.println(MBFNFC_LessonAvailability);
              Serial.println(MBFNFC_Notes);
#endif
            if(!strcmp (MBFNFC_TagCategory, "HORSE")) {
              glcd.clear();
              glcd.display();
              sprintf(buf, " That tag belongs to:                         %s", MBFNFC_CommonName);
              glcd.drawstring(0, 0, buf);
              glcd.display();
              sprintf(buf, "TTBETM.WAV");
              playcomplete(buf);
              sprintf(buf, "%s.WAV", MBFNFC_VoiceFile);
              playcomplete(buf);
            } else if((!strcmp (MBFNFC_TagCategory, "TACK")) || (!strcmp (MBFNFC_TagCategory, "ITEM"))) {
              glcd.clear();
              glcd.display();
              k = 0;
              if(!strcmp (MBFNFC_CommonName, "Bridle")) {
                sprintf(buf, " That bridle belongs to:                          %s", MBFNFC_Notes);
                glcd.drawstring(0, 0, buf);
                glcd.display();
                sprintf(buf, "BRIDLM.WAV");
                k++;
              } else if(!strcmp (MBFNFC_CommonName, "Girth")) {
                sprintf(buf, " That girth belongs to:                          %s", MBFNFC_Notes);
                glcd.drawstring(0, 0, buf);
                glcd.display();
                sprintf(buf, "GIRTLM.WAV");
                k++;
              } else if(!strcmp (MBFNFC_CommonName, "Saddle")) {
                sprintf(buf, " That saddle belongs to:                          %s", MBFNFC_Notes);
                glcd.drawstring(0, 0, buf);
                glcd.display();
                sprintf(buf, "SADDLM.WAV");
                k++;
              } else if(!strcmp (MBFNFC_CommonName, "FlyMask")) {
                sprintf(buf, " That fly mask belongs to:                          %s", MBFNFC_Notes);
                glcd.drawstring(0, 0, buf);
                glcd.display();
                sprintf(buf, "FLYMKM.WAV");
                k++;
              } else if(!strcmp (MBFNFC_TagCategory, "Object")) {
                sprintf(buf, " That object belongs to:                          %s", MBFNFC_Notes);
                glcd.drawstring(0, 0, buf);
                glcd.display();
                sprintf(buf, "ITMDLM.WAV");
                k++;
              }
              if(k > 0) {
                playcomplete(buf);
                sprintf(buf, "%s.WAV", MBFNFC_VoiceFile);
                playcomplete(buf);
                // xyzzy - Log the entry HERE.
              }
            }
            foundTag = 1;
            e = 1;
            break;
          }
        } else if(!strcmp (data, "&")) {
          e = 1;
          c = MBF_MAX_RECORDS;
          foundTag = 1;
          break;
        } else {
          strcat(myword, data);
        }
      }
    }
    file.close();

    if(foundTag == 0) {
#if LOG_DEBUG
      Serial.println("End of MBF Horse Database, tag not found!");
#endif
      sprintf(buf, "CNFNFM.WAV");
      playcomplete(buf);
    } else {
      delay(2000);
    }
  }
}



uint8_t menuInteractionModule(void) {
  // Setup the LCD with the menu and default choices.
  glcd.clear();
  glcd.display();
  if(menuLevel == 1) {
    sprintf(buf, " Main Menu:           --> 1. Riding Lessons    2. Practice Rides    3. Horse & Tack      4. Exit RiderScan=-=-=-=-=-=-=-=-=-=-=Push Knob to Select");
  } else if(menuLevel == 2) {
    sprintf(buf, " Lessons Menu:        --> 1. Take Lesson       2. Cancel Lesson     3. Main Menu                          =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
  } else if(menuLevel == 3) {
    sprintf(buf, " Practice Ride Menu:  --> 1. Take practice        ride today!       2. Cancel your          practice ride.    3. Main Menu     =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
  } else {
    sprintf(buf, " Confirmation Menu:   --> 1. Yes               2. No                                                      =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
  }
  glcd.drawstring(0, 0, buf);
  glcd.display();

  // Determine which voice file to play when displaying the menu.
  if(menuLevel == 1) {
    sprintf(buf, "MAINMM.WAV");
    playcomplete(buf);
  } else if(menuLevel == 2) {
    sprintf(buf, "RDLESM.WAV");
    playcomplete(buf);
  } else if(menuLevel == 3) {
    sprintf(buf, "RDPRIM.WAV");
    playcomplete(buf);
  }

  // Now setup a loop and look for rotary encoder output.
  c = 1;  
  d = 0;
  MenuPos = 1;
  while(c == 1) {
    EncoderState = digitalRead(RotaryEncoderButtonPIN);
    EncoderStateR = digitalRead(RotaryEncoderRightPIN);
    EncoderStateL = digitalRead(RotaryEncoderLeftPIN);
 
    // Encoder button push!
    if(EncoderState == LOW) {  
      digitalWrite(RFIDredPIN, HIGH);  
      c = 0;
    } else {
      digitalWrite(RFIDredPIN, LOW);
    }

    // If both directions show moving, set them to none moving.
    if((EncoderStateR == LOW) && (EncoderStateL == LOW)) {
      EncoderStateR = HIGH;
      EncoderStateL = HIGH;
    }

    // Handle left turn / menu up scrolling.
    if(EncoderState == HIGH) {
      if (EncoderStateL == LOW) {   
        EncoderStateL = HIGH;
        d = 1;
        if(MenuPos == 1) {
          MenuPos = 2;
        } else if(MenuPos == 2) {
          if(menuLevel == 4) {
            MenuPos = 1;
          } else {
            MenuPos = 3;
          }
        } else if(MenuPos == 3) {
          if((menuLevel == 2) || (menuLevel == 3)) {
            MenuPos = 1;
          } else if(menuLevel == 4) {
            MenuPos = 2;
          } else {
            MenuPos = 4;
          }
        } else if(MenuPos == 4) {
          MenuPos = 1;
        }
        digitalWrite(RFIDredPIN, HIGH);  
      } else {
        digitalWrite(RFIDredPIN, LOW);
      }

      // Handle right turn / menu down scrolling.
      if(EncoderStateR == LOW) {   
        EncoderStateR = HIGH;
        d = 1;
        if(MenuPos == 4) {
          if(menuLevel == 4) {
            MenuPos = 2;
          } else {
            MenuPos = 3;
          }
        } else if(MenuPos == 3) {
          if(menuLevel == 4) {
            MenuPos = 1;
          } else {
            MenuPos = 2;
          }
        } else if(MenuPos == 2) {
          MenuPos = 1;
        } else if(MenuPos == 1) {
          if(menuLevel == 4) {
            MenuPos = 2;
          } else if((MenuPos == 3) || (MenuPos == 2)) {
            MenuPos = 3;
          } else {
            MenuPos = 4;
          }
        }
        digitalWrite(RFIDredPIN, HIGH);  
      } else {
        digitalWrite(RFIDredPIN, LOW);
      }
 
      // With our choice made, show the resulting change in the menu on the LCD.
      if(d == 1) {
        glcd.clear();
        glcd.display();
        if(MenuPos == 2) {
          if(menuLevel == 1) {
            sprintf(buf, " Main Menu:               1. Riding Lessons--> 2. Practice Rides    3. Horse & Tack      4. Exit RiderScan=-=-=-=-=-=-=-=-=-=-=Push Knob to Select");
          } else if(menuLevel == 2) {
            sprintf(buf, " Lessons Menu:            1. Take Lesson   --> 2. Cancel Lesson     3. Main Menu                          =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          } else if(menuLevel == 3) {
            sprintf(buf, " Practice Ride Menu:      1. Take practice        ride today!   --> 2. Cancel your          practice ride.    3. Main Menu     =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          } else {
            sprintf(buf, " Confirmation Menu:       1. Yes           --> 2. No                                                      =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          }
        } else if(MenuPos == 3) { 
          if(menuLevel == 1) {
            sprintf(buf, " Main Menu:               1. Riding Lessons    2. Practice Rides--> 3. Horse & Tack      4. Exit RiderScan=-=-=-=-=-=-=-=-=-=-=Push Knob to Select");
          } else if(menuLevel == 2) {
            sprintf(buf, " Lessons Menu:            1. Take Lesson       2. Cancel Lesson --> 3. Main Menu                          =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          } else if(menuLevel == 3) {
            sprintf(buf, " Practice Ride Menu:      1. Take practice        ride today!       2. Cancel your          practice ride.--> 3. Main Menu     =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          }
        } else if(MenuPos == 4) {
          if(menuLevel == 1) {
            sprintf(buf, " Main Menu:               1. Riding Lessons    2. Practice Rides    3. Horse & Tack  --> 4. Exit RiderScan=-=-=-=-=-=-=-=-=-=-=Push Knob to Select");
          }
        } else {
          if(menuLevel == 1) {
            sprintf(buf, " Main Menu:           --> 1. Riding Lessons    2. Practice Rides    3. Horse & Tack      4. Exit RiderScan=-=-=-=-=-=-=-=-=-=-=Push Knob to Select");
          } else if(menuLevel == 2) {
            sprintf(buf, " Lessons Menu:        --> 1. Take Lesson       2. Cancel Lesson     3. Main Menu                          =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          } else if(menuLevel == 3) {
            sprintf(buf, " Practice Ride Menu:  --> 1. Take practice        ride today!       2. Cancel your          practice ride.    3. Main Menu     =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          } else {
            sprintf(buf, " Confirmation Menu:   --> 1. Yes               2. No                                                      =-=-=-=-=-=-=-=-=-=  Push Knob to Select");
          }
          if(MenuPos != 1) {
            MenuPos = 1;
          }
        }
        glcd.drawstring(0, 0, buf);
        glcd.display();
        delay(500);
        d = 0;
      }
    }
  }
  
  return(MenuPos);
}



void ridingLessonModule(void) 
{
  menuLevel = 2;
  choice = menuInteractionModule();

  if(choice == 1) {
    sprintf(buf, "TCYTRM.WAV");
    playcomplete(buf);

    menuLevel = 4;
    choice = menuInteractionModule();

    if(choice == 1) {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Okay, I will mark    you down as riding ina lesson today.");
      glcd.display();
      sprintf(buf, "OIMYDM.WAV");
      playcomplete(buf);
      takingAnyLesson = 1;
      // omgwtf: Write log entry to file.

    } else {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Returning to main    menu...");
      glcd.display();
      sprintf(buf, "ORTMMM.WAV");
      playcomplete(buf);
    }
  } else if(choice == 2) {
    sprintf(buf, "TCYCRM.WAV");
    playcomplete(buf);

    menuLevel = 4;
    choice = menuInteractionModule();

    if(choice == 1) {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Okay, I will mark    you down as having   cancelled today's    riding lesson.");
      glcd.display();
      sprintf(buf, "OTLWCM.WAV");
      playcomplete(buf);
      takingAnyLesson = 1;
      // omgwtf: Write log entry to file.

    } else {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Returning to main    menu...");
      glcd.display();
      sprintf(buf, "ORTMMM.WAV");
      playcomplete(buf);
    }
  } else if(choice == 3) {
    glcd.clear();
    glcd.display();
    glcd.drawstring(0, 0, " Returning to main    menu...");
    glcd.display();
    sprintf(buf, "ORTMMM.WAV");
    playcomplete(buf);
  }
}


void practiceRideModule(void) 
{
  menuLevel = 3;
  choice = menuInteractionModule();

  if(choice == 1) {
    sprintf(buf, "TCYTPM.WAV");
    playcomplete(buf);

    menuLevel = 4;
    choice = menuInteractionModule();

    if(choice == 1) {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Okay, I will mark    you down as riding ina practice ride today");
      glcd.display();
      sprintf(buf, "OIMYDM.WAV");
      playcomplete(buf);
      takingAnyLesson = 1;
      // omgwtf: Write log entry to file.

    } else {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Returning to main    menu...");
      glcd.display();
      sprintf(buf, "ORTMMM.WAV");
      playcomplete(buf);
    }
  } else if(choice == 2) {
    sprintf(buf, "TCYCRM.WAV");
    playcomplete(buf);

    menuLevel = 4;
    choice = menuInteractionModule();
    
    if(choice == 1) {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Okay, I will mark    you down as having   cancelled today's    riding lesson.");
      glcd.display();
      sprintf(buf, "OTLWCM.WAV");
      playcomplete(buf);
      takingAnyLesson = 1;
      // omgwtf: Write log entry to file.

    } else {
      glcd.clear();
      glcd.display();
      glcd.drawstring(0, 0, " Returning to main    menu...");
      glcd.display();
      sprintf(buf, "ORTMMM.WAV");
      playcomplete(buf);
    }
  } else if(choice == 3) {
    glcd.clear();
    glcd.display();
    glcd.drawstring(0, 0, " Returning to main    menu...");
    glcd.display();
    sprintf(buf, "ORTMMM.WAV");
    playcomplete(buf);
  }
}


void restartRiderScan(void)
{
  e = availableMemory();

  digitalWrite(RFIDgreenPIN, LOW);
  digitalWrite(RFIDbluePIN, LOW);

  glcd.clear();
  glcd.display();
  sprintf(buf, " Have a Great Day at  Misty Brae Farm! :)                       Free Ram: %d", e);
  glcd.drawstring(0, 0, buf);
  glcd.display();
  sprintf(buf, "OHMGRA.WAV");
  playcomplete(buf);

  c = 0;
  delay(2000);
  glcd.clear();
  glcd.display();
  glcd.drawstring(0, 0, " Startup Complete.    Ready for Work!                           Wave RFID tag near   scanner to start!");
  glcd.display();
  sprintf(buf, "READYM.WAV");
  playcomplete(buf);
}




void horseAndTackModule(void) 
{

}


// this function will return the number of bytes currently free in RAM
// written by David A. Mellis
// based on code by Rob Faludi http://www.faludi.com
int availableMemory() {
  int size = 1024; // Use 2048 with ATmega328
  byte *buff;

  while ((buff = (byte *) malloc(--size)) == NULL)
    ;
  free(buff);

  return size;
}



// ////////////////////////////
// // Setup and Loop Modules //
// ////////////////////////////

void setup() {
  pinMode(RFIDredPIN, OUTPUT);
  pinMode(RFIDgreenPIN, OUTPUT);
  pinMode(RFIDbluePIN, OUTPUT);

  pinMode(RotaryEncoderButtonPIN, INPUT);    
  pinMode(RotaryEncoderRightPIN, INPUT);
  pinMode(RotaryEncoderLeftPIN, INPUT);

  digitalWrite(RFIDredPIN, HIGH);
  digitalWrite(RFIDgreenPIN, LOW);
  digitalWrite(RFIDbluePIN, LOW);

#if LOG_DEBUG
  Serial.begin(9600);
#endif

  SPI.begin();


  // Wave Shield and SD Card setup/configuration
  if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
  //  if (!card.init()) {         //play with 8 MHz spi (default faster!)  
#if LOG_DEBUG
    sprintf(buf, "Card init. failed!");
    Serial.println(buf);
#endif
  }
  
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);

  // Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
    if (vol.init(card, part)) 
      break;                           // we found one, lets bail
  }
  if (part == 5) {                     // if we ended up not finding one  :(
#if LOG_DEBUG
    Serial.println("No valid FAT partition!");  // Something went wrong, lets print out why
#endif
  }
  
  // Lets tell the user about what we found
#if LOG_DEBUG
  putstring("Using partition ");
  Serial.print(part, DEC);
  putstring(", type is FAT");
  Serial.println(vol.fatType(),DEC);     // FAT16 or FAT32?
#endif
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
#if LOG_DEBUG
    Serial.println("Can't open root dir!");      // Something went wrong,
#endif
  }
  
  // Print out all of the files in all the directories.
  // root.ls(LS_R | LS_FLAG_FRAGMENTED);


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

  // Start the PN532 NFC reader
  nfc.begin();

  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
#if LOG_DEBUG
    Serial.print("Didn't find PN53x board");
#endif
    while (1); // halt
  }
  // Got ok data, print it out!
#if LOG_DEBUG
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
  Serial.print("Supports "); Serial.println(versiondata & 0xFF, HEX);
#endif
  // configure board to read RFID tags
  nfc.SAMConfig();

  // turn on backlight
  pinMode(BACKLIGHT_LED, OUTPUT);
  digitalWrite(BACKLIGHT_LED, HIGH);

  // initialize and set the contrast to 0x18
  glcd.begin(0x18);
  glcd.display(); // show splashscreen

  delay(2000);

  // draw a string at location (0,0)

  sprintf(buf, " RiderTrack v0.8       For Misty Brae Farm  By Kris Kortright    and Mike Bush                             Inspired by Ladyada  Adafruit Industries");
  glcd.clear();
  glcd.display();
  glcd.drawstring(0, 0, buf);
  glcd.display();

  // Play 'Welcome to Misty Brae Farm, this is the Rider Track System'
  sprintf(buf, "WELRDM.WAV");
  playcomplete(buf);

  delay(2000);

  glcd.clear();
  glcd.display();
  glcd.drawstring(0, 0, " Startup Complete.    Ready for Work!                           Wave RFID tag near  scanner to start!");
  glcd.display();

  // Play 'Startup complete, ready to work'
  sprintf(buf, "STCRFM.WAV");
  playcomplete(buf);
}



void loop() {
  int c, d, e;
  uint8_t encoderSelection;
  uint32_t RFIDcardID;

  // Search for miFare NFC/RFID cards.
  digitalWrite(RFIDgreenPIN, HIGH);
  RFIDcardID = 0;
  RFIDcardID = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A);
  
  if (RFIDcardID != 0) {

    digitalWrite(RFIDgreenPIN, LOW);
    digitalWrite(RFIDbluePIN, HIGH);
    glcd.clear();
    glcd.display();
    delay(200);

    sprintf(buf, " Found RFID %lu", RFIDcardID);
    glcd.drawstring(0, 0, buf);
    glcd.display();
    glcd.drawstring(0, 1, " Scanning Inventory.");
    glcd.display();

    /* Play FoundAnRFIDTagScanning.wav */
    sprintf(buf, "RFIDTM.WAV");
    playcomplete(buf);

    // Now scan the NFC tag database to find a matching record.
    queryMBFdatabase(RFIDcardID);

    if(foundTag == 1) {
      if((!strcmp (MBFNFC_TagCategory, "GOOD")) || (!strcmp (MBFNFC_TagCategory, "BAD")) || (!strcmp (MBFNFC_TagCategory, "ADMIN"))) {
        c = 1;
        d = 0;
        takingAnyLesson = 0;
        while(c == 1) {
          menuLevel = 1;
          encoderSelection = menuInteractionModule();
  
          if(encoderSelection == 1) {         // User wants to manage lesson rides.
            ridingLessonModule();
          } else if(encoderSelection == 2) {  // User wants to manage practice rides.
            practiceRideModule();
          } else if(encoderSelection == 3) {  // User wants to manage tack and items.
            horseAndTackModule();
          } else if(encoderSelection == 4) {  // User is done, reset system for next user.
            restartRiderScan();
            c = 0;
          }
          if(takingAnyLesson == 1) {
            c = 0;
            restartRiderScan();
          }
        }
      } else {
        glcd.clear();
        glcd.display();
        restartRiderScan();
      }
    } else {
      glcd.drawstring(0, 1, " Unknown Tag Category! Please put this tag in the Homeless Tag Box.");
      glcd.display();
      sprintf(buf, "CNFNFM.WAV");
      playcomplete(buf);
    }
  }
  // ListenForClients();

}
There are still many problems with the code that I have to work out, in addition to Mike adding the outbound trapping code. I'm still trying to find a clever way to synchronize the clocks. I can make the NTP request, but I have to figure out how to format the time to send it back (havn't even looked at it yet).

The Bigger electrical problem I'm having trouble with is an annoying feedback noise when both power supplies are plugged-in. I asked on the forums and got some helpful advice from support to get a DC-DC converter from Dimensional Engineering - which I did (PN: DE-SW050 5V 1A Switching voltage regulator). This worked good on a regular Arduino, but my RiderScan rig won't work with the 5V output going into the top 5V/GND PINS across the board from PINs 9 and 10... Is there a better/specific place I should plug the clean 5V power from the DC-DC regulator into the Arduino?

More updates to come - all feedback is Welcome!!

Cheers,

Kris

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Howdy!

I finally have the YouTube video done for RiderScan: http://www.youtube.com/watch?v=igMQNcV7kFc.

It took several days because I ran into SPI problems with the Wave/Ethernet shield not playing nice together. I ended up resolving it by reversing the order of the shields (with the Wave shield on top of the Ethernet shield). Then each time I need to send out data over the wire, I shut the SPI down: SPI.end(); - then re-start it: SPI.begin(); and then doing my Ethernet business. Once down I cycle the SPI bus again, and it's ready to play Waves again :)

One newbie question I have; If I have clean +5vcc power coming in from a DC-DC regulator, does it matter which 5v port on the Arduino that I plug it into?

More updates to come, have to complete the networking code now that I can network!

All feedback is Most welcome,

Kris

User avatar
Andyx
 
Posts: 40
Joined: Tue Aug 03, 2010 5:55 pm

Re: RiderScan - a Horse / Barn Management System

Post by Andyx »

Wow this is an incredible project.

Great Job!

Andy

User avatar
adafruit_support_bill
 
Posts: 88097
Joined: Sat Feb 07, 2009 10:11 am

Re: RiderScan - a Horse / Barn Management System

Post by adafruit_support_bill »

One newbie question I have; If I have clean +5vcc power coming in from a DC-DC regulator, does it matter which 5v port on the Arduino that I plug it into?
If you have clean, regulated 5v power, you can go right to the 5v pin on the "power" header on the Arduino.

Image

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Thanks Andyx! :)

Awesome Adafruit Support, thanks!!

That's the last bit of hardware I have to install on the unit: http://www.dimensionengineering.com/DE-SW050.htm, which hopefully will allow my 15v power supply to power both the Amp and the rest of the rig. I tried plugging the regulated 5v power into the top pin as you suggest of the Arduino, but the rig didn't completely turn-on. My wiring at that time was less than desirable, so I'll try again tonight with better wiring. If I'm un-lucky, the entire rig will pull more than 1 Amp and I'll have bought the wrong thing. :roll: Wouldn't be the first time!!

Lady Ada also suggested trying a 12V / 5amp power supply, and I got one of those last week from the Adafruit store - I'll try that tonight too and see if I get better results.

Cheers. :)

Kris

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

I think I discovered part of the reason why my DC-DC switching voltage regulator isn't powering my project - because I'm using both 3.3v and 5v power. :( Somehow I thought the Arduino would down-convert the 5v input power to 3.3v for that bus, but it clearly is not. I suspect I would need both a 5v and 3.3v regulator to make 1 power supply work.

The biggest challenge with power is the feedback I get through the Amp/speaker. If I use a standard 9v power supply for the Arduino and the 15v power supply for the Amp, there is very nasty cross-talk/static that comes through. If i plug the Arduino into a computer's via the USB port, 0% cross-talk/noise and the sound is perfect. I wonder, what kind of circuit or component would I need to eliminate noise from the power line?

More updates to come..

Kris

User avatar
adafruit_support_bill
 
Posts: 88097
Joined: Sat Feb 07, 2009 10:11 am

Re: RiderScan - a Horse / Barn Management System

Post by adafruit_support_bill »

The 3.3v regulator is based on the 5v supply - at least on the schematics: http://arduino.cc/en/uploads/Main/ardui ... ematic.pdf
The biggest challenge with power is the feedback I get through the Amp/speaker.
Make sure all your grounds are tied to the same place.

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Thank you for the reply! :)

I will try again tonight and make sure all the grounds are tied together. When I put the 5v/GND lines into the top of my Arduino stack, the units just didn't turn on - so I'm sure I was doing something wrong. :) Then I'll do more experimentation with the 5v DC-DC regulator and will report back on my progress.

Cheers!

Kris

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Happy Update! :)

Thank you again for the help Lady Ada and Adafruit Support! The power system now works perfectly running on the 12v/5amp power supply Lada Ada suggested in last-weeks Ask-An-Engineer, and the DC-DC 5v switching regulator is powering the entire Arduino rig without any feedback coming through the speaker as Adafruit Support suggested. :D It must have been bad wiring with not having the grounds tied, so I made a tiny breadboard into which I could plug the DC-DC regulator and power wires heading in and out and attached that to the Acrylic back of the unit with a screw mount:
RiderScan Power PCB/System
RiderScan Power PCB/System
RiderScanProto_v1.8_Power.JPG (227.94 KiB) Viewed 10323 times
Once all that was working well, I added the LED's to the front panel (don't have labels on them yet), as well as a master power switch (which ended up being alot bigger than they looked in the picture!). Lastly I exposed the Arduino reset button on the outside of the case so that I could reset it easily for testing. All connected together the prototype looks like this now:
RiderScan Prototype v0.2
RiderScan Prototype v0.2
RiderScanProto_v1.8_Front.JPG (246.25 KiB) Viewed 10323 times
Very happy with the progress on the hardware - again my sincere thanks for the support!!

Now I'm onto the troubling job of getting the Ethernet card working correctly. I'm getting problems where after the Ethernet shield does some work the Rig will still lock-up. That will be my troubleshooting job for the rest of the weekend. I am prepared for the possibility that (as many say) the Ethernet shield and Wave shield just can't work together - but I'm not convinced yet. Not until I've tried for a few more days. :) Should this ultimately fail, my backup plan will be to put an Xbee adapter in there and use the Tweet-A-Watt Started Kit that I got from Adafruit (http://www.adafruit.com/products/143) to broadcast data back to the server.

More updates to come! :)

Cheers,

Kris

pstemari
 
Posts: 310
Joined: Sun Mar 21, 2010 6:10 pm

Re: RiderScan - a Horse / Barn Management System

Post by pstemari »

Very cool! If this is going in a barn, you'll probably want to get a weatherproof enclosure. PacTec has some and they were offering free samples on most of the weatherproof ones the last time I looked. The speaker and the LCD may pose a problem.

User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Postby pstemari » 30 Jul 2011 23:43
Very cool! If this is going in a barn, you'll probably want to get a weatherproof enclosure. PacTec has some and they were offering free samples on most of the weatherproof ones the last time I looked. The speaker and the LCD may pose a problem.
Thank you for the feedback! :)

You are correct of course, and this is something I have been working on for quite awhile now (making weather-proof stuff for the barn), as with my RiderNet project (http://forums.adafruit.com/viewtopic.php?f=25&t=21120). I actually put that project on hold for a bit once I finished the electronics/DIY parts so that I could make the RiderScan prototype. Unlike the wireless routers submerged in small tanks of Mineral Oil, RiderScan has a speaker and LCD which you rightly point-out will be a problem in barn-weather conditions.

My original plan was to use Large size Otter Boxes from Adafruit (http://www.adafruit.com/products/339), which I already purchased. Unfortunately even the large-size doesn't have enough height to allow a triple-stack Arduino rig like I'm using with RiderScan and that was a frustrating blow. It's still a problem I'll have to solve once I make the 3 production units, and in truth the problem may be solved For me if I can't get this Ethernet shield working as I need it to (it's not being cooperative at the moment). Should my Ethernet shield idea fail, I'll fall back to the Xbee approach and using those to broadcast data back to the server. Not as elegant, nice or feature-rich as it will be if I can get the Ethernet shield working, but the side-effect of loosing it would be that I can use the Large Otter boxes again.

We'll see, today will be all about trying to get the Wave SD card, Ethernet shield and Mega to play nice. I may actually try and see if I can use the SD slot on the Ethernet shield instead of the SD slot on the Wave shield, I may try that too.

More to come,

Kris

pstemari
 
Posts: 310
Joined: Sun Mar 21, 2010 6:10 pm

Re: RiderScan - a Horse / Barn Management System

Post by pstemari »


User avatar
miax
 
Posts: 157
Joined: Tue Apr 05, 2011 11:41 am

Re: RiderScan - a Horse / Barn Management System

Post by miax »

Wow, very nice enclosures! Thank you :)

I'm a big fan of Acrylic right now as I have a bunch of it around, but if I were going to build and sell this as a kit - what you suggest is Exactly the kind of enclosure I might use.

Right now I'm doing some deep experimentation with the Wave and Ethernet shields to make them work together well - and if my new plan works out, I'll be able to use the Otter Boxes I've already bought for the RiderScan I put at Misty Brae Farm - It's just too much money to loose!

I'm actually having some success in using the SD card on-board the Ethernet shield versus using the one on the Wave shield. Technically they seem identical (or nearly so) on how they are wired and work, but they are not Quite identical. I just last night got the SD card on the Ethernet shield working with the Wave shield plugged-in so I'm making progress. Should this plan work out, I'll be able to remove the wave shield from the Arduino stack and place it along-side the stack (thus making it small-enough to fit in the Large Otter Box). Either way, I'm not going to give up easily on using Ethernet and Wave together!

Again thanks for the input! :)

Cheers,

Kris

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”