I know the solution to this is very simple but I cannot figure it out despite having spent hours looking through documentation and tutorials.
I am working with AdaFruit’s neopixels on a Uno, sending it data from SuperCollider.
The below code works fine with sending integers controlling the brightness of a pixel.
Now I would like to have Supercollider tell Arduino the number as well as brightness of the pixel, so I send an array of [x,y], where x is the pixel number and y the brightness of x.
How do I read an array from Serial.read ?
If I understand right, the two numbers arrive at slightly different times and need to be read into a new array on the Arduino?
this is my SuperCollider code:
(
p = SerialPort(
"/dev/tty.usbmodem14101",
baudrate: 9600,
crtscts: true);
)
//send serial data - slow pulsating
(
r= Routine({
inf.do{|i, x, y, z|
x = i.fold(0,7);
y = i.fold(0, 100).linexp(0, 100, 1, 255).asInteger;
z = [x, y];
p.putAll(z);
0.1.wait;
};
}).play;
)
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.
The technique in the 3rd example will be the most reliable.
You can send data in a compatible format with code like this
I must admit that it is over my head how to use the parsing in my project.
Since I am using sc to send on the serial port, I cannot monitor the Arduino and have no idea if I am on the right path.
Is there a way to keep the serial monitor open while using the serial port for sending?
I have tried adapting example 5 to deal with a string of two integers, is this right (I have only included the parsing part of the code?
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars,","); // get the first part - the string
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
}
and I need to use those integers to define pixelnumber and brightness but where to put the following incomplete code, I can't figure out, does it go in a loop?
int pixnum = // first integer
int brightness = // second integer
pixels.setPixelColor(pixnum, pixels.Color(0,0,0, brightness));
Adam_Pultz:
Is there a way to keep the serial monitor open while using the serial port for sending?
No.
I can think of two options.
Use a USB-TTL cable connected to two SoftwareSerial pins for the input for your project and leave the regular Serial port available for debugging messages.
Add some code to the program that is sending the data to the Arduino so that it can display messages sent to it by the Arduino.
I guess a third possibility would be to pretend that data had been received by hard-coding the pretend-received-data so that you can experiment with parsing it,
I followed your suggestion about sending the data back to sc, which worked well and helped towards solving the problem.
At first I didn't understand why I could only receive the first integer in the string and only numbers 0-9, until I realised that numChars needed to be set to a higher number than just the two integers I wish to receive. I have now set it to 30 and will try to work out the exact number of characters needed for the data I am receiving.
Here's the working code in case anyone find it helpful:
// Example 5 - Receive with start- and end-markers combined with parsing
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 9
#define NUMPIXELS 8
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);
const byte numChars = 30;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC1 = 0;
int integerFromPC2 = 0;
boolean newData = false;
//============
void setup() {
Serial.begin(9600);
//Serial.println();
pixels.begin();
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
showParsedData();
newData = false;
}
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
Serial.print(rc);
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//============
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars,","); // get the first part - the string
integerFromPC1 = atoi(strtokIndx);
strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
integerFromPC2 = atoi(strtokIndx); // convert this part to an integer
strcpy(messageFromPC, strtokIndx);
pixels.setPixelColor(integerFromPC1, pixels.Color(0,0,0, integerFromPC2));
pixels.show();
}
//============
void showParsedData() {
}