Unions+Functions+serialWrite = Error

Hello Everybody,

I'm working on a project, wich is kind of a midi controller, and i've ran into an error wich i can't solve, somebody please help me out :frowning:

This is part of the code, i'm currently starting using pointers for giving variables to functions, but this gives me an error:

#define _C4 		0x30
#define _Cs4 		0x31
#define _D4 		0x32
#define _Ds4		0x33
#define _E4 		0x34
#define _F4 		0x35
#define _Fs4		0x36
#define _G4 		0x37
#define _Gs4 		0x38
#define _A4 		0x39
#define _As4 		0x3A
#define _B4 		0x3B
#define _C5 		0x3C
#define NoteOn 		0x7B
#define MaxVel		0xFF

typedef union {
  struct {
    uint8_t Command;
    uint8_t Channel;
    uint8_t Data1;
    uint8_t Data2;
  } Msg;
  uint8_t Raw[4];
} MidiMsg;

void setup() {
	Serial.begin(31250);
}

void SendMsgSerial (MidiMsg* Message){
Serial.write(*Message.Raw, sizeof(*Message));
}

void SendNoteOn (uint8_t Note, uint8_t Velocity, uint8_t Channel){
MidiMsg Message;
Message.Msg.Command=NoteOn;
Message.Msg.Channel=Channel;
Message.Msg.Data1=Note;
Message.Msg.Data2=Velocity;
SendMsgSerial(&Message);
}

void SendScale (int ValveState){
uint8_t Vel=MaxVel;
uint8_t Ch=0x00;
	switch (ValveState) {
		case 0: SendNoteOn(_C4,Vel,Ch); break;
		case 1: SendNoteOn(_D4,Vel,Ch); break;
		case 2: SendNoteOn(_F4,Vel,Ch); break;
		case 3: SendNoteOn(_E4,Vel,Ch); break;
		case 4: SendNoteOn(_A4,Vel,Ch); break;
		case 5: SendNoteOn(_B4,Vel,Ch); break;
		case 6: SendNoteOn(_G4,Vel,Ch); break;
		case 7: SendNoteOn(_C5,Vel,Ch); break;
	}
}
void loop(){
SendScale(0);
}

the error is : "sketch_mar21a:36: error: request for member 'Raw' in 'Message', which is of non-class type 'MidiMsg*'"

SendMsgSerial is a must have function, and I would like to give the data to it with a pointer to a "MidiMsg" union, wich consists of 4 bytes (later it will be only 3) , because 3 serialWrite commands / midi message is a bit too much in my case, and i would like to send the 3 byte command in one "serialWrite" command...

Anybody could help me out?
Thanks a lot

What happens if you define the struct outside of the union?

Also, what happens if you change:

Serial.write(*Message.Raw, sizeof(*Message));

to:

Serial.write(Message->Raw, sizeof(*Message));

-br

billroy:
Also, what happens if you change:

Serial.write(*Message.Raw, sizeof(*Message));

to:

Serial.write(Message->Raw, sizeof(*Message));

-br

I've done both recomenndations

#define _C4 		0x30
#define _Cs4 		0x31
#define _D4 		0x32
#define _Ds4		0x33
#define _E4 		0x34
#define _F4 		0x35
#define _Fs4		0x36
#define _G4 		0x37
#define _Gs4 		0x38
#define _A4 		0x39
#define _As4 		0x3A
#define _B4 		0x3B
#define _C5 		0x3C
#define NoteOn 		0x7B
#define MaxVel		0xFF

struct Msg{
    uint8_t Command;
    uint8_t Channel;
    uint8_t Data1;
    uint8_t Data2;
  };
  
typedef union {
  Msg Msg1;
  uint8_t Raw[4];
} MidiMsg;

void setup() {
	Serial.begin(31250);
}

void SendMidiMsg (MidiMsg* Message){
Serial.write(Message->Raw, sizeof(*Message));
}

void SendNoteOn (uint8_t Note, uint8_t Velocity, uint8_t Channel){
MidiMsg Message;
Message.Msg1.Command=NoteOn;
Message.Msg1.Channel=Channel;
Message.Msg1.Data1=Note;
Message.Msg1.Data2=Velocity;
SendMidiMsg(&Message);
}

void SendScale (int ValveState){
uint8_t Vel=MaxVel;
uint8_t Ch=0x00;
	switch (ValveState) {
		case 0: SendNoteOn(_C4,Vel,Ch); break;
		case 1: SendNoteOn(_D4,Vel,Ch); break;
		case 2: SendNoteOn(_F4,Vel,Ch); break;
		case 3: SendNoteOn(_E4,Vel,Ch); break;
		case 4: SendNoteOn(_A4,Vel,Ch); break;
		case 5: SendNoteOn(_B4,Vel,Ch); break;
		case 6: SendNoteOn(_G4,Vel,Ch); break;
		case 7: SendNoteOn(_C5,Vel,Ch); break;
	}
}
void loop(){
SendScale(0);
}

Now i only have 3 errors

sketch_mar21a:19: error: variable or field 'SendMidiMsg' declared void
sketch_mar21a:19: error: 'MidiMsg' was not declared in this scope
sketch_mar21a:19: error: 'Message' was not declared in this scope

I think the original problem is solved, but im missing out something

#define ENTRIES(ARRAY)	(sizeof(ARRAY) / sizeof(ARRAY[0]))

#define _C4             0x30
#define _Cs4            0x31
#define _D4             0x32
#define _Ds4            0x33
#define _E4             0x34
#define _F4             0x35
#define _Fs4            0x36
#define _G4             0x37
#define _Gs4            0x38
#define _A4             0x39
#define _As4            0x3A
#define _B4             0x3B
#define _C5             0x3C
#define NoteOn          0x7B
#define MaxVel          0xFF

struct MidiMsg
{
    union
    {
        struct { uint8_t    Command, Channel, Data1, Data2; };
                 uint8_t    Raw[4];
    };
} ;

void setup()
{
    Serial.begin(31250);
}

void SendMsgSerial(struct MidiMsg* Message)
{
    Serial.write(Message->Raw,  ENTRIES(Message->Raw));
}

void SendNoteOn(uint8_t Note, uint8_t Velocity, uint8_t Channel)
{
    MidiMsg     Message;
    Message.Command = NoteOn;
    Message.Channel = Channel;
    Message.Data1   = Note;
    Message.Data2   = Velocity;
    SendMsgSerial(&Message);
}

void SendScale(int ValveState)
{
    uint8_t Vel = MaxVel;
    uint8_t Ch  = 0x00;
    switch ( ValveState )
    {
        case 0: SendNoteOn(_C4, Vel, Ch);   break;
        case 1: SendNoteOn(_D4, Vel, Ch);   break;
        case 2: SendNoteOn(_F4, Vel, Ch);   break;
        case 3: SendNoteOn(_E4, Vel, Ch);   break;
        case 4: SendNoteOn(_A4, Vel, Ch);   break;
        case 5: SendNoteOn(_B4, Vel, Ch);   break;
        case 6: SendNoteOn(_G4, Vel, Ch);   break;
        case 7: SendNoteOn(_C5, Vel, Ch);   break;
    }
}

void loop()
{
    SendScale(0);
}

I think you were being bit by some variant of the "precompiler rearranges your sketch" bug.

I got your last code to compile by adding a line defining SendMidiMsg(); see below.

I got there by looking at the .cpp that is actually compiled for the project, which you can get from rummaging around in the build folder, which you can identify by turning on verbose builds in Preferences. The precompiler phase moved the definition of SendMidiMsg above the definition of the union, which made the compiler unhappy. It seems better here with the patch.

-br

#include "arduino.h"

#define _C4 		0x30
#define _Cs4 		0x31
#define _D4 		0x32
#define _Ds4		0x33
#define _E4 		0x34
#define _F4 		0x35
#define _Fs4		0x36
#define _G4 		0x37
#define _Gs4 		0x38
#define _A4 		0x39
#define _As4 		0x3A
#define _B4 		0x3B
#define _C5 		0x3C
#define NoteOn 		0x7B
#define MaxVel		0xFF

typedef union {
struct {
    uint8_t Command;
    uint8_t Channel;
    uint8_t Data1;
    uint8_t Data2;
  } Msg;
  uint8_t Raw[4];
} MidiMsg;
void SendMidiMsg (MidiMsg* Message);  // this is the magic line


void setup() {
	Serial.begin(31250);
}

void SendMidiMsg (MidiMsg* Message){
Serial.write(Message->Raw, sizeof(*Message));
}

void SendNoteOn (uint8_t Note, uint8_t Velocity, uint8_t Channel){
MidiMsg Message;
Message.Msg.Command=NoteOn;
Message.Msg.Channel=Channel;
Message.Msg.Data1=Note;
Message.Msg.Data2=Velocity;
SendMidiMsg(&Message);
}

void SendScale (int ValveState){
uint8_t Vel=MaxVel;
uint8_t Ch=0x00;
	switch (ValveState) {
		case 0: SendNoteOn(_C4,Vel,Ch); break;
		case 1: SendNoteOn(_D4,Vel,Ch); break;
		case 2: SendNoteOn(_F4,Vel,Ch); break;
		case 3: SendNoteOn(_E4,Vel,Ch); break;
		case 4: SendNoteOn(_A4,Vel,Ch); break;
		case 5: SendNoteOn(_B4,Vel,Ch); break;
		case 6: SendNoteOn(_G4,Vel,Ch); break;
		case 7: SendNoteOn(_C5,Vel,Ch); break;
	}
}
void loop(){
SendScale(0);
}

lloyddean:

#define ENTRIES(ARRAY)	(sizeof(ARRAY) / sizeof(ARRAY[0]))

#define _C4             0x30
#define _Cs4            0x31
#define _D4             0x32
#define _Ds4            0x33
#define _E4             0x34
#define _F4             0x35
#define _Fs4            0x36
#define _G4             0x37
#define _Gs4            0x38
#define _A4             0x39
#define _As4            0x3A
#define _B4             0x3B
#define _C5             0x3C
#define NoteOn          0x7B
#define MaxVel          0xFF

struct MidiMsg
{
   union
   {
       struct { uint8_t    Command, Channel, Data1, Data2; };
                uint8_t    Raw[4];
   };
} ;

void setup()
{
   Serial.begin(31250);
}

void SendMsgSerial(struct MidiMsg* Message)
{
   Serial.write(Message->Raw,  ENTRIES(Message->Raw));
}

void SendNoteOn(uint8_t Note, uint8_t Velocity, uint8_t Channel)
{
   MidiMsg     Message;
   Message.Command = NoteOn;
   Message.Channel = Channel;
   Message.Data1   = Note;
   Message.Data2   = Velocity;
   SendMsgSerial(&Message);
}

void SendScale(int ValveState)
{
   uint8_t Vel = MaxVel;
   uint8_t Ch  = 0x00;
   switch ( ValveState )
   {
       case 0: SendNoteOn(_C4, Vel, Ch);   break;
       case 1: SendNoteOn(_D4, Vel, Ch);   break;
       case 2: SendNoteOn(_F4, Vel, Ch);   break;
       case 3: SendNoteOn(_E4, Vel, Ch);   break;
       case 4: SendNoteOn(_A4, Vel, Ch);   break;
       case 5: SendNoteOn(_B4, Vel, Ch);   break;
       case 6: SendNoteOn(_G4, Vel, Ch);   break;
       case 7: SendNoteOn(_C5, Vel, Ch);   break;
   }
}

void loop()
{
   SendScale(0);
}

That works like a charm, but i don't really understand the magic of that ENTRIES(ARRAY). Thank You very much for the help this forum is a really helpful place!

EDIT: Also, the second solution is working, but with a differend method and ths gets me more confused... Thanks a lot anyway!

Also while i have some pro coders attention, is there any advise about these functions, to make them more simple/efficent / ?

Since i have a ton more functions sending notes depending on input,

Example:

void Send2_5Octave (int ValveState){
	switch (ValveState) {
		case 0:
			SendMidiMsg(NoteOn,_C4,MaxVel);
			SendMidiMsg(NoteOn,_G4,MaxVel);
			SendMidiMsg(NoteOn,_C5,MaxVel);
			SendMidiMsg(NoteOn,_E5,MaxVel);
			SendMidiMsg(NoteOn,_G5,MaxVel);
			SendMidiMsg(NoteOn,_C6,MaxVel); 
			break;
		case 2:
			SendMidiMsg(NoteOn,_B3,MaxVel);
			SendMidiMsg(NoteOn,_Fs4,MaxVel);
			SendMidiMsg(NoteOn,_B4,MaxVel);
			SendMidiMsg(NoteOn,_Ds5,MaxVel);
			SendMidiMsg(NoteOn,_Fs5,MaxVel);
			SendMidiMsg(NoteOn,_B5,MaxVel); 
			break;
		case 3:
			SendMidiMsg(NoteOn,_Gs3,MaxVel);
			SendMidiMsg(NoteOn,_Ds4,MaxVel);
			SendMidiMsg(NoteOn,_Gs4,MaxVel);
			SendMidiMsg(NoteOn,_Gs5,MaxVel);
			break;
		case 4:
			SendMidiMsg(NoteOn,_As3,MaxVel);
			SendMidiMsg(NoteOn,_F4,MaxVel);
			SendMidiMsg(NoteOn,_As4,MaxVel);
			SendMidiMsg(NoteOn,_D5,MaxVel);
			SendMidiMsg(NoteOn,_F5,MaxVel);
			SendMidiMsg(NoteOn,_As5,MaxVel);
			break;
		case 5:
			SendMidiMsg(NoteOn,_G3,MaxVel);
			SendMidiMsg(NoteOn,_D4,MaxVel);
			break;
		case 7:
			SendMidiMsg(NoteOn,_Fs3,MaxVel);
			SendMidiMsg(NoteOn,_Cs4,MaxVel);
			break;
		default:
			SendMidiMsg(NoteOn,_A3,MaxVel);
			SendMidiMsg(NoteOn,_E4,MaxVel);
			SendMidiMsg(NoteOn,_A4,MaxVel);
			SendMidiMsg(NoteOn,_Cs5,MaxVel);
			SendMidiMsg(NoteOn,_A5,MaxVel);
	}
}

I would also like to make the "SendNoteOn" function to handle a dynamic number(max 10) of notes in the input parameter list. Any advise on that?

#define ENTRIES(ARRAY)	(sizeof(ARRAY) / sizeof(ARRAY[0]))

#define _C4             0x30
#define _Cs4            0x31
#define _D4             0x32
#define _Ds4            0x33
#define _E4             0x34
#define _F4             0x35
#define _Fs4            0x36
#define _G4             0x37
#define _Gs4            0x38
#define _A4             0x39
#define _As4            0x3A
#define _B4             0x3B
#define _C5             0x3C
#define NoteOn          0x7B
#define MaxVel          0xFF

struct MidiMsg
{
    union
    {
        struct { uint8_t    Data1, Data2, Data3; };
                 uint8_t    Raw[3];
    };
} ;

void setup()
{
    Serial.begin(31250);
}

void SendMsgSerial(struct MidiMsg* Message)
{
    Serial.write(Message->Raw,  ENTRIES(Message->Raw));
}

void SendNoteOn(uint8_t Note, uint8_t Velocity, uint8_t Channel)
{
    uint8_t Sum;
    Sum=NoteOn<<4;
    Sum=(Sum|Channel);
    MidiMsg     Message;
    Message.Data1   = Sum;
    Message.Data2   = Note;
    Message.Data3   = Velocity;
    SendMsgSerial(&Message);
}

void SendScale(int ValveState)
{
    uint8_t Vel = MaxVel;
    uint8_t Ch  = 0x00;
    switch ( ValveState )
    {
        case 0: SendNoteOn(_C4, Vel, Ch);   break;
        case 1: SendNoteOn(_D4, Vel, Ch);   break;
        case 2: SendNoteOn(_F4, Vel, Ch);   break;
        case 3: SendNoteOn(_E4, Vel, Ch);   break;
        case 4: SendNoteOn(_A4, Vel, Ch);   break;
        case 5: SendNoteOn(_B4, Vel, Ch);   break;
        case 6: SendNoteOn(_G4, Vel, Ch);   break;
        case 7: SendNoteOn(_C5, Vel, Ch);   break;
    }
}

void loop()
{
    SendScale(0);
}

Yes but I'm out for the next 5 to 6 hours.

lloyddean:
Yes but I'm out for the next 5 to 6 hours.

Now i'm going to do some work with the current code, if You willing to take a look, i will send it when done, and You could take a look on it, OK?

I would define the struct, and then define the union, and then define the typedef separately.

void SendMsgSerial (MidiMsg* Message){
Serial.write(*Message.Raw, sizeof(*Message));
}

In this code, Raw is the name of an array, and therefore a pointer. It isn't the values of the array, or even the
value of the first element of the array.

As far as I know, Serial.write() does not have a version to print the values in the array.

If I wanted to write the elements of the array, I'd write this:

for (int i=0 ; i<4 ; i++)
{
    Serial.write( Message->Raw[i], sizeof(uint_8));
}
#define ENTRIES(ARRAY)	(sizeof(ARRAY) / sizeof(ARRAY[0]))

'ENTRIES' is a preprocessor macro. The preprocessor runs before the 'compiler' doing "smart" text substitution on our code before handing it off to the compiler. Here 'ENTRIES' has one parameter 'ARRAY'.

'sizeof' give us the number of bytes a data object occupies.

'sizeof(ARRAY)' give us the byte count of the entire array.

'sizeof(ARRAY[0])' give us the byte count of the first element of the array. An array can't have zero elements so there must be at least 1 element at index 0.

So the macro gets the byte count of the entire array and divides it by the number of bytes in an element of the array giving us the number of elements in the array.

Never hard code a value if you can get it compile time.

Anyway go ahead when ready and we'll help you get something working.

michinyon:
In this code, Raw is the name of an array, and therefore a pointer. It isn't the values of the array, or even the of the first element of the array.

Actually the array 'name' is the ADDRESS of the array it is not a pointer.

A pointer a named memory location large enough to HOLD THE ADDRESS OF data of a known type.