Arduino Library for M9803R Mastech Multimeter data logging

Hi, all.

This is my first post and my first library.

M9803R Library permit to collect data from a M9803R Mastech Multimeter, if you want it follow:

If you find some bug add an issue on github.

uint16_t _u16BaudRate; ///< baud rate (300..115200) initialized in begin()

115200 does not fit in an uint16_t

Hi

thank you for your interest, M9803R Multimeter works only at 9600 baudrate so that is not so important :slight_smile:

I will remove that and don't let user to select a baudrate.

Cheers.

and adjust the comments accordingly :slight_smile:

will have another look (I don't have such a MM so my remarks will be on code only)

void M9803RMaster::RefreshM9803R(void)
{
	int8_t index_of_crlf = 0;
	// Read data if available
	while (M9803RSerial.available())
	{
		_u8ResponseBuffer[_u8ResponseBufferIndex] = M9803RSerial.read();
		_u8ResponseBufferIndex++;
	}

	index_of_crlf = getEndOfPacket();
	if (index_of_crlf != -1)
	{
		_u8M9803RStatus = M9803RCore(index_of_crlf);
	}
}

should have a test on the array size.

void M9803RMaster::RefreshM9803R(void)
{
	// Read data if available
	while ( (M9803RSerial.available() > 0 ) && (_u8ResponseBufferIndex < ku8MaxBufferSize) )
	{
		_u8ResponseBuffer[_u8ResponseBufferIndex] = M9803RSerial.read();
		_u8ResponseBufferIndex++;
	}

	int8_t index_of_crlf = getEndOfPacket();
	if (index_of_crlf != -1)
	{
		_u8M9803RStatus = M9803RCore(index_of_crlf);
	}
}
void M9803RMaster::InitResponseBuffer(void)
{
	for (uint8_t i = 0; i < ku8MaxBufferSize; i++){
		_u8ResponseBuffer[i] = 0;
	}
	_u8ResponseBufferIndex = 0;
}

could be slightly faster

void M9803RMaster::InitResponseBuffer(void)
{
	while( _u8ResponseBufferIndex > 0 )
        {
		_u8ResponseBuffer[--_u8ResponseBufferIndex] = 0;
	}
}
uint8_t M9803RMaster::getEndOfPacket(void)
{
	if (_u8ResponseBufferIndex >= 2)
	{
		for (uint8_t i = 0; i < (_u8ResponseBufferIndex - 1); i++){
			if (_u8ResponseBuffer[i] == 0x0D)
			{
				if (_u8ResponseBuffer[i + 1] == 0x0A)
				{
					return i;
				}
			}
		}
	}
	return -1;
}

has a bug.
return type is uint8_t so it cannot return a -1;

  • removed unreachable code from switches

  • used e notation for some floats (better do them all) - less typos
    1e-4 == 1 * pow(10, -4) = 0.0001
    1e3 == 1 * pow(10,3) = 1000

  • added missing return at the end

float M9803RMaster::ku8M9803RFloatConvertion(uint8_t unit, uint8_t range)
{
    switch (unit)
    {
    case 0:
    case 1: // ??
        switch (range)
        {
        case 0: return 1e-4;
        case 1: return 1e-3;
        case 2: return 1e-2;
        case 3: return 1e-1;
        case 4: return 1e0;
        default: return 0.0;
        }
    case 2:
    case 3: // ??
        switch (range)
        {
        case 0: return 1e-6;
        case 1: return 1e-5;
        case 2: return 1e-4;
        case 3:
        case 4: return 1e-3;
        default: return 0.0;
        }
    case 4:
    case 5: // ??
        switch (range)
        {
        case 0: return 1e-1;
        case 1:	return 1e0;
        case 2:	return 1e1;
        case 3:	return 1e2;
        case 4:	return 1e3;
        case 5:	return 1e4;
        default: return 0.0;
        }
        break;
    case 6: // DIODE
        switch (range)
        {
        case 0:
        case 1: return 1e-3;
        default: return 0.0;
        }
        break;
    case 7: // ADP
    case 10: // Hz
        switch (range)
        {
        case 0: return 10.0;
        case 1:	return 100.0;
        case 2:	return 1000.0;
        case 5:	return 0.1;
        case 6:	return 1.0;
        default: return 0.0;
        }
        break;
    case 8:
    case 9: // ??
        switch (range)
        {
        case 0:
        case 2: return 0.01;
        default: return 0.0;
        }
        break;
    case 11:
        return 0.0;
    case 12:
        switch (range)
        {
        case 0:	return 1e-12;
        case 1:	return 1e-11;
        case 2:	return 1e-10;
        case 3:	return 1e-9;
        case 4:	return 1e-8;
        default: return 0.0;
        }
    default:
        return 0.0;
    }
    return 0.0
}

give it a try

merging compacting,

M9803RMaster::ConvertUnitToChar(uint8_t unit)
{
	if (unit > 12)
	{
		return ' ';
	}
	return "VVAAOODHAAH F"[unit];
}

ku8M9803RUnit can be removed;

Refactored

uint8_t M9803RMaster::M9803RCore(int8_t index_crlf)
{
    uint8_t u8Status = ku8M9803RSuccess;
    uint8_t u8_unit;
    uint8_t u8_range;

    if (index_crlf < 9)
    {
        u8Status = ku8M9803RFrameMalformed;
    }
    else
    {
        // Store data
        _M9803RData.status = _u8ResponseBuffer[index_crlf - 9];
        if (_M9803RData.status & 0x01) // OL Over Range
        {
            u8Status = ku8M9803ROverRange;
        }
        else
        {
            _M9803RData.d0 = _u8ResponseBuffer[index_crlf - 8] - '0'; // char '7' != 7
            _M9803RData.d1 = _u8ResponseBuffer[index_crlf - 7] - '0';
            _M9803RData.d2 = _u8ResponseBuffer[index_crlf - 6] - '0';
            _M9803RData.d3 = _u8ResponseBuffer[index_crlf - 5] - '0';
            
            _M9803RData.unit = _u8ResponseBuffer[index_crlf - 4];
            _M9803RData.range = _u8ResponseBuffer[index_crlf - 3];
            // not used
            // _M9803RData.special_1 = _u8ResponseBuffer[index_crlf - 2];
            // _M9803RData.special_2 = _u8ResponseBuffer[index_crlf - 1];

            u8_unit = _M9803RData.unit & 127;
            u8_range = _M9803RData.range & 127;
            
            m9803r_absolute_value_unit = ConvertUnitToChar(u8_unit);
            uint16_t temp = _M9803RData.d0 + _M9803RData.d1 * 10 + _M9803RData.d2 * 100 + _M9803RData.d3 * 1000; // can be integer math.
            m9803r_absolute_value = temp * ku8M9803RFloatConvertion(u8_unit, u8_range);

            if (_M9803RData.status & 0x08) // if this bit is set sign is negative
            {
                m9803r_absolute_value *= -1.0;
            }
        }
    }
    
    InitResponseBuffer();
    return u8Status;
}

should also add

#define M9803R_LIB_VERSION "0.1.00"

sofar my remarks

robtillaart:
Refactored

uint8_t M9803RMaster::M9803RCore(int8_t index_crlf)

{
    uint8_t u8Status = ku8M9803RSuccess;
    uint8_t u8_unit;
    uint8_t u8_range;

if (index_crlf < 9)
    {
        u8Status = ku8M9803RFrameMalformed;
    }
    else
    {
        // Store data
        _M9803RData.status = _u8ResponseBuffer[index_crlf - 9];
        if (_M9803RData.status & 0x01) // OL Over Range
        {
            u8Status = ku8M9803ROverRange;
        }
        else
        {
            _M9803RData.d0 = _u8ResponseBuffer[index_crlf - 8] - '0'; // char '7' != 7
            _M9803RData.d1 = _u8ResponseBuffer[index_crlf - 7] - '0';
            _M9803RData.d2 = _u8ResponseBuffer[index_crlf - 6] - '0';
            _M9803RData.d3 = _u8ResponseBuffer[index_crlf - 5] - '0';
           
            _M9803RData.unit = _u8ResponseBuffer[index_crlf - 4];
            _M9803RData.range = _u8ResponseBuffer[index_crlf - 3];
            // not used
            // _M9803RData.special_1 = _u8ResponseBuffer[index_crlf - 2];
            // _M9803RData.special_2 = _u8ResponseBuffer[index_crlf - 1];

u8_unit = _M9803RData.unit & 127;
            u8_range = _M9803RData.range & 127;
           
            m9803r_absolute_value_unit = ConvertUnitToChar(u8_unit);
            uint16_t temp = _M9803RData.d0 + _M9803RData.d1 * 10 + _M9803RData.d2 * 100 + _M9803RData.d3 * 1000; // can be integer math.
            m9803r_absolute_value = temp * ku8M9803RFloatConvertion(u8_unit, u8_range);

if (_M9803RData.status & 0x08) // if this bit is set sign is negative
            {
                m9803r_absolute_value *= -1.0;
            }
        }
    }
   
    InitResponseBuffer();
    return u8Status;
}




should also add

#define M9803R_LIB_VERSION "0.1.00"


sofar my remarks

Hi, this will not work. Data byte are sent from M9803R as raw byte not ascii chars.

I changed the code accordingly to your hints, thank you.

Hi, this will not work. Data byte are sent from M9803R as raw byte not ascii chars.

didn't know that, I've have an old multimeter that sent strings in ASCII.

I expect the footprint of the lib should become a bit smaller, can you confirm?

Yes the footprint could be smaller but I want to make a Library with all function available. Special1 and Special2 needs to be coded.

If you want to debug example code use GitHub - gremlinc5/VirtualInstrumentsM9803R: An easy to use virtual M9803R Mastech Multimeter to be used on a Serial Port
In the Publish folder there is a setup to install a M9803R Virtual Instrument. With that you will be able to send to your arduino M9803R packets with a simple USB to TTL converter.

Your PC with Virtual M9803R sends packets to your arduino.
Remeber to set RS232C CheckBox in the program or you will not get packets.

With the example code you will get the led set high if the value of the packet is greater than 10.0 (absolute value, not the four digits value that are relative to the range in use).