Vai al contenuto

Gestire dispositivo Sonoff DIY da SlimLine

Il modulo Sonoff della Itead è un relè WiFi dal costo contenuto, può essere collegato direttamente a 220V permettendo di alimentare sia il modulo che il carico pilotato dal relè. Nelle ultime versioni del software la Itead ha introdotto la modalità DIY che ci permette di gestire i dispositivi della famiglia Sonoff senza transitare dal cloud eWeLink comandandoli in locale con connessione WiFi utilizzando le REST API.

Per attivare la modalità DIY occorre aprire il dispositivo ed inserire il ponticello nei due pin esposti.

Immagine modulo SonOff basic

Configurazione dispositivo

Ora occorre attivare sullo smartphone la modalità hotspot (Trasformando il telefono in un router WiFi) creando una rete WiFi con SSID sonoffDiy e con password 20170618sn (Attenzione alle lettere maiuscole e minuscole). Alla accensione il dispositivo si connetterà immediatamente alla rete WiFi creata, a conferma dell'avvenuta connessione il led blu lampeggerà 2 volte consecutive continuamente.

Ora utilizzando un PC Windows scaricare ed installare il Tool 01DIY85(v3.3.0).exe (In Internet troverete svariati link da dove scaricarlo). Installato il programma si dovrà connettere il PC alla stessa rete WiFi dell'hotspot creato con il cellulare.

Eseguendo il tool ci si troverà una schermata come quella riportata in basso, da dove è possibile configurare il dispositivo. Nella prima casella bordata in rosso compare l'identificativo del dispositivo, è importante annotarsi questo numero perchè servirà per l'accesso al dispositivo.

Selezionando il dispositivo ed agendo sul tasto Change SSID Password è possibile configurare la rete WiFi a cui connetterlo. Indicando i parametri della propria rete WiFi il dispositivo si sconnetterà dall'hotspot e si connetterà alla rete definita. Ora sarà possibile riconnettere alla stessa rete anche il PC e se tutto è corretto nel programma comparirà nuovamente il dispositivo e sarà possibile comandarne il relè e configurarlo.

Purtroppo il programma non ci dà alcuna informazione sull'indirizzo IP assegnato al dispositivo, per individuarlo ho eseguito una scansione della rete.

Screenshot DIY mode tool

Invio comandi REST

A questo punto è possibile testare il funzionamento inviando direttamente i comandi REST dal browser Chrome, per fare questo occorre installare l'App Advanced REST client che permette di gestire l'invio di comandi HTTP in modalità POST. La descrizione dei comandi REST è disponibile in file pdf dal repository del fornitore.  Per inviare i comandi occorre indicare l'ID dispositivo così come visualizzata nel DIY Tool.

Tutti comandi e le risposte sono in formato JSON, non stò ad elencare tutti i possibili comandi, riporto solo i comandi per attivare/disattivare il relè. Inviando in POST all'indirizzo http://xxx.xxx.xxx.xxx:8081/zeroconf/switch la stringa {"deviceid": "10***4**d4", "data":{"switch": "on"}} attiveremo il relè, inviando la stringa {"deviceid": "10***4**d4", "data":{"switch": "off"}} disattiveremo il relè.

Esempi

Come utilizzare gli esempi.

ST_SonOffDIY: Viene gestito un dispositivo SonOff utilizzando le API REST, è possibile da debug agire sulle variabili indicate per comandare il relè sul dispositivo. Ogni 10 secondi il programma invia il comando REST di lettura stato ed acquisisce lo stato del relè.

SonOffByFB: Come nel programma precedente viene gestito un dispositivo SonOff utilizzando le API REST, a gestione è realizzata tramite un blocco funzione (Di cui è fornito il programma sorgente). Il FB è realizzato in modo da permetterne la gestione in cascata per poter gestire più dispositivi SonOff.

LogicLab (Ptp172)
PROGRAM ST_SonOffDIY
VAR
    i : UDINT; (* Auxiliary variable *)
    SpyOn : BOOL; (* Attiva  spionaggio *)
    RCommand : BOOL; (* Relay command *)
    RStatus : BOOL; (* Relay status *)
    DOk : BOOL; (* Device Ok *)
    Sequence : UDINT; (* Sequence letta dal dispositivo *)
    ErrorNr : UDINT; (* Error number *)
    RCPulse : BOOL; (* Relay command pulse *)
    DError : UDINT; (* Device error *)
    Errors : UDINT; (* Service errors *)
    PSent : UDINT; (* Packets sent *)
    HTTPRq : HTTPClient_v1; (* HTTP client *)
    JDecode : JSONDecode_v2; (* JSON decode *)
    CaseNr : USINT; (* Case gestione *)
    Result : STRING[ 256 ]; (* Result string *)
    Request : STRING[ 64 ]; (* Request string *)
    TimeBf : UDINT; (* Time buffer (uS) *)
    Ptr : ARRAY[0..2] OF @BYTE; (* Auxiliary pointer *)
    RValue : STRING[ 8 ]; (* Relay value da JSON *)
    JData : STRING[ 256 ]; (* JSON data received *)
    DAddress : @STRING; (* Device address *)
    DeviceID : @STRING; (* Device ID *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_SonOffDIY"
// *****************************************************************************
// Viene gestito un dispositivo SonOff in modalità DIY.
//
// Comandi gestibili da debug.
// SpyOn: BOOL, Attiva  spionaggio
// RCommand: BOOL, Relay command
//
// Stati gestibili da debug.
// DOk: BOOL, Device Ok
// RStatus: BOOL, Relay status
// DError: UDINT, Device error
// PSent: UDINT, Packets sent
// Errors: UDINT, Service errors
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INIZIALIZZAZIONI
    // -------------------------------------------------------------------------
    // Eseguo inizializzazione.

    IF (SysFirstLoop) THEN
    
        // Parametrizzo FB HTTPClient.

        HTTPRq.SpyOn:=SpyOn; //Activate the spy
        HTTPRq.Method:=1; //Request method, POST
        HTTPRq.HostAddress:=ADR('192.168.1.38'); //Indirizzo dispositivo
        HTTPRq.HostName:=HTTPRq.HostAddress; //Hostname
        HTTPRq.HostPort:=8081; //Server port
        HTTPRq.Request:=ADR(Request); //Request string
        HTTPRq.DBSize:=256; //JData buffer size
        HTTPRq.Timeout:=2000; //Timeout time (mS)

        // Definizione parametri.
        
        DeviceID:=ADR('1000a456d4'); //Device ID
    END_IF;

    // -------------------------------------------------------------------------
    // ESEGUO GESTIONE ERRORE
    // -------------------------------------------------------------------------
    // Gestione  errore di esecuzione.

    IF (ErrorNr <> 0) THEN
        IF (SpyOn) THEN
            IF NOT(SysSpyData(0, 0, 0, 0)) THEN RETURN; END_IF;
            i:=SysVarsnprintf(ADR(Result), SIZEOF(Result), 'Error:%08d', UDINT_TYPE, ADR(ErrorNr));
            i:=SysLWVarsnprintf(ADR(Result), SIZEOF(Result), ', On Case:%d', USINT_TYPE, ADR(CaseNr));
            i:=SysSpyData(SPY_ASCII, 16#40000000, ADR('Er'), ADR(Result));
        END_IF;

        // Incremento counter errori.

        Errors:=Errors+1; //Service errors
        ErrorNr:=0; //Error number
        DOk:=FALSE; //Device Ok
        CaseNr:=0; //Case gestione
        RETURN;
     END_IF;

    // -------------------------------------------------------------------------
    // ESEGUO GESTIONE HTTP CLIENT
    // -------------------------------------------------------------------------
    // Eseguo gestione HTTP client e controllo se errore.

    HTTPRq(); //HTTP client
    IF (HTTPRq.Fault) THEN HTTPRq.Enable:=FALSE; ErrorNr:=200; RETURN; END_IF;

    // Acquisizione dati ricevuti, li trasferisco in stringa.

    IF ((HTTPRq.HPSelector) AND (HTTPRq.DBChars <> 0)) THEN
        IF ((Sysstrlen(ADR(Result))+HTTPRq.DBChars) < SIZEOF(Result)) THEN
            i:=Sysmemmove(ADR(Result)+Sysstrlen(ADR(Result)), HTTPRq.DBAddress, HTTPRq.DBChars);
        END_IF;
    END_IF;

    // -------------------------------------------------------------------------
    // GESTIONE SEQUENZE
    // -------------------------------------------------------------------------
    // Case gestione sequenze programma.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // INIT GESTIONE
        // ---------------------------------------------------------------------
        // Controllo se variazione comando relè.

        0:
        HTTPRq.Enable:=FALSE; //HTTP get page enable
        IF (RCommand <> RCPulse) THEN
            RCPulse:=RCommand; //Relay command pulse
            PSent:=PSent+1; //Packets sent
            CaseNr:=10; //Case gestione
            RETURN;
        END_IF;

        // Controllo timeout controllo informazioni.

        IF ((SysGetSysTime(TRUE)-TimeBf) > 10000000) THEN
            PSent:=PSent+1; //Packets sent
            CaseNr:=20; //Case gestione
            RETURN;
        END_IF;

        // ---------------------------------------------------------------------
        // INVIO COMANDO RELE'
        // ---------------------------------------------------------------------
        // Richiesta informazioni.
        // http://xxx.xxx.xxx.xxx:8081/zeroconf/switch
        // POST: {"deviceid": "nnnnnnnn", "JData":{"switch": "on"}}

        10:
        i:=Sysmemset(ADR(Request), 0, SIZEOF(Request)); //Empty request
        i:=Sysmemset(ADR(Result), 0, SIZEOF(Result)); //Empty result
        
        IF NOT(RCommand) THEN RValue:='off'; ELSE RValue:='on'; END_IF;
        
        i:=SysVarsnprintf(ADR(Request), SIZEOF(Request), '{"deviceid": "%s",', STRING_TYPE, DeviceID);
        i:=SysLWVarsnprintf(ADR(Request), SIZEOF(Request), '"JData": {"switch": "%s"}}', STRING_TYPE, ADR(RValue));

        HTTPRq.Page:=ADR('zeroconf/switch'); //Web page
         HTTPRq.Enable:=TRUE; //HTTP get page enable
        CaseNr:=CaseNr+1; //Case gestione

        // ---------------------------------------------------------------------
        // Se fine esecuzione controllo stato ritornato. Il numero di sequenza
        // si incrementa ad ogni invio di comando.
        // {"seq": 13, "error": 0}

        11:
        IF NOT(HTTPRq.Done) THEN RETURN; END_IF;
        HTTPRq.Enable:=FALSE; //HTTP get page enable

        // Controllo se ricevuto risposta da dispositivo.
        // Decodifico valori da stringa JSON ricevuta.

        IF NOT(HTTPRq.PLoad) THEN ErrorNr:=1000; RETURN; END_IF;

        JDecode(Object:=ADR(Result), Name:=ADR('seq'), VType:=UDINT_TYPE, VAddress:=ADR(Sequence), Count:=1);
        IF (JDecode.ECode <> 0) THEN ErrorNr:=1001; RETURN; END_IF;

        JDecode(Object:=ADR(Result), Name:=ADR('error'), VType:=UDINT_TYPE, VAddress:=ADR(DError), Count:=1);
        IF (JDecode.ECode <> 0) THEN ErrorNr:=1002; RETURN; END_IF;
        CaseNr:=20; //Case gestione (Eseguo lettura informazioni)

        // ---------------------------------------------------------------------
        // RICHIESTA INFORMAZIONI
        // ---------------------------------------------------------------------
        // Richiesta informazioni.
        // http://xxx.xxx.xxx.xxx:8081/zeroconf/info
        // POST: {"deviceid": "nnnnnnnn","JData": {}}

        20:
        i:=Sysmemset(ADR(Request), 0, SIZEOF(Request)); //Empty request
        i:=Sysmemset(ADR(Result), 0, SIZEOF(Result)); //Empty result
        i:=SysVarsnprintf(ADR(Request), SIZEOF(Request), '{"deviceid": "%s","JData": {}}', STRING_TYPE, DeviceID);

        HTTPRq.Page:=ADR('zeroconf/info'); //Web page
         HTTPRq.Enable:=TRUE; //HTTP get page enable
        CaseNr:=CaseNr+1; //Case gestione

        // ---------------------------------------------------------------------
        // Se fine esecuzione controllo stato ritornato.
        // {"seq":2,"error":0,"JData":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"Elsist WiFi\",\"otaUnlock\":false}"}

        21:
        IF NOT(HTTPRq.Done) THEN RETURN; END_IF;
        HTTPRq.Enable:=FALSE; //HTTP get page enable
        TimeBf:=SysGetSysTime(TRUE); //Time buffer (uS)

        // Controllo se ricevuto risposta da dispositivo.
        // Decodifico valori da stringa JSON ricevuta.

        IF NOT(HTTPRq.PLoad) THEN ErrorNr:=2000; RETURN; END_IF;

        JDecode(Object:=ADR(Result), Name:=ADR('seq'), VType:=UDINT_TYPE, VAddress:=ADR(Sequence), Count:=1);
        IF (JDecode.ECode <> 0) THEN ErrorNr:=2001; RETURN; END_IF;

        JDecode(Object:=ADR(Result), Name:=ADR('error'), VType:=UDINT_TYPE, VAddress:=ADR(DError), Count:=1);
        IF (JDecode.ECode <> 0) THEN ErrorNr:=2002; RETURN; END_IF;

        JDecode(Object:=ADR(Result), Name:=ADR('JData'), VType:=STRING_TYPE, VAddress:=ADR(JData), Count:=1, Size:=SIZEOF(JData));
        IF (JDecode.ECode <> 0) THEN ErrorNr:=2003; RETURN; END_IF;
 
         // Il valore di "JData" è un oggetto JSON ritornato come stringa.
         // Al simbolo " è anteposto "\" ne eseguo eliminazione.

        Ptr[0]:=ADR(JData)+Sysstrlen(ADR(JData)); //Auxiliary pointer
        FOR Ptr[1]:=ADR(JData) TO Ptr[0] DO
            Ptr[2]:=Ptr[1]+1; //Auxiliary pointer
            IF ((@Ptr[1] = 16#5C) AND (@Ptr[2] = 16#22)) THEN i:=Sysmemmove(Ptr[1], Ptr[2], Ptr[0]-Ptr[2]); END_IF;
            IF (@Ptr[1] = 16#7D) THEN @Ptr[2]:=0; END_IF;
        END_FOR;

        // Acquisizione stato relè.

        JDecode(Object:=ADR(JData), Name:=ADR('switch'), VType:=STRING_TYPE, VAddress:=ADR(RValue), Count:=1, Size:=SIZEOF(RValue));
        IF (JDecode.ECode <> 0) THEN  ErrorNr:=2004; RETURN; END_IF;
        RStatus:=TO_BOOL(RValue = 'on');

        // Se acquisito valore inviato da dispositivo setto Ok.

        DOk:=TRUE; //Device Ok
        CaseNr:=0; //Case gestione
    END_CASE;

// [End of file]
Was this article helpful?