gioblu / PJON

PJON (Padded Jittering Operative Network) is an experimental, arduino-compatible, multi-master, multi-media network protocol.
Other
2.73k stars 238 forks source link

SWBB half duplex issue #337

Open sacreyoubeurt opened 4 years ago

sacreyoubeurt commented 4 years ago

Hello, I'm working on a project of MIDI (or equivalent) modular controller based on Arduino Uno boards. 1) Peripheral Arduino boards have to send their sensors values (buttons, potentiometers, faders) to a central Arduino board connected to the computer. 2) In return, the central board have to send to the peripheral boards the correspondant values from a software (probably Ableton Live) to insure synchronisation between sensor and software values when sensors are encoders or motorized faders. 3) Additionally, the central board would send strings from the computer to peripheral boards in order to display on OLED units. I'm trying to use PJON with SoftwareBitBang strategy as communication protocol between the boards.

At the moment I succeed in doing 1) send sensors values from peripheral to central board. But when I try to implement 2) I've got the following issues :

Here is the code of the peripheral board : `#define pinVal1 A0

define pinVal2 A1

define pinVal3 A2

define pinVal4 A3

define pinVal5 2

define pinVal6 3

define pinVal7 4

define pinVal8 5

define nbrIN 8

define pinPJON 12

define PJON_INCLUDE_SWBB

include

const int id = 2; // Peripheral bus id int idM; // Central bus id const char type = 'A'; // Peripheral unit type char typeM; // Central unit type unsigned long time; int lenght = nbrIN+1; uint8_t val[nbrIN]; uint8_t newval[nbrIN];

PJON bus(id);

void receiver_function(uint8_t payload, uint16_t length, const PJON_Packet_Info &packet_info) { / Make use of the payload before sending something, the buffer where payload points to is overwritten when a new message is dispatched*/

if(!bus.update()) { idM = (packet_info.sender_id) ; typeM = ((uint8_t)(payload[0])) ; newval[1] = ((uint8_t)(payload[1])) ; newval[2] = ((uint8_t)(payload[2])) ; newval[3] = ((uint8_t)(payload[3])) ; newval[4] = ((uint8_t)(payload[4])) ; newval[5] = ((uint8_t)(payload[5])) ; newval[6] = ((uint8_t)(payload[6])) ; newval[7] = ((uint8_t)(payload[7])) ; newval[8] = ((uint8_t)(payload[8])) ;

Serial.print("Central unit of ID ") ;
  Serial.print(packet_info.sender_id) ;
  Serial.print(" and type ") ;
  Serial.print(typeM);
  Serial.print(" newval1: ") ;
  Serial.print(newval[1]);
  Serial.print(" newval2: ") ;
  Serial.print(newval[2]);
  Serial.print(" newval3: ") ;
  Serial.print(newval[3]);
  Serial.print(" newval4: ") ;
  Serial.print(newval[4]);
  Serial.print(" newval5: ") ;
  Serial.print(newval[5]);
  Serial.print(" newval6: ") ;
  Serial.print(newval[6]);
  Serial.print(" newval7: ") ;
  Serial.print(newval[7]);
  Serial.print(" newval8: ") ;
  Serial.print(newval[8]);
}

}

void transmitter_function() { uint8_t packetA[lenght] = { (uint8_t)(type), (uint8_t)(val[1]), (uint8_t)(val[2]), (uint8_t)(val[3]), (uint8_t)(val[4]), (uint8_t)(val[5]), (uint8_t)(val[6]), (uint8_t)(val[7]), (uint8_t)(val[8]) }; bus.send_packet(idM, packetA, lenght) ; }

void setup() { pinMode(pinVal5, INPUT_PULLUP); pinMode(pinVal6, INPUT_PULLUP); pinMode(pinVal7, INPUT_PULLUP); pinMode(pinVal8, INPUT_PULLUP); Serial.begin(115200); bus.strategy.set_pin(12); bus.begin(); bus.set_receiver(receiver_function); bus.set_communication_mode(PJON_HALF_DUPLEX); }

void loop() {

val[1] = analogRead(pinVal1)>>3; val[2] = analogRead(pinVal2)>>3; val[3] = analogRead(pinVal3)>>3; val[4] = analogRead(pinVal4)>>3; val[5] = !digitalRead(pinVal5)127; val[6] = !digitalRead(pinVal6)127; val[7] = !digitalRead(pinVal7)127; val[8] = !digitalRead(pinVal8)127;

bus.receive(1000); if(millis() - time > 10) { time = millis(); transmitter_function(); } bus.update(); };`

And here is the code of the central board : `#define PINtransmitPJON 12

define PJON_INCLUDE_SWBB

include

PJON bus(1);

unsigned long time; char type; uint8_t count = 0;

void receiver_function(uint8_t payload, uint16_t length, const PJON_Packet_Info &packet_info) { / Make use of the payload before sending something, the buffer where payload points to is overwritten when a new message is dispatched */

if(!bus.update()) { type = ((uint8_t)(payload[0])) ; count = ((uint8_t)(payload[1])) ;

Serial.print(type);
Serial.println(count);

} }

void transmitter_function() { uint8_t packetM[2] = {(uint8_t)('M'), (uint8_t)(count)}; bus.send_packet(2, packetM, 2) ; }

void setup() { Serial.begin(115200); bus.strategy.set_pin(12); bus.begin(); bus.set_receiver(receiver_function); bus.set_communication_mode(PJON_HALF_DUPLEX); };

void loop() { count++; count %= 127; if(millis() - time > 10) { time = millis(); transmitter_function(); } bus.receive(); bus.update(); };`

Thank you in advance for your help :-)

gioblu commented 4 years ago

Ciao @sacreyoubeurt I suspect the problem is Serial.print. Try to delete the prints and see if communication gets better. I have experienced that Serial prints can disrupt performance if they occur along with data reception. If you really need the prints, try to call flush when finished to print (to force serial transmission to be blocking) and call bus.receive() passing a duration ie bus.receive(1000); to receive for a millisecond (to have higher chances during polling to find the start of a frame).

gioblu commented 4 years ago

@sacreyoubeurt I also see here if(millis() - time > 10) { Are you transmitting cyclically from each node every 10 milliseconds? That's a tempest of packets I suspect you are saturating the band, specially if running MODE 1

If you need to send a binary state for buttons/switches consider using a single bit instead of an entire byte. That would reduce almost by half the length of your packets.

sacreyoubeurt commented 4 years ago

Hi @gioblu , thank you for your help. I can't delete Serial.print because there are at the moment my only way to see if communication is okay. I tried Serial.flush() but it didn't change anything.

However, what you said about bandwidth is interesting for me. Currently, I'm only using 1 peripheral board and 1 central board :

The question about bandwidth is that in my full project, I will need :

gioblu commented 4 years ago

Ciao @sacreyoubeurt for sure one way to reduce the bandwidth use is to transmit only when the value changes. If the module includes in the packet his own type, the master could know how the data received is formatted.

Consider that if you use send_packet or send_packet_blocking you don't need to call update. You need to call update if you use the send or send_repeatedly functions.

gioblu commented 4 years ago

@sacreyoubeurt one thing, once you got it working stable try faster communication modes to have higher bandwidth.

gioblu commented 4 years ago

@sacreyoubeurt I suggest you to toggle the state of a LED instead of the prints if you are probing communication performance, try also to call receive in the slaves passing a longer duration

sacreyoubeurt commented 4 years ago

Hello there, I made some improvements in the data transmission frequency but still the half duplex communication between peripheral board and central board is not functional.

When a sensor value changes on peripheral board, it is sent to the central board. This central affects assigns this value to a MIDI parameter (NoteOn or CC) depending on the type, the Id ans the sensor number of the peripheral. It communicates these MIDI data to the computer as a MIDI class compliant device (it is a flashed Arduino Uno). This path : peripheral>central>computer is functional.

Then, when computer send MIDI feedback, the central board read and interpret values to find back the Id and sensor number where to send this value (which will be important for motorized faders for example). It works correctly. But then the transmission of this value by the central or the reception by the peripheral doesn't work and I can't figure it out. Do you have any idea of what could be the issue ?

Peripheral code : `

define pinVal0 A0

define pinVal1 A1

define pinVal2 A2

define pinVal3 A3

define pinVal4 2

define pinVal5 3

define pinVal6 4

define pinVal7 5

define nbrIN 7

define pinPJON 12

define PJON_INCLUDE_SWBB

include

const int Id = 0; // This peripheral unit bus id int IdM = 127; // Central bus id const char Type = 'A'; // This peripheral unit type A: buttons, B: potentiometers, C: encoders, D: faders, E: motorized faders char TypeM; // Central unit type int i=0; uint8_t val[nbrIN]; uint8_t oldval[nbrIN]; int FeedSensNum; int FeedVal;

PJON bus(Id);

void receiver_function(uint8_t payload, uint16_t length, const PJON_Packet_Info &packet_info) { / Make use of the payload before sending something, the buffer where payload points to is overwritten when a new message is dispatched */

if((char)payload[0] == 'M') { IdM = (packet_info.sender_id) ; FeedSensNum = ((uint8_t)(payload[1])) ; FeedVal = ((uint8_t)(payload[2])) ;

Serial.print(FeedSensNum);
Serial.println(FeedVal);
}

}

void transmitter_function() { for (int i=0; i<(nbrIN); i++){ if(oldval[i]!=val[i]){ uint8_t packet[3] = { (uint8_t)(Type), (uint8_t)(i), (uint8_t)(val[i]) }; bus.send_packet(IdM, packet, 3); break; } } }

void setup() { pinMode(pinVal4, INPUT_PULLUP); pinMode(pinVal5, INPUT_PULLUP); pinMode(pinVal6, INPUT_PULLUP); pinMode(pinVal7, INPUT_PULLUP); Serial.begin(115200); bus.strategy.set_pin(12); bus.set_error(error_handler); bus.set_receiver(receiver_function); bus.begin(); bus.set_communication_mode(PJON_HALF_DUPLEX); }

void error_handler(uint8_t code, uint16_t data, void *custom_pointer) { if(code == PJON_CONNECTION_LOST) { Serial.print("Connection with device ID "); Serial.print(bus.packets[data].content[0], DEC); Serial.println(" is lost."); } if(code == PJON_PACKETS_BUFFER_FULL) { Serial.print("Packet buffer is full, has now a length of "); Serial.println(data, DEC); Serial.println("Possible wrong bus configuration!"); Serial.println("higher PJON_MAX_PACKETS if necessary."); } if(code == PJON_CONTENT_TOO_LONG) { Serial.print("Content is too long, length: "); Serial.println(data); } };

void loop() { bus.receive(50000);

val[0] = analogRead(pinVal0)>>3; val[1] = analogRead(pinVal1)>>3; val[2] = analogRead(pinVal2)>>3; val[3] = analogRead(pinVal3)>>3; val[4] = !digitalRead(pinVal4)127; val[5] = !digitalRead(pinVal5)127; val[6] = !digitalRead(pinVal6)127; val[7] = !digitalRead(pinVal7)127;

transmitter_function();

oldval[0] = val[0]; oldval[1] = val[1]; oldval[2] = val[2]; oldval[3] = val[3]; oldval[4] = val[4]; oldval[5] = val[5]; oldval[6] = val[6]; oldval[7] = val[7]; }; Central code :

define nbrIN 8;

define PINtransmitPJON 12

define PJON_INCLUDE_SWBB

include

include

MIDI_CREATE_DEFAULT_INSTANCE()

const int IdM = 127; // This central unit bus id int Id; // Peripheral bus id const char TypeM = 'M'; // This central unit type char Type; // Peripheral unit type int SensNum = 0; // Peripheral sensor number received in the PJON packet uint8_t SensVal; // Peripheral sensor value received in the PJON packet int MIDIoutNote = 0; // MIDI note to transmit int MIDIoutCC = 0; // MIDI control change to transmit int MIDIoutChan = 1; // MIDI channel to transnmit int MIDIinChan; // MIDI channel received int MIDIinNote; // MIDI note number received int MIDIinCC; // MIDI CC number received int MIDIinVal; // MIDI value received int FeedId; // Peripheral Id to send the MIDI feedback int FeedSensNum; // Peripheral sensor to send the MIDI feedback

PJON bus(IdM);

void receiver_function(uint8_t payload, uint16_t length, const PJON_Packet_Info &packet_info) { / Make use of the payload before sending something, the buffer where payload points to is overwritten when a new message is dispatched */

if((char)payload[0] != 'M') { Id = (packet_info.sender_id) ; Type = ((uint8_t)(payload[0])) ; SensNum = ((uint8_t)(payload[1])) ; SensVal = ((uint8_t)(payload[2])) ; }

if(Type == 'A'){ MIDIoutNote = Id*8+SensNum ; MIDI.sendNoteOn(MIDIoutNote, SensVal, MIDIoutChan); }

else {
MIDIoutCC = (Id-16)*8+SensNum ; MIDI.sendControlChange(MIDIoutCC, SensVal, MIDIoutChan); } }

void transmitter_note_function() { uint8_t packetNote[3] = { (uint8_t)(TypeM), (uint8_t)(FeedSensNum), (uint8_t)(MIDIinVal) }; bus.send_packet(FeedId, packetNote, 3); }

void transmitter_CC_function() { uint8_t packetCC[3] = { (uint8_t)(TypeM), (uint8_t)(FeedSensNum), (uint8_t)(MIDIinVal) }; bus.send_packet(FeedId, packetCC, 3); }

void setup() { bus.begin(); bus.strategy.set_pin(12); bus.set_receiver(receiver_function); bus.set_communication_mode(PJON_HALF_DUPLEX);

MIDI.turnThruOff(); MIDI.begin(MIDI_CHANNEL_OMNI); };

void loop() { bus.receive(50000); if (MIDI.read()) // Is there a MIDI message incoming ? { switch(MIDI.getType()) // Get the type of the message we caught {
case midi::NoteOn: // Midi Notes MIDIinNote = MIDI.getData1(); MIDIinVal = MIDI.getData2(); FeedId = MIDIinNote/8; FeedSensNum = MIDIinNote-MIDIinNote/8*8; transmitter_note_function(); break;

            case midi::ControlChange:     // Midi Control Change 
            MIDIinCC = MIDI.getData1();
            MIDIinVal = MIDI.getData2();
            FeedId = MIDIinCC/8+16;
            FeedSensNum = MIDIinCC-MIDIinCC/8*8;
            transmitter_CC_function();
            break;
            }
    }

} `

Thank you in advance for your help.

gioblu commented 4 years ago

Ciao @sacreyoubeurt sorry for the late response. Have you added a pull-down resistor as suggested in the README? https://github.com/gioblu/PJON/tree/master/src/strategies/SoftwareBitBang

send_packet attempts the transmission once, if the channel is busy or interference is present (possibly because of the missing pull-down) packet sending could be aborted. Try to use send_packet_blocking instead of send_packet.

Let me know how it goes :)