So, als letztes hier noch zwei überarbeitete Sketche.
Sketche und Verdrahtung
Zur direkten Ansteuerung der Led-Kette (ehemals "Nano2"):
#include <FastLED.h>
#include <SoftwareSerial.h>
constexpr int NUM_LEDS{ 60 };
constexpr byte DATA_PIN{ 3 };
constexpr byte SIGNAL_PIN{ 4 };
constexpr byte rxPin{ 5 };
constexpr byte txPin{ 6 };
constexpr byte CLEAR_PIN{ 7 };
// SoftwareSerial object
SoftwareSerial mySerial(rxPin, txPin);
CRGB leds[NUM_LEDS];
constexpr int noOfBytes = 8;
byte ledStates[noOfBytes];
int no = 0;
void setup() {
pinMode(SIGNAL_PIN, OUTPUT);
digitalWrite(SIGNAL_PIN, LOW);
Serial.begin(57600);
Serial.println("Led Access I");
mySerial.begin(57600);
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
delay(10);
digitalWrite(SIGNAL_PIN, HIGH);
}
void loop() {
checkClearPin();
evaluateMySerial();
}
void checkClearPin() {
static byte lastState = HIGH;
byte actState = digitalRead(CLEAR_PIN);
if (actState != lastState) {
lastState = actState;
if (lastState == LOW) {
clearBuffer();
}
}
}
void evaluateMySerial() {
if (mySerial.available()) {
byte c = mySerial.read();
fillBuffer(c);
}
}
void fillBuffer(byte c) {
ledStates[no] = c;
no++;
if (no >= noOfBytes) {
while (mySerial.available()) { byte d = mySerial.read(); }; // Clear Buffer after 8 bytes to remove CR/LF/NL
no = 0;
setLeds();
}
}
void clearBuffer() {
for (int i = 0; i < noOfBytes; i++) {
ledStates[i] = 0;
}
no = 0;
}
void setLeds() {
int startPos = 0;
for (int i = 0; i < noOfBytes; i++) {
byte actByte = ledStates[i];
for (int j = 0; j < 8; j++) {
int ledIndex = startPos + j;
if (ledIndex < NUM_LEDS) {
if (actByte & 0x01 == 0x01) {
leds[ledIndex] = CRGB::Green;
} else {
leds[ledIndex] = CRGB::Black;
}
actByte = actByte >> 1;
}
}
startPos += 8;
}
digitalWrite(SIGNAL_PIN, LOW);
FastLED.show();
digitalWrite(SIGNAL_PIN, HIGH);
}
Der Sketch für den UNO ("Nano1"), von dem der o.a. "Nano2" das Bit-Array der anzusteuernden Leds erhält:
#include <SoftwareSerial.h>
constexpr byte SIGNAL_PIN{ 4 };
constexpr byte rxPin{ 5 };
constexpr byte txPin{ 6 };
constexpr byte CLEAR_PIN{ 7 };
constexpr int NUM_LEDS{ 60 };
byte ledStates[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int noOfBytes = sizeof(ledStates) / sizeof(ledStates[0]);
SoftwareSerial mySerial(rxPin, txPin);
void setup() {
pinMode(CLEAR_PIN, OUTPUT);
digitalWrite(CLEAR_PIN, HIGH);
mySerial.begin(57600);
pinMode(SIGNAL_PIN, INPUT_PULLUP);
clearAllLeds();
Serial.begin(115200);
Serial.println("Led Control II");
}
void loop() {
if (newData(20)) {
sendData();
}
}
boolean newData(unsigned long interval) {
static unsigned long lastChange = 0;
static int count = 0;
static int lastCount = -1;
if (millis() - lastChange > interval) {
lastChange = millis();
clearLastLeds(lastCount);
setLedNo(count);
setLedNo(NUM_LEDS - count - 1);
lastCount = count;
count++;
if (count >= NUM_LEDS) {
count = 0;
}
return true;
}
return false;
}
void sendData() {
while (digitalRead(SIGNAL_PIN) == LOW) { delayMicroseconds(50); };
clearRemoteBuffer();
for (int i = 0; i < noOfBytes; i++) {
mySerial.write(ledStates[i]);
}
}
void setLedNo(int num) {
if (num < NUM_LEDS && num >= 0) {
int index = num / 8;
int actBit = num - index * 8;
byte actByte = ledStates[index];
ledStates[index] = bitSet(actByte, actBit);
}
}
void clearLedNo(int num) {
if (num < NUM_LEDS && num >= 0) {
int index = num / 8;
int actBit = num - index * 8;
byte actByte = ledStates[index];
ledStates[index] = bitClear(actByte, actBit);
}
}
void clearLastLeds(int num) {
if (num >= 0) {
clearLedNo(num);
clearLedNo(NUM_LEDS - num - 1);
}
}
void clearRemoteBuffer() {
digitalWrite(CLEAR_PIN, LOW);
delay(1);
digitalWrite(CLEAR_PIN, HIGH);
}
void clearAllLeds() {
for (int i = 0; i < noOfBytes; i++) {
ledStates[i] = 0;
}
}
Zwischen den beiden Controllern habe ich noch eine weitere digitale Verbindung (Pin 7 - Pin 7) eingeführt, über die der UNO den Empfangspuffer des NANO jeweils rücksetzen lässt, bevor die relevanten Daten übertragen werden. Das stellt die Synchronisation des Bit-Arrays auch dann sicher, wenn die beiden mal - z.B. durch Neustart eines Controllers - "aus dem Tritt" kommen.
Die SoftwareSerial-Rückleitung vom Nano zum Uno kann man auch weglassen, sie wird aktuell nicht genutzt.
Um 61 statt 60 Leds zu unterstützen muss in beiden Sketchen die folgende Konstante auf 61 gesetzt werden:
- constexpr int NUM_LEDS{ 60 };
Für die Auswertung der Midi-Kommandos muss man in dem bei mir dem Uno zugeordneten Sketch nur die Funktion boolean newData() umschreiben. Dort wird aktuell alle "interval" Millisekunden das Array bearbeitet. Für Midi würde man
- NoteON mit setLedNo(num) und 0 <= num < NUM_LEDS und
- NoteOff mit clearLedNo(num) und 0 <= num < NUM_LEDS
umsetzen. Ich vermute, dass Du die Orgelbelegung nutzt (61 Tasten bis zum vier gestrichenen C, wobei dem tiefsten Ton C der Midi-Wert 36 zugeordnet ist). Dann ergäbe sich num = (Midi-Wert - 36).
Gibt die Funktion newData() true zurück, so wird in loop() die Funktion sendData() aufgerufen. Solange keine neuen Daten vorliegen (oder noch Daten "gesammelt" werden) gibt man false zurück.
Ob man die Auswirkung jeder NoteON/NoteOFF Message einzeln überträgt oder bis zu einem noch zu definierenden Timeout sammelt, muss man mal ausprobieren.
Viel Erfolg!
ec2021
