Quick GUI for MBED projects, part 2

The previous post I explained the code for the dear ImGui part of the GUI. In this part the code for the serial connection will be discussed.

This code is made up of 2 parts, the MBED code and the PC side code. The MBED code will be discussed first. The MBED code uses the excellent MODSERIAL library. The MBED code after stripping away the initialization and such looks like this:

#define MESSAGE_BUFFER_SIZE 32

volatile bool messageReceived = false;
char messageBufferIncoming[MESSAGE_BUFFER_SIZE];
char messageBufferOutgoing[MESSAGE_BUFFER_SIZE];
 
void messageReceive(MODSERIAL_IRQ_INFO *q) 
{
    MODSERIAL *sys = q->serial;
    sys->move(messageBufferIncoming, MESSAGE_BUFFER_SIZE);
    messageReceived = true;
}
 
void adcread()
{
    uint16_t adc1, adc2;
    adc1 = ain1.read_u16();
    adc2 = ain2.read_u16();
    sprintf(messageBufferOutgoing,"A%02u%02u\n",adc1/656,adc2/656);
    pc.puts(messageBufferOutgoing);
}
 
void ioread()
{
    messageBufferOutgoing[0] = 'I';
    messageBufferOutgoing[1] = dips1 + 48;
    messageBufferOutgoing[2] = dips2 + 48;
    messageBufferOutgoing[3] = dips3 + 48;
    messageBufferOutgoing[4] = dips4 + 48;
    messageBufferOutgoing[5] = dips5 + 48;
    messageBufferOutgoing[6] = dips6 + 48;
    messageBufferOutgoing[7] = dips7 + 48;
    messageBufferOutgoing[8] = dips8 + 48;
    messageBufferOutgoing[9] = '\n';
    messageBufferOutgoing[10] = NULL;
    pc.puts(messageBufferOutgoing);
}
 
int main() 
{
    pc.baud(115200);
    pc.attach(&messageReceive, MODSERIAL::RxAutoDetect);
    pc.autoDetectChar('\n');
    
    adctimer.attach(&adcread, 0.025);
    iotimer.attach(&ioread, 0.25);
     
    while(1) 
    {
        while(messageReceived)
        {
            if(messageBufferIncoming[0] == 'L')
            {
                led1 = messageBufferIncoming[1] - 48;
                led2 = messageBufferIncoming[2] - 48;
                led3 = messageBufferIncoming[3] - 48;
                led4 = messageBufferIncoming[4] - 48;
                led5 = messageBufferIncoming[5] - 48;
                led6 = messageBufferIncoming[6] - 48;
                led7 = messageBufferIncoming[7] - 48;
                led8 = messageBufferIncoming[8] - 48;
            }
            else if(messageBufferIncoming[0] == 'D')
            {
                float dacval = (float)(messageBufferIncoming[3] - 48)/10.0;
                dacval += (float)(messageBufferIncoming[4] - 48)/100.0;
                aout = dacval;
            }
            messageReceived = false;
        }    
    }
}

The full code can be found on my Github.

The MBED sends a few strings to the PC, for an ADC value: “A1234\n”, in this case ADC1 has the value 12 and ADC2 has the value of 34. The ADC range is 00 to 99 and always is 2 digits. The IO pins are send as following: “I00110101\n”. Every 0 or 1 is the value of an IO pin. The PC side sends 2 strings back. The DAC value (“D12\n”, range of 00 to 99) and the selected LED’s (“L00001111\n”), works the same as the IO pins.)

The MBED code sets up MODSERIAL so it will make messageReceived high when an end of line “\n” character is received. In the main loop the MBED constantly checks if messageReceived is high and if so it decodes the message and sets the LEDs and DAC accordingly. 2 timer interrupts are initialized, one for the ADC and one for the IO pins. Every time these timer interrupts trigger the MBED sends the IO and ADC values to the PC side.

The PC code shown below are snippets from the code as all the ImGui related code has been explained in the previous blog post.

The serial port is opened with:

serial::Serial my_serial("/dev/ttyACM0", 115200, serial::Timeout::simpleTimeout(1000));

Open port ttyACM0 at 115200 BAUD with a timeout of 1000ms. The port has to be changed to the correct port when running this code obviously.

The code to read in lines and do something with them is as follows:

	int count = my_serial.readline(buffer, 100, "\n");
	if((count >= 1) && (buffer[0] == 'A'))
	{
		adc1arr[counter] = ((buffer[1]-48) * 10) + (buffer[2]-48);
		adc2arr[counter] = ((buffer[3]-48) * 10) + (buffer[4]-48);
		counter++;
		if(counter >= 100)
			counter = 0;
	}
	else if((count >= 1) && (buffer[0] == 'I'))
	{
		input[0] = buffer[1] - 48;
		input[1] = buffer[2] - 48;
		input[2] = buffer[3] - 48;
		input[3] = buffer[4] - 48;
		input[4] = buffer[5] - 48;
		input[5] = buffer[6] - 48;
		input[6] = buffer[7] - 48;
		input[7] = buffer[8] - 48;
	}

my_serial.readline(buffer, 100, “\n”); reads a line that ends with a “\n” character and places it in the buffer. It returns the amount of bytes read in. If there is 1 byte or more and the first byte is an A it’s ADC data, first byte an I means it’s IO pin data. The received data then gets decoded accordingly.

The LEDs and DAC values are send using the following code:

	static int ledsold;
	int leds = led[0] + (led[1] << 1) + (led[2] << 2) + (led[3] << 3)  + (led[4] << 4) + (led[5] << 5) + (led[6] << 6) + (led[7] << 7);
	if(ledsold != leds)
	{
		ledsold = leds;
		string test_string;
		test_string = "L";
		for(int i = 0; i < 8; i++)
		{
			if(led[i] == true)
				test_string += "1";
			else
				test_string += "0";
		}
		test_string += "\n";
		size_t bytes_wrote = my_serial.write(test_string);
		cout << "Bytes wrote: " << bytes_wrote << endl;
	}


	ImGui::SliderInt("DAC value", &tint, 0, 100);
	if(tintold != tint)
	{
		tintold = tint;
		float tfloat = (float)tint/100.0;
		if(tfloat > 1.0)
			tfloat = 1.0;
		string dacstring = "D";
		std::ostringstream buff;
		buff << std::setprecision(2) << tfloat;
		dacstring += buff.str();
		dacstring += "\n";
		size_t bytes_wrote = my_serial.write(dacstring);
		cout << "Bytes wrote: " << bytes_wrote << endl;
	}

The actual part that sends data to the MBED is very simple, gather all the data in a string and send it using my_serial.write(); For the LEDs first all the separate LED values are combined in a single integer. If this integer is different from the last value it will send the new value, else it will do nothing. If a new value has to be send all the LED values are placed in an string and send to the MBED.

the DAC code works in a similar way. The integer value of the DAC gets converted a few times before it’s placed in a string and send to the MBED but the principle is the same as the LEDs.

The full code can be found on my Github page. It uses Dear ImGui and Serial, so the requirements for those two libraries should be installed, which I think is only OpenGL on a Linux system. But more info can be found on their github pages. In the end these two libraries make it fairly simple to create a fully C++ based GUI for any microcontroller with a serial port, be it an Arduino or a Raspberry pi. I hope these two posts can help others getting started with these two libraries and if there are any questions, feel free to contact me :)


So, what do you think ?