IoT wifi platform

I've been like two days looking for an android app which is wifi based and lets me read and write gpios, but not mqtt based as I don't have a raspberry.
Out of many, I've finally found this great app called Homotica which really stands out : https://github.com/davidevertuani/homotica and I've finally switched from cayenne (it keeps crashing).

There is one problem: the library has this one function called "homotica.attachCallback(myCallback);".
The description in /examples says " //if you need a callback when a pin state is changed, use attachCallback(myCallback) //myCallback must accept int arguments as the example below"

So technically, when one (specified) gpio changes state, a function is called and the code does something.
What happens instead, is that ANY gpio that changes state triggers the function.

This is what the library looks like, maybe you can help me (i've contacted the creator already- but no luck)

/*
 Created by Davide Vertuani, April 28, 2018.
 Released into the public domain.
 Version 2.1
 READ HERE! -------------------------------------
 To ensure compatibility with th ESP8266,
 BEFORE compiling open \libraries\Homotica\Homotica.h
 and make sure the line
   #define ESP
 is NOT commented out.
 If you're using an ethernet shield which needs
 EthernetUDP and SPI, be sure that
   #define ESP
 IS commented out.
 Then compile, you're good to go!
 READ HERE! -------------------------------------
*/

#include <Homotica.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

//MODIFY THESE PARAMETERS
const char* ssid = "yourssid";                             //your wifi ssid
const char* password = "yourpassword";                     //your wifi password

IPAddress ip(192, 168, 1, 20);                             //your arduino IP
IPAddress gateway(192, 168, 1, 1);                         //your network gateway
IPAddress subnet(255, 255, 255, 0);                        //your network subnet mask

unsigned int mUdpPort = 5858;                              //modify here your arduino port
static String code = "xxxxxxxx";                           //modify here your 8-digits auth code

Homotica homotica;

void setup() {
 WiFi.config(ip, gateway, subnet);
 WiFi.begin(ssid, password);

 while (WiFi.status() != WL_CONNECTED) {
   delay(500);
 }

 homotica.addUsedPinOutput(D1);
 homotica.addUsedPinOutput(D2);
 //for each pin you'se using as output (i.e. to control devices),
 //call addUsedPinOutput(pin) on it, so that homotica knows it's being used
 //and can configure it high or low depending on your configuration
 //ALWAYS call homotica.addUsedPinOutput(pin) BEFORE calling homotica.set(port, code)

 homotica.addUsedPinInput(D3);
 homotica.addUsedPinInput(A0);
 //for each pin you'se using as input (i.e. to read pin state),
 //call addUsedPinInput(pin) on it, so that homotica knows it's being used
 //and can configure it as input
 //ALWAYS call homotica.addUsedPinInput(pin) BEFORE calling homotica.set(port, code)

 homotica.attachInputFunction(myCustomInputFunction, 'M');
 //if you need to precess some data before feeding it to the homotica app
 //(like if you're reading a sensor via I2C) attach an Input Function and
 //identify it with a char (now available: )
 //in the app select "Custom channel", will call the fuction instead of radind
 //directly from the GPIO

 homotica.setActiveLow();
 //delete the line if you're using active_high setup
 //ALWAYS call homotica.setActiveLow() BEFORE calling homotica.set(port, code)

 homotica.set(mUdpPort, code);
 //homotica.set(mUdpPort, code) sets up the udp connection, MUST be called in setup()

 //if you need to turn on/off your devices without worring about HIGH and LOW
 //in Active High or low application, use the following fuctions

 homotica.push(D1, 1000);
 //pushes pin D1 for 1000 milliseconds
 //i.e. turns it on, waits 1 second, then turns it off

 homotica.turnOn(D2);
 //turns on pin D2

 homotica.turnOff(D2);
 //turns off pin D2

 homotica.toggle(D2);
 //toggles pin D2 i.e. changes it state

 homotica.attachCallback(myCallback);
 //if you need a callback when a pin state is changed, use attachCallback(myCallback)
 //myCallback must accept int arguments as the example below
}

void loop() {
 homotica.refresh();
}

void myCallback(int usedPin){
 //do stuff
}

int16_t myCustomInputFunction(){
 //read sensors or do stuff
 int16_t returnValue = 1234;
 return returnValue;
 
 //if an error has occurred - like the sensor didn't respond - you could
 //refuse to return a valid value; in this case use:
 //return homotica.SENSOR_ERROR;
 //which will generate a negative response to the app.
 
 //NOTE: the method does not accept negative values
}
 davidevertuani / homotica
Code  Issues 0  Pull requests 0  Projects 0  Pulse
Homotica/Homotica.h

/*
	Homotica.h - Library for Homotica Android app
	Created by Davide Vertuani, April 28, 2018.
	Released into the public domain.
	Version 2.0
*/
#ifndef Homotica_h
#define Homotica_h
#include "Arduino.h"
#include <inttypes.h>
#include "Data.h"
#include "InputFunction.h"

#define ESP			//COMMENT OUT THIS LINE TO USE AN ARDUINO ETHERNET SHIELD

#ifdef ESP
#include <WiFiUdp.h>
#else
#include <SPI.h>
#include <EthernetUdp.h>
#endif

#define MAX_ITEMS (15)
#define NOTHING_AVAILABLE (-1)

class Homotica{
public:
	Homotica(void);

	const int8_t SENSOR_ERROR = -1;

	void refresh(void);
	void set(unsigned int local_port, String code);
	void setActiveLow(void);
	void addUsedPinInput(unsigned int pin);
	void addUsedPinOutput(unsigned int pin);
	void push(int pin, int duration);
	void turnOn(int pin);
	void turnOff(int pin);
	void toggle(int pin);
	void attachCallback(void(&)(int));

	int8_t attachInputFunction(int16_t (&)(void), char code);

protected:
	Data _datas[MAX_ITEMS];
	InputFunction _inputFunction[MAX_ITEMS];

	int8_t findSuitableDataIndex(void);
	int8_t findSuitableInputFunctionIndex(void);

private:
	#ifdef ESP
	WiFiUDP Udp;
	#else
	EthernetUDP Udp;
	#endif

	int PIN_INDEX_INPUT = 0;
	int PIN_INDEX_OUTPUT = 0;

	int _usedPinsInput[20] = {};
	int _usedPinsOutput[20] = {};

	int _MLOW = 0x0;
	int _MHIGH = 0x1;

	void processMultipleRunnable(String msg);
	void processSingleRunnable(String msg);
	void checkUDP();
	void refreshDatas();
	void simulateUdp(String incoming_string);
	char* formatResponse(int responseCode, uint16_t input);
	char* formatResponse(int responseCode);
	int8_t pushPin(uint8_t pin, unsigned long duration, uint8_t startingValue);
	void(*_functionPointer)(int);

	bool _hasCallback = false;
	String _code;
	unsigned int _localport;
	char _packetBuffer[200];

	const int _negativeResponse = 0;
	const int _positiveResponse = 1;

	char _response[7];
};
#endif
 Desktop version
 davidevertuani / homotica
Code  Issues 0  Pull requests 0  Projects 0  Pulse
Homotica/Homotica.cpp

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

#include "Homotica.h"

Homotica::Homotica(void){}

void Homotica::refresh(void)
{
 checkUDP();
 refreshDatas();
}

void Homotica::attachCallback(void (&functionPointer)(int)){
 _functionPointer = functionPointer;
 _hasCallback = true;
}

int8_t Homotica::attachInputFunction(int16_t (&functionPointer)(void), char code){
 int8_t i = findSuitableInputFunctionIndex();

 if (i == NOTHING_AVAILABLE) return NOTHING_AVAILABLE;

 _inputFunction[i].isUsed = USED_DATA;
 _inputFunction[i].code = code;
 _inputFunction[i].inputFunctionPointer = functionPointer;

 return i;
}

int8_t Homotica::findSuitableInputFunctionIndex(void){
 for (int8_t i = 0; i < MAX_ITEMS; i++) {
 if (_inputFunction[i].isUsed == EMPTY_DATA) return i;
 }
 return NOTHING_AVAILABLE;
}

void Homotica::refreshDatas(void){
 for (int8_t i = 0; i < MAX_ITEMS; i++)
 {
 if (_datas[i].isUsed == USED_DATA)
 {
 _datas[i].refresh();
 }
 }
}

void Homotica::set(unsigned int local_port, String code){
 _code = code;
 _localport = local_port;
 Udp.begin(_localport);
#ifndef ESP
 SPI.begin();
#endif
 for (int i = 0; i < PIN_INDEX_OUTPUT; i++) {
 pinMode(_usedPinsOutput[i], OUTPUT);
 digitalWrite(_usedPinsOutput[i], _MLOW);
 }
 for (int i = 0; i < PIN_INDEX_INPUT; i++) {
 pinMode(_usedPinsInput[i], INPUT);
 }
}

void Homotica::setActiveLow(){
 _MLOW = 0x1;
 _MHIGH = 0x0;
}

void Homotica::addUsedPinOutput(unsigned int pin){
 _usedPinsOutput[PIN_INDEX_OUTPUT] = pin;
 PIN_INDEX_OUTPUT += 1;
}

void Homotica::addUsedPinInput(unsigned int pin){
 _usedPinsInput[PIN_INDEX_INPUT] = pin;
 PIN_INDEX_INPUT += 1;
}

void Homotica::push(int pin, int duration){
 pushPin(pin, duration, _MLOW);
}

void Homotica::turnOn(int pin){
 digitalWrite(pin, _MHIGH);
}

void Homotica::turnOff(int pin){
 digitalWrite(pin, _MLOW);
}

void Homotica::toggle(int pin){
 digitalWrite(pin, !digitalRead(pin));
}

void Homotica::simulateUdp(String incoming_string){
 processSingleRunnable(incoming_string);
}

void Homotica::checkUDP(void) {
 int packetSize = Udp.parsePacket();

 if (packetSize) {
 Udp.read(_packetBuffer, sizeof(_packetBuffer));
 String msg = _packetBuffer;

 Serial.println(msg);

 int andIndex = msg.indexOf("?");
 int eqIndex = msg.indexOf("=");

 String recivedCode = msg.substring(eqIndex + 1, msg.indexOf("!"));
 String request = msg.substring(andIndex + 1, eqIndex);

 if (recivedCode == _code) {
 String mSubstring = msg.substring(msg.indexOf("!"));
 Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());

 if (request == "QA" || request == "QD") {
 int start = mSubstring.indexOf("!");
 int stop = mSubstring.indexOf("!", start + 1);

 uint8_t PIN = mSubstring.substring(start + 1, stop).toInt();
 uint16_t sensorReading;

 if (request == "QA") sensorReading = analogRead(PIN);
 else sensorReading = digitalRead(PIN);

 Udp.write(formatResponse(_positiveResponse, sensorReading));
 Udp.endPacket();
 }
 else if (request == "QC") {
 int start = mSubstring.indexOf("!");
 int stop = mSubstring.indexOf("!", start + 1);

 int16_t response = 0;
 char code = mSubstring.substring(start + 1, stop)[0];

 bool succeded = false;
 for (int8_t i = 0; i < MAX_ITEMS; i++){
 if (_inputFunction[i].isUsed == USED_DATA){
 if(_inputFunction[i].code == code) {
 response = _inputFunction[i].getValue();
 if (response > 0) succeded = true;
 }}
 }

 if (succeded) Udp.write(formatResponse(_positiveResponse, response));
 if (!succeded) Udp.write(formatResponse(_negativeResponse));
 Udp.endPacket();
 }

 else if (request == "ST") {
 Udp.write(formatResponse(_positiveResponse));
 Udp.endPacket();

 processSingleRunnable(mSubstring);
 }
 else if (request == "MT") {
 Udp.write(formatResponse(_positiveResponse));
 Udp.endPacket();

 processMultipleRunnable(mSubstring);
 }
 else if (request == "CH") {
 Udp.write(formatResponse(_positiveResponse));
 Udp.endPacket();
 }
 else {
 Udp.write(formatResponse(_negativeResponse));
 Udp.endPacket();
 }
 }
 for (int i = 0; i < sizeof(_packetBuffer); ++i) {
 _packetBuffer[i] = (char)0;
 }
 }
 delay(10);
}

void Homotica::processSingleRunnable(String msg) {
 boolean accepted = false;
 int charsIndex[4];

 charsIndex[0] = msg.indexOf("!");
 charsIndex[1] = msg.indexOf("!", charsIndex[0] + 1);
 charsIndex[2] = msg.indexOf("!", charsIndex[1] + 1);
 charsIndex[3] = msg.indexOf("!", charsIndex[2] + 1);

 int PIN = msg.substring(charsIndex[0] + 1, charsIndex[1]).toInt();
 int relayMode = msg.substring(charsIndex[1] + 1, charsIndex[2]).toInt();
 int del = msg.substring(charsIndex[2] + 1, charsIndex[3]).toInt();

 if (_hasCallback){ _functionPointer(PIN); }
 if (relayMode == 0) digitalWrite(PIN, _MHIGH);
 else if (relayMode == 1) digitalWrite(PIN, _MLOW);
 else if (relayMode == 2) pushPin(PIN, del, _MLOW);
 else if (relayMode == 3) digitalWrite(PIN, !digitalRead(PIN));
}

void Homotica::processMultipleRunnable(String msg) {
 char input[msg.length() + 1];
 const char* divider = ("+");
 int lastIndex = 0;

 msg.toCharArray(input, msg.length() + 1);

 for (int k = 0; k < sizeof(input); k++) {
 if (String(input[k]) == "+") {
 processSingleRunnable(msg.substring(lastIndex, k));
 k += 1;
 lastIndex = k;
 }
 }
}

int8_t Homotica::pushPin(uint8_t pin, unsigned long duration, uint8_t startingValue){
 int8_t i = findSuitableDataIndex();

 if (i == NOTHING_AVAILABLE) return NOTHING_AVAILABLE;

 _datas[i].isUsed = USED_DATA;
 _datas[i].pin = pin;
 _datas[i].duration = duration;
 _datas[i].startingValue = !startingValue;
 _datas[i].creationTime = millis();

 digitalWrite(pin, !startingValue);
 return i;
}

int8_t Homotica::findSuitableDataIndex(void){
 for (int8_t i = 0; i < MAX_ITEMS; i++) {
 if (_datas[i].isUsed == EMPTY_DATA) return i;
 }
 return NOTHING_AVAILABLE;
}

char* Homotica::formatResponse(int responseCode){
 return formatResponse(responseCode, 0);
}

char* Homotica::formatResponse(int responseCode, uint16_t input){
 snprintf(_response, sizeof(_response), "%d%05d", responseCode, input);
 return _response;
}
 Desktop version

Thanks for going through it. As you see, it's called twice in the code. I wonder if the second time, is a state that executes a list of instructions

My bad.

Ok the instruction says:"/if you need a callback when a pin state is changed, use attachCallback(myCallback) "

In the function i see arguments being passed. So I expect to be able to specify a pin.

"void myCallback(int usedPin)"

I expect it to do what it says it does...

Thanks!

I tried to implement it, but it didn't work.
This is what i did:

void myCallback(int usedPin){
  if(usedPin == D5){
        Serial.print("Pin D5 changed state");
        delay(1000);
        digitalWrite(D5, HIGH);
    }
     
    else {
        Serial.print("It was a pin I don't care about");
    }
}

What is weird, is that I do get "Pin D5 changed state" if the gate opens and "It was a pin I don't care about" in other cases, but the pin doesn't toggle back to the original state.

If I set it to "digitalWrite(D5, LOW);", instead, I cannot even toggle it with my smartphone. Which is strange, because the gate stays closed with HIGH.

Atm this timer does the fix

void setup ()

    timer.setInterval(2000L, gateoff);

void loop()


void gateoff()
{
  // read the state of the pushbutton value:
   buttonstatedoor = digitalRead(D5);
   //The button is pushed
    if (buttonstatedoor == LOW) 
     {
       Serial.println("Close DA GATE");
       delay (500);
       digitalWrite(D5, HIGH);
        
     }
     
   
}