Skocz do zawartości

ESP - IDF odczyt licznika energii za pomocą MODBUS RTU


_LM_

Pomocna odpowiedź

Cześć. Mam za zadanie odczytanie parametrów licznika DRS - 100 - 3P -MOD lub jego inna nazwa LUMEL NMID30-1 oto instrukcja tego licznika https://www.lumel.com.pl/resources/Pliki do pobrania/NMID30-1/NMID30-1_instrukcja_obslugi_Modbus.pdf 

Do tego celu postanowiłem skorzystać z przykładu modbus od espressif: https://docs.espressif.com/projects/esp-modbus/en/latest/esp32/master_api_overview.html#modbus-api-master-overview

Problem polega na tym że różne próby wypełnienia tablicy deskryptorów nie przynoszą oczekiwanych parametrów. Chcę na początek odczytać rejestr przechowujący napięcie pomiędzy L1 i N wypełniam tablicę jak poniżej:

const mb_parameter_descriptor_t device_parameters[] = {
    // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
//    {     CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1,  MB_PARAM_HOLDING, 30000, 2,
//                    INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS(0,0,0),  PAR_PERMS_READ  },
		    { CID_INP_DATA_0,		//CID
		     STR("Data_channel_1"), //Par Name
			 STR("Volts"),			//Unit
			 MB_DEVICE_ADDR1,		//DEV ADDR '1'
		//	 MB_PARAM_HOLDING,
			  MB_PARAM_INPUT,		//  Modbus Reg Type

			 0,						//  Reg Start
			 2,						//  Reg Size

			 	 INPUT_OFFSET(input_data0), // Instance Offset
			 PARAM_TYPE_FLOAT,				// data type 'float'
	//		 PARAM_TYPE_FLOAT_BADC,
		//	 PARAM_TYPE_FLOAT_DCBA,
		//	 PARAM_TYPE_U32,
			 4,								// Data Size
			 OPTS( 0,0,0),					// nd
			 PAR_PERMS_READ_WRITE_TRIGGER },// nd

Kod z którego korzystałem pochodzi z przykładów espressif https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_master

 

I (498) MASTER_TEST: Start modbus test...
I (518) MASTER_TEST: Characteristic #0 Data_channel_1 (Volts) value = -4266057265024839188480.000000 (0xe3674370) read successful.
I (1018) MASTER_TEST: Alarm triggered by cid #0.
I (1018) MASTER_TEST: Destroy master...
I (1018) main_task: Returned from app_main()

Elektrycznie komunikacja jest sprawna gdyż podczas łączenia licznik wskazuje ikonę słuchawki oraz podłączenie "śledziem" modbus - usb również wskazuje na prawidłową komunikację, mam to przetestowane. 
ModbusMaster64bit_iQLVSFCSmM.thumb.png.9151c0ec987f01f285e5e350a148b5b3.png

Niestety dalsze działania, skomunikowania tego z esp nie dają efektów. W programach na PC często jest zaznaczenie "float swapped" co to oznacza? 

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

2 godziny temu, _LM_ napisał:

W programach na PC często jest zaznaczenie "float swapped" co to oznacza?

Pewnie oznacza to iż masz odczyt z przecinkiem albo bez.

Link do komentarza
Share on other sites

(edytowany)
2 godziny temu, _LM_ napisał:

W programach na PC często jest zaznaczenie "float swapped" co to oznacza? 

Że dwie "połówki floata" są zamienione miejscami 😉 

Jeżeli dobrze pamiętam

#include <inttypes.h>
#include <stdio.h>

void swap_float(float* floatPtr)
{
    uint16_t* intPtr = (uint16_t*) floatPtr;
    uint16_t cpy = *intPtr;
    *intPtr = *(intPtr + 1);
    *(intPtr + 1) = cpy;
}

int main()
{
    uint16_t firstHalf = 0x199A;
    uint16_t secondHalf = 0x4366;
    
    uint32_t floatObj = firstHalf << 16 | secondHalf;
    float floatInfo = *((float*) (&floatObj));
    
    printf("%08x", floatObj);
    printf("\r\n");
    printf("%lf", floatInfo);
    printf("\r\n");
    
    swap_float(&floatInfo);
    printf("\r\n");
    
    printf("%08x", *((uint32_t*) (&floatInfo)));
    printf("\r\n");
    printf("%lf", floatInfo);
    printf("\r\n");

    return 0;
}

Pisane na kolanie
https://www.se.com/in/en/faqs/FA401500/

Edytowano przez H1M4W4R1
  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

Ok to się dotyczy odczytanych danych więc póki nie mam nic konkretnego odczytane to nie zaprzątam sobie tym głowy. Mam podłączony analizator stanów logicznych to tak wygląda jakbym miał skopane adresowanie rejestrów chyba że też występuje jeszcze coś innego czego nie zauważam

Logic_Vst8cTOIxg.thumb.png.82cf2c113706f8324202a71c5a3f81a9.png

CRC póki co zostawiam bo nie bierze w tej części analizy udziału. Skąd takie adresowanie skoro powinienem mieć 0x01|0x04|...

@H1M4W4R1 Biblioteka umożliwia wszelkie konwersje w te i nazad także nie powinno być kłopotu 
 

    PARAM_TYPE_FLOAT_ABCD = 0x1A,           /*!< Float ABCD floating point, big endian */
    PARAM_TYPE_FLOAT_CDAB = 0x1B,           /*!< Float CDAB floating point big endian, reversed register order */
    PARAM_TYPE_FLOAT_BADC = 0x1C,           /*!< Float BADC floating point, little endian, reversed register order */
    PARAM_TYPE_FLOAT_DCBA = 0x1D,           /*!< Float DCBA floating point, little endian */

 

Edytowano przez _LM_
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Podłączyłem analizator tx rx i de 

Logic_KFOZGr5i7a.thumb.png.3264629ba0c227b91e9dd10dc6c3adfd.png

Przełączenie enable występuje 200us przed zakończeniem nadawania TX prędkość 9600

Link do komentarza
Share on other sites

Te stracone bity to reszta CRC to może wpływać na odczyty z licznika 

Link do komentarza
Share on other sites

Zmagań ciąg dalszy, ze względu na brak lepszych pomysłów powołałem do życia osobny task do sterowania pinem DE 
 

static void dectrltask(void * arg)
{
    gpio_reset_pin(GPIO_NUM_15);

    gpio_set_direction(GPIO_NUM_15, GPIO_MODE_OUTPUT);
    RESET_DE;
    	while(1)
    	{
    		vTaskSuspend(detask);
    		vTaskDelay(pdMS_TO_TICKS(10));
    		RESET_DE;
    	}
}

Task ma trywialne zadanie, po wybudzeniu ma odczekać 10ms, ustawić stan niski na pinie DE i pójść spać. Przebiegi z analizatora wykazują że sterowanie przepływem danych mieści się w czasie wysłania bufora więc tu mam pewność co do tej kwestii. Pytanie czy takie przedwczesne uruchomienie nadajnika RS486 nie przysporzy mi innych problemów ale że póki co mam tylko jedno urządzenie, zostawiam to do testów jak jest.

 Logic_KgbFCWQI0k.thumb.png.2258e4deab127a2623921d07678e290f.png

Logic_ZaA2fZK7Q0.thumb.png.0108442184a4bf0d22eac31dbaf5dcb8.png

Widać że stan wysoki utrzymuje się do końca wysłania bufora. Na testy powinno to wystarczyć

 

Link do komentarza
Share on other sites

(edytowany)

Jest postęp, otrzymuję już prawidłowe dane z licznika. Póki co w HEX prawdopodobnie większość odczytów które miałem (wcześniej) były w porządku, musiałem jednak poświecić nieco czasu na zrozumienie działania deskryptora. Espressif nie ułatwia zadania swoimi przykładami, są nazbyt rozbudowane. Poniżej odczytane wartości rejestru napięcia L1 - N 
 

I (2128) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x7f854371) parameter read successful.
I (2658) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x896d4371) parameter read successful.
I (3188) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x7ca94371) parameter read successful.
I (3718) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x70e44371) parameter read successful.
I (4268) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x6c924371) parameter read successful.
I (4798) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x5a8b4371) parameter read successful.
I (5328) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x63574371) parameter read successful.
I (5858) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x5df54371) parameter read successful.
I (6388) MODBUS_MASTER: Characteristic #0 Measurements  (Volts) value = (0x5df54371) parameter read successful.

teraz wystarczy tego HEXa przerobić na format strawny dla ludzkiego oka, mogę to zrobić ręcznie (rzutowania, przesuwania itd) lub skorzystać z wbudowanych narzędzi z przykładowego kodu co mam zamiar uczynić. Jeszcze screen z programu PC do analizy modbus odczytane napięcie to około 240V tyle jest teraz u mnie.

ModbusMaster64bit_BNMF728dFH.thumb.png.095e51a58a9fe257c7a3262dd115f884.png

Cieszy mnie to bo mając nawet surowe dane z tego licznika mogę z tym zrobić co się mi podoba. 

EDIT: drobna uwaga dotycząca narzędzi... do podglądu danych używam klona saleae z programem Logic2 urządzenie mam wpięte na TX RX o czym pisałem wyżej, gdybym od razu ustawił dekoder na "serial" być może już wczoraj wiedziałbym że przekazywane dane są w porządku. Niestety użyłem dekodera modbus (być może w sposób niewłaściwy) przez co dane zbierane przez analizator były błędne. Taka to nauczka na przyszłość

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)
Dnia 24.05.2024 o 15:41, H1M4W4R1 napisał:

Pisane na kolanie

Użyłem unii 
 

        	float value = *(float*)par_data;

        	union {
        	  float f;
        	  char b[4];
        	}u;

        	u.f = value;

        	char tabs[4];
        	tabs[0] = u.b[2];
        	tabs[1] = u.b[3];
        	tabs[2] = u.b[0];
        	tabs[3] = u.b[1];


        	*(float*)&value = *(float*)tabs;

Teraz odczyt się zgadza

ATBespTerminal_tYwsIphqNd.thumb.png.765fa0b1b29e0e1201f31a966d8132e9.png

 

 

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

15 godzin temu, _LM_ napisał:

musiałem jednak poświecić nieco czasu na zrozumienie działania deskryptora. Espressif nie ułatwia zadania swoimi przykładami, są nazbyt rozbudowane.

Jak znajdziesz chwilę wolnego możesz nam potem zrobić artykuł z prostszymi 😉 

2 godziny temu, _LM_ napisał:

Użyłem unii 

Nie trawię ich, wolę matematykę na wskaźnikach 😉 

Btw. mogłeś zrobić unię z uint16_t, wtedy byś miał mniej o 2 operacje... wydajność ma znaczenie... (tak to sarkazm)

Problemem jest to, że Twój kod jest też unsafe - nie gwarantujesz, że float ma 32 bity. Akurat w przypadku ESP32 nie ma to znaczenia, ale jakbyś chciał go gdzieś skopiować to przydałaby się dyrektywa sprawdzająca rozmiar floata (tak dla pewności), bo np. GPU spokojnie obsługują fp16 😉 

Link do komentarza
Share on other sites

40 minut temu, H1M4W4R1 napisał:

Problemem jest to, że Twój kod jest też unsafe - nie gwarantujesz, że float ma 32 bity

Jakbyś to rozwiązał? 

Na usprawiedliwienie mam że to wersja biurkowo robocza. Espressif udostępnia funkcje do dekodowania tych danych ale jeszcze muszę nad tym posiedzieć. 

42 minuty temu, H1M4W4R1 napisał:

Jak znajdziesz chwilę wolnego możesz nam potem zrobić artykuł z prostszymi

Szczęśliwie znalazłem prostszy przykład dzięki któremu w ogóle ruszyłem z tematem. Po Brazylijsku 😄

Link do komentarza
Share on other sites

6 minut temu, _LM_ napisał:

Po Brazylijsku

Wait... a w Brazylii nie mówili po portugalsku? xD

6 minut temu, _LM_ napisał:

Jakbyś to rozwiązał? 

#include "inttypes.h"

float convertFloat(float* fpNumber)
{
	if(sizeof(float) == sizeof(uint32_t))
	{
		// Data (at same address as fpNumber)
		uint16_t* data = (uint16_t*) fpNumber;

		// Swap first and second half
		const uint16_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
	}
	else if(sizeof(float) == sizeof(uint16_t))
	{
		// Data (at same address as fpNumber)
		uint8_t* data = (uint8_t*) fpNumber;

		// Swap first and second half
		const uint8_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
	}
	else if(sizeof(float) == sizeof(uint64_t))
	{
		// Data (at same address as fpNumber)
		uint32_t* data = (uint32_t*) fpNumber;

		// Swap first and second half
		const uint32_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
	}

	return *fpNumber;
}

Jakoś tak, każdy ogarnięty kompilator powinien usunąć else, a potem całkowicie wykasować pętlę.

obraz.thumb.png.85269665beb3073f79744f08d0fb4aa7.png

Ew. możesz przenieść returna do warunków i jak nie wejdzie do żadnego to wejść do FaultHandler(); Wtedy będzie najlepiej 😉 

#include "inttypes.h"

void convertFloat(float* fpNumber)
{
	if(sizeof(float) == sizeof(uint32_t))
	{
		// Data (at same address as fpNumber)
		uint16_t* data = (uint16_t*) fpNumber;

		// Swap first and second half
		const uint16_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
		return;
	}
	else if(sizeof(float) == sizeof(uint16_t))
	{
		// Data (at same address as fpNumber)
		uint8_t* data = (uint8_t*) fpNumber;

		// Swap first and second half
		const uint8_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
		return;
	}
	else if(sizeof(float) == sizeof(uint64_t))
	{
		// Data (at same address as fpNumber)
		uint32_t* data = (uint32_t*) fpNumber;

		// Swap first and second half
		const uint32_t copy = *data;
		*(data) = *(data + 1);
		*(data + 1) = copy;
		return;
	}

	FaultHandler();	
}

Wszystkie warunki są obliczalne podczas kompilacji, więc optymalizacja kompilatora powinna załatwić problem 😉 Mało prawdopodobne, by float nie był 32-bitowy, no ale safe code to safe code... xD

Link do komentarza
Share on other sites

1 minutę temu, H1M4W4R1 napisał:

Mało prawdopodobne, by float nie był 32-bitowy, no ale safe code to safe code... xD

To było trzeba dać sizeof float xD 

2 minuty temu, H1M4W4R1 napisał:

Wait... a w Brazylii nie mówili po portugalsku? xD

Tak mi coś nie grało jak to pisałem hehe nima czasu sprawdzać.

W każdym razie facio szczęśliwie nazywa zmienne i funkcje w języku międzynarodowym a nie upiera się przy swoim jak niektórzy... Sorki musiałem. 

Link do komentarza
Share on other sites

(edytowany)

@_LM_ Portugalski brazylijski pt_br to ten właściwy portugalski, pt_pt europejski ma za dużo naleciałości. A co do obcych języków... portugalski jeszcze da się zrozumieć jak np znasz hiszpański. Kiedyś musiałem poprawić jakis kod gdzie nie tylko komentarze, ale i wszystkie nazwy były po turecku 😞

A przy okazji - kto pamięta ile bajtów miał float w 8-bitowych komodorkach?

Edytowano przez ethanak
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.