Controllo remoto del livello dell’acqua e della pompa via LoRa (ReWaL): client software – 3

Spread the love

Per il client, un pannello solare ricarica una batteria che alimenta il dispositivo, quindi deve andare in modalità di sospensione ogni volta che è possibile, e deve notificare lo stato della batteria al server, e quando si sveglia deve controllare lo stato del sensore del livello dell’acqua e inviarlo al server e attendere un ACK per essere sicuri che lo stato sia ricevuto, altrimenti inviare nuovamente il messaggio, infine inviare un messaggio ogni volta che lo stato cambia.

LoRa wireless remote water tank and pump controller (esp8266) Client software Arduino IDE
LoRa wireless remote water tank and pump controller (esp8266) Client software Arduino IDE

Configurazione EByte E32

Il dispositivo LoRa è un E32, è configurato per una trasmissione fissa con un ciclo breve di WOR per essere sicuri che intercetterà il lungo preambolo del server così da potersi riattivare.

Fare riferimento all’articolo “Ebyte LoRa E32 per Arduino, esp32 o esp8266: configurazione“.

		ResponseStructContainer c;
		c = e32ttl.getConfiguration();
		Configuration configuration = *(Configuration*) c.data;
		configuration.ADDL = CLIENT_ADDL;
		configuration.ADDH = CLIENT_ADDH;
		configuration.CHAN = CLIENT_CHANNEL;
		configuration.OPTION.fixedTransmission = FT_FIXED_TRANSMISSION;
		configuration.OPTION.wirelessWakeupTime = WAKE_UP_250;

		configuration.OPTION.fec = FEC_1_ON;
		configuration.OPTION.ioDriveMode = IO_D_MODE_PUSH_PULLS_PULL_UPS;
		configuration.OPTION.transmissionPower = POWER_20;

		configuration.SPED.airDataRate = AIR_DATA_RATE_010_24;
		configuration.SPED.uartBaudRate = UART_BPS_9600;
		configuration.SPED.uartParity = MODE_00_8N1;

		ResponseStatus rs = e32ttl.setConfiguration(configuration, WRITE_CFG_PWR_DWN_SAVE);
		SERIAL_DEBUG.println(rs.getResponseDescription());

		c = e32ttl.getConfiguration();
		Configuration configurationNew = *(Configuration*) c.data;

		printParameters(configurationNew);

Il microcontrollore entra immediatamente in modalità di sospensione leggera e rimane in ascolto sul pin AUX per l’attivazione.

La gestione della modalità sleep è spiegata su “Ebyte LoRa E32 per Arduino, esp32 o esp8266: WOR (wake on radio) anche il microcontrollore ed il nuovo WeMos D1 shield”.

		gpio_pin_wakeup_enable(GPIO_ID_PIN(AUX_PIN), GPIO_PIN_INTR_LOLEVEL);
		wifi_set_opmode(NULL_MODE);
		wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
		wifi_fpm_open();
		wifi_fpm_set_wakeup_cb(callback);
		wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME);
		delay(1000);

Quindi, quando arriva un messaggio, il WeMos D1 si sveglia e inizia a recuperare il messaggio.

Operazioni client

Immediatamente al risveglio, si imposterà il livello minimo e massimo dal sensore

void setMin(){
	uint8_t valMin = digitalRead(TANK_MIN);
	minLevel = valMin==LOW;
}
void setMax(){
	uint8_t valMax = digitalRead(TANK_MAX);
	maxLevel = valMax==LOW;
}

se minLevel è vero significa che il serbatoio è vuoto, se maxLevel è vero significa che il serbatoio è pieno.

Dopo la riattivazione, l’operazione verifica se sono presenti dati nel buffer E32 ed elabora tutto il messaggio

	if (e32ttl.available()){
		SERIAL_DEBUG.println("Start reading!");

		ResponseContainer rs = e32ttl.receiveMessage();
		String message = rs.data;

		SERIAL_DEBUG.println(rs.status.getResponseDescription());

		SERIAL_DEBUG.println(message);
		deserializeJson(doc, message);

		String type = doc["type"];
		SERIAL_DEBUG.print("type --> ");
		SERIAL_DEBUG.println(type);

		operationalSelected = static_cast<OPERATION_MODE>((int)doc["mode"]);

		ResponseStatus rsW;

		if (type=="start"){
			pumpIsActive = true;

			SERIAL_DEBUG.print(rsW.getResponseDescription());
			SERIAL_DEBUG.println("Operation complete!!");

			if (batteryLevel>1){
				batteryTimePassed = 0;
				btSended = false;
			}

		}else if(type=="stopp"){
			batteryTimePassed = 0;

			pumpIsActive = false;
			ResponseStatus rsUpdate = sendUpdate(PACKET_PUMP_LEVEL);
			SERIAL_DEBUG.println(rsUpdate.getResponseDescription());
			rsW = setModeSleep();
			SERIAL_DEBUG.print(rsW.getResponseDescription());
			SERIAL_DEBUG.println("Operation complete, go to sleep!!");
		}else if (type=="ackpa"){
			needAck = false;
		}

		ResponseStatus rsUpdate = sendUpdate(PACKET_PUMP_LEVEL);
		SERIAL_DEBUG.println(rsUpdate.getResponseDescription());

		SERIAL_DEBUG.println("Update complete!!");

		timePassed = millis();
	}

Messaggi

Ecco un semplice schema di comunicazione

Water tank level and pump controller LoRa messages
Water tank level and pump controller LoRa messages

Messaggi del server

Il tipo base di messaggi che possono essere ricevuti dal server è:

  • start: si desidera avviare la pompa e in risposta è necessario il livello aggiornato del serbatoio;
  • stop: la pompa è ferma e il client deve andare in sleep;
  • ackpa: messaggio di conferma ricezione.

Messaggio “start”

Il messaggio di start ci informa che è necessario avviare la pompa e per iniziare a far funzionare il server è necessario il livello del serbatoio.

Il server invia anche OPERATION_MODE, che sostanzialmente dice al client

  • OPERATION_NORMAL: nessun ping necessario e il Server attende solo lo stato del livello quando cambia;
  • OPERATION_PING: dopo la lettura del messaggio, il client inizia ad inviare un ping con il livello del serbatoio al server, se il server non riceve il ping dopo 20 secondi arresta la pompa e dà errore.

Messaggio “stop”

Il messaggio di arresto notifica semplicemente che fermiamo la pompa, quindi il client invia l’ultimo messaggio informativo e va in sleep.

Messaggio di acknowledge

Questo è il messaggio di conferma della ricezione se non arriva alcun acknowledge il client invia nuovamente il messaggio di stato.

Messaggio client

Esistono 2 semplici tipi di messaggio client e questi vengono gestiti con questa funzione

ResponseStatus sendUpdate(PACKET_TYPE packetType, bool needAckParam ){
	SERIAL_DEBUG.println(" ------------ START ---------------");
	SERIAL_DEBUG.println(packetType);
	delay(500);

	JsonObject root = doc.to<JsonObject>(); // get the root object

	switch (packetType) {
		case BATTERY_LEVEL: {
			batteryLevel = getBatteryVoltage();
			SERIAL_DEBUG.print(F(" BATTERY --> "));
			SERIAL_DEBUG.println(batteryLevel);
			root["ty"] = "bl";
			root["pn"] = packetNumber++;
			root["battLev"] = batteryLevel;
			break;
		}
		case PACKET_PUMP_LEVEL: {
			setMin();
			setMax();

			root["ty"] = "ppl";
			root["pn"] = packetNumber++;
			root["maxL"] = (maxLevel?1:0);
			root["minL"] = (minLevel?1:0);
			root["ack"] = needAckParam?1:0;
			break;
		}
		default:
			break;
	}

	int size = measureJson(doc)+1;

	char buf[size];
	serializeJson(doc, buf, size);
	SERIAL_DEBUG.println(buf);
	SERIAL_DEBUG.println(measureJson(doc));

	SERIAL_DEBUG.print("Send message to server ");
	SERIAL_DEBUG.print(SERVER_ADDH, DEC);
	SERIAL_DEBUG.print(" ");
	SERIAL_DEBUG.print(SERVER_ADDL, DEC);
	SERIAL_DEBUG.print(" ");
	SERIAL_DEBUG.print(SERVER_CHANNEL, HEX);
	SERIAL_DEBUG.println(" ");

	SERIAL_DEBUG.println("Check mode ");
	SERIAL_DEBUG.println(e32ttl.getMode());

	ResponseStatus rsW = setModeNormal();

	SERIAL_DEBUG.println(rsW.getResponseDescription());

	if (rsW.code!=SUCCESS) return rsW;

	ResponseStatus rsSend = e32ttl.sendFixedMessage(SERVER_ADDH, SERVER_ADDL, SERVER_CHANNEL, buf, size);
	SERIAL_DEBUG.println(rsSend.getResponseDescription());

	if (rsSend.code==SUCCESS && needAckParam){
		ackStartTime = millis();
		needAck = true;
	}

	return rsSend;
}

I messaggi sono:

  • BATTERY_LEVEL: che invia il livello della batteria in volt;
  • PACKET_PUMP_LEVEL: che invia lo stato del livello minimo e massimo.

Livello della batteria

Questo è un messaggio per gestire il livello della batteria, ottengo il valore del pin analogico con divisore di tensione e converto il valore in volt.

Per la gestione della batteria si può fare riferimento all’articolo “Partitore di tensione (voltage divider): calcolatore e applicazioni”.

float getBatteryVoltage(){
	//************ Measuring Battery Voltage ***********
	float sample1 = 0;

	for (int i = 0; i < 100; i++) {
		sample1 = sample1 + analogRead(A0); //read the voltage from the divider circuit
		delay(2);
	}
	sample1 = sample1 / 100;
	DEBUG_PRINT(F("AnalogRead..."));
	DEBUG_PRINTLN(sample1);
	float batVolt = (sample1 * 3.3 * (BAT_RES_VALUE_VCC + BAT_RES_VALUE_GND) / BAT_RES_VALUE_GND) / 1023;

	int bvI = batVolt * 100;
	batVolt = (float)bvI/100;
	return batVolt;
}

L’intervallo di aggiornamento dell’aggiornamento è maggiore quando la batteria è carica e diminuisce quando la batteria si scarica.

Pacchetto con il livello dell’acqua

Qui inviamo il valore dell’interruttore di livello minimo e interruttore di livello massimo con la richiesta di acknowledging, questo messaggio viene inviato quando la pompa si avvia e quando vengono attivati gli interrupt dagli interruttori di livello.

	  pinMode(TANK_MIN, INPUT);
	  pinMode(TANK_MAX, INPUT);

	  attachInterrupt(digitalPinToInterrupt(TANK_MIN), minCallack, CHANGE );
	  attachInterrupt(digitalPinToInterrupt(TANK_MAX), maxCallack, CHANGE );

Grazie

Puoi trovare il codice client completo sul mio GitHub come al solito.

  1. Controllo remoto del livello della cisterna d’acqua e della pompa via LoRa: introduzione
  2. Controllo remoto del livello dell’acqua e della pompa via LoRa: server software
  3. Controllo remoto del livello dell’acqua e della pompa via LoRa: client software
  4. Controllo remoto del livello dell’acqua e della pompa via LoRa: PCB del server
  5. Controllo remoto del livello dell’acqua e della pompa via LoRa: PCB client
  6. Controllo remoto del livello dell’acqua e della pompa via LoRa: assemblare il server e box stampato in 3D
  7. Controllo remoto del livello dell’acqua e della pompa via LoRa: assemblare il client e box stampato in 3D

Spread the love

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *