Enviar más de 32 bit's desde Maestro a Esclavo I2C con ArduinoJson

Saludos!!!

Pretendo con el siguiente POST, entregar una solución a cientos de personas que han pasado por el problema de realizar el envío de más de 32 BIT's desde el Maestro al Esclavo usando el protocolo I2C y apoyandome en la librería ARDUINOJSON, y por supuesto enviar datos desde el Esclavo con más de 32 BIT'S.
La dinámica de trabajo fue concebida gracias a tutoriales en la RED y desde este propio espacio.
No creo en lo absoluto que sea mi propuesta la solución definitiva, pero como no he encontrado en ninguna publicación los aspectos aquí tratados, me tomo el tiempo de realizarlo esperando sea de beneficio para muchos.
Espero también los aportes de los tan prestigiosos miembros con el fin de generar alguna especia de ESTANDAR para solucionar el requerimiento.
Este proyecto esta concebido entre dos ARDUINO MEGA, El Maestro posea un escudo CS3231 con el que obtengo los parámetros de DIA Y MES.
Sin más, le dejo mis códigos.

Código del Arduino Maestro:

#include "Wire.h"
#include <ArduinoJson.hpp>
#include <ArduinoJson.h>
#include <DS3231.h>

DS3231 rtc;

boolean century = false;                // Control para el Siglo XXII
boolean h12Flag;                        // Variable para distinguir formato de 12 horas
boolean pmFlag;                         // Variable para saber si es am o pm

const byte I2C_SLAVE_ADDR = 0x20;
const char SEND_LENGTH = 'R';
const char ASK_FOR_LENGTH = 'L';
const char ASK_FOR_DATA = 'D';

String response;
String jsonA;
String jsonB;
StaticJsonDocument<300> doc;
StaticJsonDocument<300> doc_env;
int veces = 1;

void setup() {
    Serial.begin(9600);
    Wire.begin();
}

void loop() {
    Serial.print("Proceso número: ");Serial.println(veces);
    askSlave();
    if(response != "") DeserializeResponse();
    veces ++;
}

void askSlave() {
    response = "";
    paqueteEnvio(veces, 0);

    unsigned int envioTamPaq = envioInicio();
    if (envioTamPaq != 1) return;

    envioLargoPaq();

    Serial.println("Continua proceso normal ");
    Serial.println(" ");
    unsigned int responseLenght = askForLength();
    if (responseLenght == 0) return;

    askForData(responseLenght);
    delay(2000);
}

unsigned int envioInicio(){
    Wire.beginTransmission(I2C_SLAVE_ADDR);
    Wire.write(SEND_LENGTH);
    Wire.endTransmission();

    Wire.requestFrom(I2C_SLAVE_ADDR, 1);
    unsigned int respuesta = Wire.read();
    return respuesta;
}

void envioLargoPaq(){
    Serial.print("Se va a enviar ");Serial.println(jsonB.length());
    Wire.beginTransmission(I2C_SLAVE_ADDR);
    Wire.write(jsonB.length());
    Wire.endTransmission();
    int acumulaLargo = 0;

    Wire.requestFrom(I2C_SLAVE_ADDR, 1);
    unsigned int respuesta = Wire.read();

    Serial.print("Inicio de envío de Paquete ");Serial.println(jsonB);
    if (jsonB.length() > 32) {
        Serial.println("Paquete > a 32 bit");
        int tot_iter = jsonB.length() / 32;
        int inicio = 0;
        int final = 31;
        for (int iter = 0; iter <= tot_iter; iter++) {
            String sub_S = jsonB.substring(inicio, final);            
            Serial.print("Paquete dividido. Acción: -> ");Serial.println(iter);
            Serial.print("Paquete -> ");Serial.println(sub_S);
            Wire.beginTransmission(I2C_SLAVE_ADDR);
            Wire.write(sub_S.c_str(), sub_S.length());
            Wire.endTransmission();
            inicio = final;
            final = final + 31;
            acumulaLargo ++;
            Wire.requestFrom(I2C_SLAVE_ADDR, 1);
            unsigned int listo = Wire.read();
            Serial.print("Respuesta de pasos -> ");Serial.println(listo);
        } 
    } else {
        Serial.println("Paquete < a 32 bit");
        Wire.beginTransmission(I2C_SLAVE_ADDR);
        Wire.write(jsonB.c_str() + acumulaLargo * 32, (jsonB.length() % 32));
        Wire.endTransmission();
        acumulaLargo = 0;
    }

    Serial.println("termino el envío de Paquete ");
}

unsigned int askForLength()
{
   Wire.beginTransmission(I2C_SLAVE_ADDR);
   Wire.write(ASK_FOR_LENGTH);
   Wire.endTransmission();

   Wire.requestFrom(I2C_SLAVE_ADDR, 1);
   unsigned int responseLenght = Wire.read();
   return responseLenght;
}

void askForData(unsigned int responseLenght) {
    Wire.beginTransmission(I2C_SLAVE_ADDR);
    Wire.write(ASK_FOR_DATA);
    Wire.endTransmission();

    for (int requestIndex = 0; requestIndex <= (responseLenght / 32); requestIndex++) {
        Wire.requestFrom(I2C_SLAVE_ADDR, requestIndex < (responseLenght / 32) ? 32 : responseLenght % 32);
        while (Wire.available())
        {
            response += (char)Wire.read();
        }
    }
}

char*  text;
int id;
bool stat;
float value;
void DeserializeResponse() {
    DeserializationError error = deserializeJson(doc, response);
    if (error) { return; }
 
    text = doc["text"];
    id = doc["id"];
    stat = doc["status"];
    value = doc["value"];
 
    Serial.print("Text ");Serial.print(text);
    Serial.print(" - id ");Serial.print(id);
    Serial.print(" - status ");Serial.print(stat);
    Serial.print(" - value ");Serial.println(value);
    Serial.println(" ");
}

void paqueteEnvio(int var_08, int accion) {
    String tabla;
    jsonB = "";
    doc_env.clear();

    if (esPar(var_08) == true) {
        tabla = "0";
    } else {
        tabla = "1";
    }

    doc_env["tabla"] = tabla;

    switch (tabla.toInt()) {
        case 0: // - Datos Generales
            doc_env["accion"] = accion;
        break;
        case 1: // - Datos de DUMP (Archivo diario)
            doc_env["accion"] = accion;
            switch (accion) {
                case 0: // - Datos Generales
                    int v01__mes = rtc.getMonth(century);
                    int v01__dia = rtc.getDate();
                    doc_env["v_mes"] = v01__mes;
                    doc_env["v_dia"] = v01__dia;
                break;
            }
        break;
        case 2: // - Datos de Temperatura y Humedad
        break;
        case 3: // - Datos de Eventos Especiales
        break;
    }
    serializeJson(doc_env, jsonB);
}


boolean esPar(int param_01){
    if (param_01 & 0x01) {
        return false;
    } else {
        return true;
    }
}

Código del Arduino Esclavo

#include "Wire.h"
#include <ArduinoJson.hpp>
#include <ArduinoJson.h>
 
String json;
String response = "";

int acto = 1;

unsigned int largoRequerimiento;

StaticJsonDocument<300> doc;

const byte I2C_SLAVE_ADDR = 0x20;
const char SEND_LENGTH = 'R';
const char ASK_FOR_LENGTH = 'L';
const char ASK_FOR_DATA = 'D';

char request = ' ';
char requestIndex = 0;
int canIter = 1;
int pasos = 0;

void setup() {
    Serial.begin(9600);
    SerializeObject();
    Wire.begin(I2C_SLAVE_ADDR);
    Wire.onRequest(requestEvent);
    Wire.onReceive(receiveEvent);
}

void receiveEvent(int bytes) {
    switch (acto) {
        case 1:
            while (Wire.available()) {
                request = (char)Wire.read();
            }
        break;    
        case 2:
            largoRequerimiento = Wire.read();
            if (largoRequerimiento > 32) {
                int positivo;
                int decimal;
                float tot_div = (float)largoRequerimiento / 32;
                positivo = int(tot_div);
                decimal = int((tot_div - int(tot_div)) * 100);
                if (decimal > 0){
                    canIter = positivo + 1;
                    pasos = canIter; 
                } else {
                    canIter = positivo;
                }
            } else {
                canIter = 1;
            }
            request = 'Z';
        break;    
        case 3:
            if (canIter == 1) {
                while (Wire.available()) {
                    response += (char)Wire.read();
                }
                Serial.print("Respuesta ");Serial.println(response);
                acto = 1;
                request = ' ';
            } else {           
                String paquete;
                paquete = "";
                while (Wire.available()) {
                    paquete += (char)Wire.read();
//                    char paquete = Wire.read();
                }
                response = response + paquete;
                request = 'X';
                pasos = pasos -1;
            }
        break;    
    }
}

void requestEvent() {
    if(request == SEND_LENGTH) {
        Wire.write(1);
        acto = 2;
    }
    if(request == 'Z') {
        Wire.write(1);
        acto = 3;
        response = "";
    }
    if(request == 'X') {
        Wire.write(pasos);
        if (pasos == 0){
            Serial.print("Respuesta ");Serial.println(response);
            acto = 1;
        } else {
            acto = 3;
        }
    }
    if(request == ASK_FOR_LENGTH) {
        Wire.write(json.length());
        char requestIndex = 0;
    }
    if(request == ASK_FOR_DATA) {
        if(requestIndex < (json.length() / 32)) {
            Wire.write(json.c_str() + requestIndex * 32, 32);
            requestIndex ++;
        } else {
            Wire.write(json.c_str() + requestIndex * 32, (json.length() % 32));
            requestIndex = 0;
        }
    }
}

void loop() {
}

void SerializeObject() {
    doc["text"] = "myText";
    doc["id"] = 10;
    doc["status"] = true;
    doc["value"] = 3.14;
    serializeJson(doc, json);
}

Luego de obtener el requerimiento del maestro, en el código del Arduino Esclavo en "Serial.print("Respuesta ");Serial.println(response);" se puede descomponer dicho valor para realizar las tareas pertinentes.
El paquete desde el Maestro se arma de la siguiente manera:

Primera posición (Identificación de Archivo) Posición [0]
0 - Datos Generales
1 - Datos de DUMP (Archivo diario)
2 - Datos de Temperatura y Humedad
3 - Datos de Eventos Especiales

Segunda posición (Acción) Posición [2] (Para CONSULTA varia dependiendo la posición a ubicar desde 2 a N)
0 - Verifica existencia de archivo. (Si no existe, lo crea)
1 - Update (Actualización de registro) Para Datos Generales actualiza todos los registros
2 - Crea un nuevo registro
3 - Consulta (L-ULTIMO, F-PRIMERO, UN NUMERO ES POSICION)

Las posiciones siguientes corresponden a los datos en los casos de UPDATE, CREACION  y CONSULTA.

Espero les sea de provecho.
Gracias por su tiempo en valorar este aporte.

Hola
Hay otras bibliotecas i2c además de Wire.h que no usan un búfer y, por lo tanto, no tienen un límite de 32 bytes para la transferencia.
Prueba por ejemplo

Te agradezco el dato. Sin embargo, mi intención de la publicación es otra. Si existen mil formas de hacerlo, me gustaría ver esos ejemplos. No solo hacer mención de esa tecnología. Yo subí como aporte un Sketch funcional que se pueda adaptar a cualquier necesidad. ¿Cual es el tuyo?

Lo siento, no te entendí bien. El español no es mi lengua materna. Pensé que estabas preguntando de qué otra manera podría hacerse.

Por la biblioteca Wire.h estándar divida los paquetes en fragmentos de 32 bytes es una práctica común.

Estas en lo correcto, la fragmentación es la única vía por medio de esa librería. No conozco aún tiny-i2c. Quizás de haberlo hecho no hubiese pasado por tantos problemas.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.