YimFY: Gardening Popsicle

Made by Samuel Lee and Chris Perry

A component to reward personal contributions within a connected gardening environment

Created: May 1st, 2018

0

Description

The gardening popsicle stick integrates a Particle Photon, a Sparkfun Soil Moisture sensor and an RFID scanner in order to connect user contributions toward plant care in a connected communal garden.

0

Parts 

RC522 RFID scanner - $7.98

Particle Photon - $19

Sparkfun Soil Moisture Sensor - $4.95


0

Circuit Diagram

0

Particle Code

Flash the following code to your particle after assembling the circuit shown above. This enables the particle to scan any Mifare RFID chip and displays the UID to console. It also sets up a reward system that incorporates the moisture sensor. Whenever the system detects that a plant has been watered i.e if the moisture level rises above a defined threshold, it publishes an event to the particle cloud which is then published as a tweet to a twitter account via IFTTT. The system can  also easily integrate functionality to request for water if enabled.

0
#include <SPI.h>
#include <MFRC522.h>

/*
 * Project GardeningStick
 * Description:
 * Author:
 * Date:
 */

 #define SS_PIN A2
 #define RST_PIN D0
 #define ID_LEN 4

 #define MOISTURE_IN A0 // pin for reading moisture values, must be analog
 #define MOISTURE_EN D1 // pin for activating the moisture sensor

//RFID globals
MFRC522 rfid(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
byte nuidPICC[ID_LEN] ;



/********************RFID helper functions**************************/
char arr_equal(void *arr1, void *arr2, unsigned len) {
  char *a1 = (char *)arr1 ;
  char *a2 = (char *)arr2 ;
  for (unsigned i =0 ; i < len; i++) {
    if(a1[i] != a2[i]) {
      return 0 ;
    }
  }
  return 1 ;
}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void printHex(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

/**
 * Helper routine to dump a byte array as dec values to Serial.
 */
void printDec(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], DEC);
  }
}

// refresh the nuid buffer
void resetNUID() {
  for(int i = 0; i < ID_LEN ; i++) {
    nuidPICC[i] = 0 ;
  }
}

/***************Helper functions for moisture sensor*************/
#define DRY (0)
#define WET (1)

int soil_state ;
//moisture sensor globals
int moisture_max = 4096 ; // assuming 12 bit ADC input
int moisture_threshold_lo = 200 ;
//int moisture_threshold_hi ;
// int lastMoisture ;

void printSoilState() {
  if(soil_state == DRY) {
    Serial.println(F("Soil is dry")) ;
    return ;
  }
  Serial.println(F("Soil is wet")) ;
}

// returns the moisture level from the sensors
int getMoistureLevel() {
  digitalWrite(MOISTURE_EN, HIGH) ;
  int wait_ms = 10 ;
  delay(wait_ms) ; //delay 10ms
  int moisture_level = analogRead(MOISTURE_IN) ;
  digitalWrite(MOISTURE_EN, LOW) ; // turn off moisture sensor
  return moisture_level ;
}

// returns 1 if curr_moisture is < low threshold specified
unsigned char needWater() {
  int curr_moisture = getMoistureLevel() ;
  return curr_moisture < moisture_threshold_lo ;
}


unsigned char justWatered() {
  int curr_moisture = getMoistureLevel() ;
  if((soil_state == DRY) && (curr_moisture > moisture_threshold_lo)) {
    soil_state = WET ;
    return 1 ;
  }
  return 0 ;
}

void updateSoilState() {
  if(needWater()) {
    soil_state = DRY ;
  }
  else {
    soil_state = WET ;
  }
  return ;
}

void setupMoistureSensor() {
  //setup moisture detector
  pinMode(MOISTURE_IN, INPUT) ;
  pinMode(MOISTURE_EN, OUTPUT) ;
  moisture_max = 880 ; // maximum moisture Analog input value
  delay(10) ;
  updateSoilState() ;
}


/**************Helper functions for sending to twiiter**************/

unsigned long time_of_last_read = 0 ; // can simply start with 0
unsigned long refresh_period = 1000 ;// in milliseconds

// returns 1 if refresh_period expired since last rfid read
unsigned char lastUserExpired() {
  unsigned long elapsed = millis() - time_of_last_read ;
  return elapsed > refresh_period ;
}

// increment social credit
void rewardUser(byte *uid) {
  Serial.print("Rewarding User: ") ;
  printHex(uid, ID_LEN) ;
  Particle.publish("plantWatered", "user1") ;
  Serial.println() ;
}

// post twitter request for water
void requestForWater() {
  Serial.println("Requesting for water") ;
  /* Uncomment the line below to tweet for water 
   * Care should be taken to run this functiion only sporadically to prevent
   * exceeding of twitter's API rate limit
   */
  // Particle.publish("plantDry", "dry") ;
}

// call when same user is detected
void handleSameUser(byte *old_uid) {
  Serial.println("Handling same user") ;
  time_of_last_read = millis() ;
  if(justWatered()) {
    rewardUser(old_uid) ;
  }
}

// call when new user is encountered
void handleNewUser(byte *new_uid) {
  Serial.println("Handling new user") ;
  time_of_last_read = millis() ;
  if(justWatered()) {
    rewardUser(new_uid) ;
  }
}

// resets variables regarding tracking user info and state
void resetUserInfo() {
  resetNUID() ;
  time_of_last_read = millis() ;
}

// returns 1 if uid is a user i,e if uid != [0,0,0,0]
char isUser(byte *uid) {
  for(int i =0; i < ID_LEN; i++) {
    if(uid[i] != 0) {
      return 1 ;
    }
  }
  return 0 ;
}


void setupRewardSystem() {
  resetUserInfo() ;
  // setup code for twitter webhooks
}


/****************Main loop and setup*****************************/
// setup() runs once, when the device is first turned on.
void setup() {
  Serial.begin(9600) ;
  Serial.println("Starting...") ;

  setupMoistureSensor() ;
  setupRewardSystem() ;

  // setup RFID device
  rfid.setSPIConfig() ;
  // SPI.begin() ;
  rfid.PCD_Init() ; // Init MFRC522
  for (char i=0; i < 6; i++) {
    key.keyByte[i] = 0xff ;
  }
  Serial.println(F("This code scans the MIFARE Classsic NUID."));
  Serial.print(F("Using the following key:"));
  printHex(key.keyByte, MFRC522::MF_KEY_SIZE);
}

void loop() {
  // Look for new cards
  if ( ! rfid.PICC_IsNewCardPresent()) {
    // Serial.println("Same Card Detected") ;
    if(!isUser(nuidPICC)) {
      return ;
    }
    else if(lastUserExpired()) { //isUser and user expired
        resetUserInfo() ;
    }
    else if(justWatered()) { //justWatered and isUser
      // to account for watering after the user has left vicinity
      rewardUser(nuidPICC) ;
    }
    return;
  }
  // Verify if the NUID has been read
  if ( ! rfid.PICC_ReadCardSerial()) {
    //Serial.println("Trying to read tag") ;
    return;
  }

  // Serial.print(F("PICC type: "));
  MFRC522::PICC_Type piccType = (MFRC522::PICC_Type)rfid.PICC_GetType(rfid.uid.sak);
  // Serial.println(rfid.PICC_GetTypeName(piccType));

  // Check is the PICC of Classic MIFARE type
  if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI &&
    piccType != MFRC522::PICC_TYPE_MIFARE_1K &&
    piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
    Serial.println(F("Your tag is not of type MIFARE Classic."));
    return;
  }

  // rfid encountered new user
  if(!arr_equal(rfid.uid.uidByte, nuidPICC, ID_LEN)) {
    Serial.println("New card detected") ;
    //store new uid
    for(byte i = 0; i < ID_LEN; i++) {
      nuidPICC[i] = rfid.uid.uidByte[i] ;
    }
    Serial.println(F("The NUID tag is:"));
    Serial.print(F("In hex: "));
    printHex(rfid.uid.uidByte, rfid.uid.size);
    Serial.println();
    //Serial.print(F("In dec: "));
    //printDec(rfid.uid.uidByte, rfid.uid.size);
    //Serial.println();
    handleNewUser(nuidPICC) ;
    printSoilState() ;
    Serial.println(getMoistureLevel());
  }
  else { // same user scanning card
    Serial.println(F("Card read previously."));
    handleSameUser(nuidPICC) ;
  }
  // Halt PICC
  rfid.PICC_HaltA();

  // Stop encryption on PCD
  rfid.PCD_StopCrypto1();

  updateSoilState() ; // checks and updates state of soil
  if(needWater()) {
    requestForWater() ;
  }
}
Click to Expand
0
0
x
Share this Project

Courses

49313 Designing for the Internet of Things (Undergrad)

· 22 members

A hands-on introductory course exploring the Internet of Things and connected product experiences.


About

A component to reward personal contributions within a connected gardening environment