Integrating Blynk With Custom Classes

I am currently working on a decently sized project using 4 custom classes, one of which needs to integrate blynk functions. The way I want it to work is as follows:

  1. rfid card is read using a function in my RF_Admin class to read a card’s uid and creates a user (one of my other custom classes).
  2. using BLYNK_WRITE(V0) and the textbox input widget, wait for the user to type in a name to go with the card’s uid.
  3. when a name is entered, that name is paired with the uid to make a new user

When I can get this part working it will be used in a function called RF_Admin::masterMode after the master tag is read to allow putting new users in the database.

The closest I’ve gotten is my attempt where I declare a String variable to hold blynk input string and the BLYNK_WRITE function in the RF_Admin.h file’s public space.

Here is the pertinent code in my project:

RF_Admin.h:

public:
//...
void getBlynkName(LiquidCrystal_I2C&, User&);
//...
// compare and search id's
  bool cmpUID(LiquidCrystal_I2C&, byte*, byte*);
  User lookupID(LiquidCrystal_I2C&, byte*);
//...
String newStr;

  BLYNK_WRITE(V0)
  {
    newStr = param.asStr();
  }

private:
  User master;
  User owner;
  User guests[MAX_GUESTS];
  int count;
};

RF_Admin.cpp:

void RF_Admin::getBlynkName(LiquidCrystal_I2C& lcd, User& usr)
{
  String oldStr = newStr;
  String fn, ln;
  int spaceIndex;
  int count = 0;
  Blynk.config(AUTH);
  bool blynkConnected = Blynk.connect();
  while( (newStr == oldStr) )
  {
    Blynk.run();
    if( count == 0 )
    {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Please enter a");
      lcd.setCursor(0,1);
      lcd.print("name in blynk");
    }
    count++;
  }
  usr.setName(newStr);
}

//...

bool RF_Admin::cmpUID(LiquidCrystal_I2C& lcd, byte* uid1, byte* uid2)
{
  for( byte x = 0; x < UID_LENGTH; x++ )
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("i=");
    lcd.print(x);
    lcd.print(" u1:");
    lcd.print(uid1[x]);
    lcd.setCursor(4,1);
    lcd.print("u2:");
    lcd.print(uid2[x]);
    delay(600);
    // check each byte of uid1 against uid2
    if( uid1[x] != uid2[x] )
    {
      return false;     // not the same card
    }
  }
  return true;
}

User RF_Admin::lookupID(LiquidCrystal_I2C& lcd, byte* uid)
{
  bool isMaster = true, isOwner = true, isGuest = false;

  // check if master card
  isMaster = cmpUID(lcd, uid, master.getUID());
  
  if(isMaster) return getMaster();

  // check if owner
  isOwner = cmpUID(lcd, uid, owner.getUID());
  if( isOwner ) return getOwner();

  // check against each set guest id in guests array
  for( int i = 0; (i < MAX_GUESTS) && (guests[i].getIsSet()); i++ )
  {
    isGuest = cmpUID(lcd, uid, getGuest(i).getUID());
    if( isGuest ) return getGuest(i);
  }
  return User("new card", uid, 0);
}

User.h:

/* File: User.h
 * Author: Michael
 * Date: 1/19/2019
 * This class contains User rfid information and an EEPROM mem locaiton to be used
 * with an rfid user database.  This class also contains functions used to manipulate
 * and compare User information
 */

#ifndef USER_H
#define USER_H

#if (ARDUINO >= 100)
  #include "Arduino.h"
#else
  #include "WProgram.h"
#endif

#define MAX_NAME_LEN 28
#define UID_LENGTH 10
#define TID_LENGTH 3
#define MEM_START_L 100

class User
{
public:
  // ctors
  User();
  User(String, byte*, int);

  // get/set functions
  String getName() const { return name; }
  void setName( String s ) { name = s; }
  byte* getUID() const { return uid; }
  void setUID(byte* u) { for( int i = 0; i < UID_LENGTH; i++ ) uid[i] = u[i]; }
  int getLocation() const { return location; }
  void setLocation(int l) { location = l; }
  bool getIsSet() const { return isSet; }
  void setIsSet(bool b) { isSet = b; }

  // utility get functions for tid (first 3 bytes of uid)
  byte getByte(byte x) const { return getUID()[x]; }
  byte* getTID() const;

  //overloaded '=' operator
  User& operator=(const User&);

  // overloaded '==' operator
  friend bool operator==(const User&, const User&);

  // overloaded '!=' operator
  friend bool operator!=(const User& u1, const User& u2) { return !(u1 == u2); }
  
private:
  String name;
  byte uid[UID_LENGTH];
  int location;
  bool isSet;
};

#endif

Door_Unlocker_Pt4.ino:

#include <SparkFun_UHF_RFID_Reader.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include <BlynkSimpleWiFiNINA.h>
#include <LiquidCrystal_I2C.h>

#include "Robot.h"
#include "RF_Admin.h"
#include "User.h"

LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display
RFID nano;                          // create uhf rfid object
SoftwareSerial softSerial(2, 3);    //RX, TX

int potVal, prevPotVal, buttonState;
int pot = A0;
int i = 0, j = 0;
int s[4] = {2, 0, 1, 3};
int curAngle, curI, rCount;
ClickButton btn(4, LOW, CLICKBTN_PULLUP);
Position curPos;
unsigned long t;
User tmpUsr;
BlynkWifiCommon Blynk(_blynkTransport);

char auth[] = "36xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "xxxx";
char pass[] = "xxxxxxxxxxxx";

RF_Admin database;

Robot myRobot;

void setup()
{
  database.init(lcd, nano, softSerial, 1000);
  btn.Update();
  lcd.init();
  lcd.backlight();
  myRobot.init(lcd, curPos);

  Serial.begin(115200);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("sizeof(rfa):");
  lcd.print(sizeof(RF_Admin));

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("step:2");

  WiFi.begin(ssid, pass);
  Blynk.config(auth);
}

void loop() {
  Blynk.run();
  btn.Update();
  t = millis();

  // every 6 seconds for 10 milliseconds start read for tags
  if ( (t % 6000) < 10)
  {
    tmpUsr = database.readCard(lcd, nano);
//    if( tmpUsr != User() ) database.printUsr(lcd, tmpUsr);
    if( tmpUsr != User() ) database.getBlynkName(lcd, tmpUsr);
//    if( tmpUsr == database.getMaster() ) database.masterMode(lcd, nano, btn);
    
//    if( tmpUsr == database.getOwner() )
//    {
//      myRobot.goToFinal(curPos, lcd);
//      myRobot.goToHome(curPos, lcd);
//    }
  }
  if (btn.clicks == -5) myRobot.startTeachMode(btn, lcd, curPos, pot);
}

What I end up with is blynk connected (i think) to the server, but when I enter a name in blynk it doesn’t do anything in the program. I think this has something to do with the blynk_write(v0) function being defined in the header file instead of the cpp file where the blynk.run() function is. I am using the BlynkWifiCommon Blynk(_blynkTransport); work-around to allow for multiple includes of the same library. Any help would be much appreciated. And please forgive me if I formatted the code wrong as this is my first time posting code to a forum.

I moved your post into your own current topic. Edit the title as needed. Thanks.

@Gunner Should I post all of the code for each relevant file or leave it as is?

I don’t know exactly what you are looking to accomplish, so I have no answer on that. But this is your topic, so post whatever you feel might elicit some answers.

Be aware though that most assistance is for more basic Blynk learning, not necessarily “structural” reworkings of how the library works. Besides, The developers are working on Blynk 2.0 so who knows what that may involve for library changes.

I basically want to use blink_write and the txt input widget on app for entering names in the middle of a custom class function. But if by this you meant you haven’t looked through my post then that’s fair.

I hope they make using blynk in c++ custom classes easier in 2.0 but somehow I doubt it. I could be wrong though. Thanks for getting back to me though.

I have glanced through it… However, I personally don’t currently have any use for custom classes, so I don’t use them, thus have nothing much to contribute. The closest I think I have worked with something along that line is here… but not probably not the same “custom class” as what you are doing.

Gotcha. Alright well thanks anyways.

I did something similar using the HTTP API blynkapi · Apiary and https://github.com/blynkkk/blynk-library/blob/master/examples/Boards_With_HTTP_API/ESP8266/ESP8266.ino

I had a quick look to your code and you’re declaring String newStr; twice, without thinking to much I would strat from here.

Thanks for that. That’s been the main hitch in compiling when I was changing declaration locations. I forgot to undo that one before copying and pasting to this post. Sorry I’ll change my main post to reflect what it actually was when it compiled but didn’t accept the new string from the text input blink widget at runtime.

Ok, how about you move your BLYNK_WRITE(V0) to the main Door_Unlocker_Pt4.ino and declare the String newStr; as static? not sure, just guessing.

I can actually do without the ino ‘blynk.run()’ if I can have it function within the while loop of my getBlynkName function. I don’t think it can work the way I’m going for with the BLYNK_WRITE function within the ino since the getBlynkName function isn’t in the ino and newStr needs to be accessible by BLYNK_WRITE and the getBlynkName. It may boil down to an issue of scope with newStr and possibly BLYNK_WRITE.

Thus the suggestion for the Static variable.

What does the debug shows? Does it connect? Disconnect? Times out…

Sorry, I didn’t see that part. I didn’t actually know what static did until I looked it up just now and that sounds like what I want for newStr.

It shows in app that it is connected, but the BLYNK_WRITE function doesn’t execute (I think) because it is in the .h file and not the .cpp file of RF_Admin. I’ll add debugging mode though and let you know what it says.

1 Like
[56004] 
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.5.4 on Arduino

[56017] Connecting to blynk-cloud.com:80
[56079] <[02|00|01|00] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[56395] >[00|00|01|00|C8]
[56397] Ready (ping: 312ms).
[56399] Free RAM: 4129
[56402] >[14|00|01|00|08]
[56406] >pm[00]2[00]out
[56465] <[11|00|02|00]Tver[00]0.5.4[00]h-beat[00]10[00]buff-in[00]256[00]dev[00]Arduino[00]con[00]WiFiNINA[00]build[00]Jan 30 2019 15:55:56[00]
[56704] >[00|00|02|00|C8]
[66481] <[06|00|03|00|00]
[66571] >[00|00|03|00|C8]
[74897] >[14|00|18|00|0A]
[74900] >vw[00]0[00]Owner
[76488] <[06|00|04|00|00]
[76747] >[00|00|04|00|C8]
[86495] <[06|00|05|00|00]
[86562] >[00|00|05|00|C8]
[96501] <[06|00|06|00|00]
[96560] >[00|00|06|00|C8]
[106507] <[06|00|07|00|00]
[106562] >[00|00|07|00|C8]
[116512] <[06|00|08|00|00]
[116569] >[00|00|08|00|C8]
[124867] >[14|00|19|00|0A]
[124870] >vw[00]0[00]Owner
[126517] <[06|00|09|00|00]
[126716] >[00|00|09|00|C8]
[136524] <[06|00|0A|00|00]
[136583] >[00|00|0A|00|C8]
[146530] <[06|00|0B|00|00]
[146586] >[00|00|0B|00|C8]

The lcd at this point should show me that newStr is now different than oldStr but it stays in the loop since newStr never gets changed. I couldn’t get it to compile using static since i use it as the control variable in the loop of getBlynkName. I tried making it static in its current spot, removing it and BLYNK_WRITE from the .h file, and moving BLYNK_WRITE to the .cpp file and putting static String newStr = param.asStr(); into BLYNK_WRITE and get the error

RF_Admin.cpp:250:19: error: 'newStr' was not declared in this scope

  String oldStr = newStr;

                  ^

Based on your comments I don’t believe you used Static right, I would declare it on your .h file and you need to use it like this RF_Admin::new_string but again I’m not sure if this will work

Further I would suggest to include some Serial.print on your code to show you where you’re and the variable status, would help you to debug.

Ah…that makes sense. I’ll try that when I get home tonight

You were correct. I had to move BLYNK_WRITE to RF_Admin.cpp, put static String newStr; in RF_Admin.h in the public space of the class, re-declare outside a function with String RF_Admin::newStr; in RF_Admin.cpp, and use RF_Admin::newStr to reference it within functions in the RF_Admin.cpp file. Unfortunately I can’t test if the getBlynkName function works until I figure out why all of the sudden my program breaks before getting to the function when using WiFi.begin(ssid, auth);…I’ll post again when I have that issue solved.

I know you’re not using GSM but here it’s explained Blynk.config

Also, I’m not sure if you need String RF_Admin::newStr;

Blynk callback functions must be defined at the main scope, try first to call any Blynk function from where you are defining BLYNK_WRITE like Blynk.connect() , if you got a compilation error ‘Blynk not defined in this scope’ ; so Blink Api has no idea about your implementation of BLYNK_WRITE() to call it.