Code keeps looping even if serial is available - c++

I'm using an external application to sync my RGB peripherals with my WS2812B Strip. I wanted to make a default color that it could switch to when the application isn't running but after 2 seconds of enabling the strip in the software, it goes back to the default color I set (255,255,255).
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUMPIXELS 30
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB +
NEO_KHZ800);
void setup() {
pixels.begin();
Serial.begin(19200, SERIAL_8E1);
}
void loop() {
for (int w = 0; w < NUMPIXELS; w++) {
pixels.setPixelColor(w, 255, 255, 255);
}
pixels.show();
int w = 0;
if (Serial.available()) {
int n = 0;
for (int n = 0; n < 3; n++) {
byte rgb[3];
Serial.readBytes(rgb, 3);
uint32_t r = int(rgb[0]);
uint32_t g = int(rgb[1]);
uint32_t b = int(rgb[2]);
for (int i = 0; i < NUMPIXELS; i++) {
pixels.setPixelColor(i, r, g, b);
}
pixels.show();
}
}
}

The function Serial.available() does something else than what you expect:
You expect it to return if there is a communication partner available. Instead it checks if there are bytes to read available:
Get the number of bytes (characters) available for reading from the serial port. (Source: https://www.arduino.cc/en/Serial/Available )
This means: Your Arduino has run out of bytes to read, because it empties the pipeline too fast. So, as there are no more bytes to read, it will just make the leds show white.
Sidenote: The if condition should be changed to reflect this (see post on Arduino website):
if (Serial.available() > 0) {

Related

ATTiny85 + Adafruit_NeoPixel not working with array

I want to create a LED animation using Adafruit_NeoPixel library.
Unfortunately I'm struggling with a probably dump thing. For some reason the strip does not anymore work when using a certain array access if (led_states[pixel] > 0) {. Meaning even strip.clean() + strip.show() in setup doesn't work anymore. May you can tell me what I did wrong because I really don't get it. PS: I'm using ATTiny85 with arduino framework if that helps.
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>
#define LED_AMOUNT 30
#define LED_DATA_PIN PB1
SoftwareSerial SWSERIAL(0, PB3); // RX, TX
Adafruit_NeoPixel strip(LED_AMOUNT, LED_DATA_PIN, NEO_GRB + NEO_KHZ800);
int step = 2;
int max = 255 - step;
int led_states[LED_AMOUNT] = { 0 };
int led_values[LED_AMOUNT] = { 0 };
unsigned long last_frame = 0;
unsigned long last_change = 0;
void led_test_setup() {
delay(5000);
SWSERIAL.begin(9600);
SWSERIAL.println("Setup");
randomSeed(analogRead(0));
strip.begin();
strip.clear();
strip.show();
}
void led_test_loop() {
unsigned long now = millis();
if (last_change + 200 < now) {
int pixel = random(0, LED_AMOUNT);
led_states[pixel] = 1;
led_values[pixel] = 0;
last_change = now;
}
if (last_frame + 10 < now) {
for (int pixel = 0; pixel < LED_AMOUNT; pixel++) {
if (led_states[pixel] > 0) { // <---- strip works when commenting this block
SWSERIAL.printf("V: %i\n", led_values[pixel]);
}
strip.setPixelColor(pixel, led_values[pixel], led_values[pixel], led_values[pixel]);
}
last_frame = now;
strip.show();
}
}
I may found the issue. Seems like SoftwareSerial and Neopixel library are using both interrupts which then corrupts neo pixel communication.
Found here: https://forum.arduino.cc/t/arduino-nano-softwareserial-adafruit_neopixel-problem/540057/2

How do I use an arduino as an LED controller?

I'm a total C++ newbie, but I recently got an arduino and I'm doing my first project with it. I may be jumping into the deep end here, but I've got some experience with electronics already. I also have some experience with python.
I want to make a fancy interior lighting setup using two of these led strips and this arduino starter kit.
I need to make a whole bunch of presets that can be quickly activated using the IR remote included with the kit.
I need to be able to split the strips into individual zones , which I can use to easily set the presets. Maybe I want preset1 to be the ceiling lights a dark blue at 50% brightness and the window lights an orange at 25% brightness.
I know that to achieve this I want every button on the remote to call a different function. I've written some Psuedocode and I'd like some advice on the best way to learn how to implement this on an arduino.
I really need help with the software side of this project, the hardware side is all good.
disclaimer: I haven't touched coding since high school, so my psuedocode is probably trash. I just hope it's vaguely understandable
//Psuedocode attempt to make an LED thingy
// Importing needed libraries
Import fastled
Import IRremote
define num_led 300
define data_pin 5
// defining various zones. Zone 1 = Under cabinet, Zone 2 = Around window etc.
Global zone1 = num_led [0:24]
Global zone2 = num_led [24:172]
Global zone3 = num_led [173:277]
Global zone4 = num_led [278:299]
Def preset1()
for led in zone1:
ledRGB = (160, 0, 210)
for led in zone2:
ledRGB = (120, 0, 24)
for led in zone3:
ledRGB = (0, 0, 0)
for led in zone4:
ledRGB = (100,100,100)
// Make a whole bunch more of these normal presets
Def preset6()
for led in zone1:
do cool fastled animation
for led in zone2:
do different fastled animation
for led in zone3:
ledRGB = (0, 0, 0)
for led in zone4:
ledRGB = (0, 0, 0)
// Make a whole bunch more of these fancy presets
Def BrightnessUp()
i = 0
for num_led in zone1 and zone2 and zone3 and zone4:
if i in ledRGB > 0: //This is to minimise colour shift. Any RGB channel set at 0 won’t increase.
i = i+10
else:
return
// Similar for brightness down
void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
{
//The following is mostly copy pasted C++ code with modifications
//It works to understand the IR remote inputs and call functions based on input
const int RECV_PIN = 7;
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long key_value = 0;
void setup(){
irrecv.enableIRIn();
irrecv.blink13(true);
}
void loop(){
if (irrecv.decode(&results)){
if (results.value == 0XFFFFFFFF)
results.value = key_value;
switch(results.value)
case 0xFF30CF:
Serial.println("1");
Call preset1()
break ;
case 0xFF18E7:
Serial.println("2");
Call preset2()
break ;
case 0xFF7A85:
Serial.println("3");
Call preset3()
break ;
//ETC for every number on remote
case 0xFFE01F:
Serial.println("-");
Call BrigtnessDown()
break ;
case 0xFFA857:
Serial.println("+");
Call BrightnessUp()
break ;
}
key_value = results.value;
irrecv.resume();
}
}
Steps to be followed are
Install the FastLED Library from GitHub: FastLED Lib
Install the IRremote Library from GitHub: IRremote Lib
Import it to your Arduino library folder via Arduino IDE: Importing a .zip Library
Here is a basic structure of the program which we will require.
#include "FastLED.h"
#include "IRremote.h"
#define DATA_PIN 5 // digital pin of your arduino
#define NUM_LEDS 300
CRGB leds[NUM_LEDS];
//Zones array
int zone1[2] = {0, 24}; //For zone1: Start,End
int zone2[2] = {25, 172}; //For zone2: Start,End
int zone3[2] = {173, 277}; //For zone3: Start,End
int zone4[2] = {278, 299}; //For zone4: Start,End
// For IR receiver
const int RECV_PIN = 3; // Hardware specs allows 3 or 9 pin to be used for ATmega328p
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup() {
//Setup for FastLED
FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);
//Setup for IRremote
pinMode(LED_BUILTIN, OUTPUT); //LED_BUILTIN is the inbuild led on Arduino UNO at Pin13
irrecv.enableIRIn(); // Start the receiver
irrecv.blink13(true); // Enable blinking the LED when during reception
}
void loop() {
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX);
switch (results.value) {
case 0xFF30CF:
Serial.println("1");
preset1();
break;
case 0xFF18E7:
Serial.println("2");
//preset2(); //TODO: Complete this preset2
break;
case 0xFF7A85:
Serial.println("3");
//preset3(); //TODO: Complete this preset3
break;
//ETC for every number on remote
case 0xFFE01F:
Serial.println("-");
brightnessDown();
break;
case 0xFFA857:
Serial.println("+");
brightnessUp();
break;
default:
Serial.println("Not Understood.");
break;
}
irrecv.resume(); // Receive the next value
}
delay(100);
}
void preset1()
{
//Zone 1
for (int i = zone1[0]; i <= zone1[1]; i++) {
leds[i] = CRGB(160, 0, 210);
}
//Zone 2
for (int i = zone2[0]; i <= zone2[1]; i++) {
leds[i] = CRGB(120, 0, 24);
}
//Zone 3
for (int i = zone3[0]; i <= zone3[1]; i++) {
leds[i] = CRGB(0, 0, 0);
}
//Zone 4
for (int i = zone4[0]; i <= zone4[1]; i++) {
leds[i] = CRGB(100, 100, 100);
}
//To set the LEDs
FastLED.show();
}
void brightnessUp()
{
for (int i = 0; i < NUM_LEDS; i++) {
if (leds[i].r > 0)
leds[i].r = leds[i].r + 10;
if (leds[i].g > 0)
leds[i].g = leds[i].g + 10;
if (leds[i].b > 0)
leds[i].b = leds[i].b + 10;
}
//To set the LEDs
FastLED.show();
}
void brightnessDown()
{
for (int i = 0; i < NUM_LEDS; i++) {
if (leds[i].r > 10)
leds[i].r = leds[i].r - 10;
if (leds[i].g > 10)
leds[i].g = leds[i].g - 10;
if (leds[i].b > 10)
leds[i].b = leds[i].b - 10;
}
//To set the LEDs
FastLED.show();
}
Note: This code this created with the help of Pseudo-code provided and the logic implemented in it, you may have to change certain things in order for it to produce best results.

Handling different variable types as commands

I'm building an Arduino sketch, where i want to communicate with my arduino to control a neorgb strip by adafruit, which is a strip with in my case 60 RGB LEDs with each LED is adressable.
I got it to work with a simple if construct to check if I send the command "off" to switch the whole strip of or I send a hex code for a specific color.
That works fine, but now I want to also dim the brightness of the strip, my function to dim every color component works fine but I need a way to trigger it with a command. My problem is that I don't know how I can handle it if I sent a specific string (in this case "off"), a hex color code or a percentage to dimm my strip.
Also another question is why I need to enter " off" in the serial monitor for the sketch to turn of my strip.
My whole code looks like this:
#include <Adafruit_NeoPixel.h>
//Vars
int stripPin = 6; //NeoPixel Strip Pin
int numberLED = 60; //How many LEDs?
int maxBrightness = 50; //maximum Brightness
String incoming;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(numberLED, stripPin, NEO_GRB + NEO_KHZ800);
void setup() {
// put your setup code here, to run once:
strip.setBrightness(maxBrightness);
strip.begin();
strip.show(); //Clear Strip
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) { //Listening for Commands
incoming = Serial.readString();
if (incoming == "off ") { //if command is "off", switch strip of
setStripColor(0,0,0);
}
else{
// Get rid of '#' and convert it to integer
long number = (long) strtol( &incoming[1], NULL, 16);
// Split them up into r, g, b values
long r = number >> 16;
long g = number >> 8 & 0xFF;
long b = number & 0xFF;
setStripColor(r,g,b); //Set Color for whole Strip
strip.show();
}
Serial.println(incoming);
}
delay(10);
}
int setStripColor(int redValue, int greenValue, int blueValue) //Set Color for whole strip
{
for(int i = 0; i < numberLED; i++ ){
strip.setPixelColor(i, redValue,greenValue,blueValue);
}
}
int dim(int color, int dimVal) //Dim color to dimVal in percent
{
color = color / 100 * dimVal;
return color;
}

Arduino Serial not parsing commands

I am using Processing (java) to communicate over serial to an Arduino which controls two servos and a laser. Everything works independently, however when I turn the laser on using Processing I am unable to control the servos until I turn the laser back off.
Troubleshooting I've tried:
Manually sending commands to the Arduino using Arduino Serial or
Putty results in thethe Arduino working as expected (servo control is
independent of laser status)
Putting the Arduino in the serial-bridge configuration to monitor what
serial commands Processing is sending to the Arduino, and it is as
expected (motor positions, laser status)
I am using an external power supply so I know its not a
max-current-draw issue. (and it works
Included delays in Processing, flushing the Arduino buffer.
Used all "\n", "\r", "\r\n" combinations.
Here is my processing program :
import processing.serial.*;
import org.gamecontrolplus.gui.*;
import org.gamecontrolplus.*;
import net.java.games.input.*;
String ard_ser = "/dev/ttyACM0";
ControlIO control;
ControlDevice stick;
float px, py;
int deg_x, deg_y;
boolean trailOn;
Serial myPort;
ArrayList<PVector> shadows = new ArrayList<PVector>();
ArrayList<PVector> trail = new ArrayList<PVector>();
public void setup() {
size(400, 400);
myPort = new Serial(this, Serial.list()[0],9600);
printArray(Serial.list());
// Initialise the ControlIO
control = ControlIO.getInstance(this);
// Find a device that matches the configuration file
stick = control.getMatchedDevice("joystick");
if (stick == null) {
println("No suitable device configured");
System.exit(-1); // End the program NOW!
}
// Setup a function to trap events for this button
stick.getButton("LASER").plug(this, "toggleLaser", ControlIO.ON_PRESS);
}
// Poll for user input called from the draw() method.
public void getUserInput() {
px = map(stick.getSlider("X").getValue(), -1, 1, 0, width);
deg_x = int(map(stick.getSlider("X").getValue(),-1,1,0,180));
py = map(stick.getSlider("Y").getValue(), -1, 1, 0, height);
deg_y = int(map(stick.getSlider("Y").getValue(),-1,1,0,180));
//stick.getButton("LASER").plug(this,"toggleLaser",ControlIO.);
}
// Event handler for the Laser button
public void toggleLaser() {
println("laser");
if (myPort.available()>0){
myPort.write("-1\n");
}
//delay(1000);
return;
}
public void draw() {
getUserInput(); // Polling
background(255, 255, 240);
// Draw shadows
fill(0, 0, 255, 32);
noStroke();
for (PVector shadow : shadows)
ellipse(shadow.x, shadow.y, shadow.z, shadow.z);
if ( trail.size() > 1) {
stroke(132, 0, 0);
for (int n = 1; n < trail.size(); n++) {
PVector v0 = trail.get(n-1);
PVector v1 = trail.get(n);
line(v0.x, v0.y, v1.x, v1.y);
v0 = v1;
}
}
// Show position
noStroke();
fill(255, 64, 64, 64);
ellipse(px, py, 20, 20);
String position = str(deg_x)+','+str(deg_y)+'\n';
if (myPort.available()>0){
//println(position);
myPort.write(position);
}
delay(10);
}
It has a little visualization to monitor the motor location, and basically just sends "mot_x_pos,mot_y_pos \n", and a "-1\n" to toggle the laser.
example output serial stream:
90,90\n
50,50\n
-1\n
The Arduino code parses the stream and controls the motors/laser:
#include <Servo.h>
bool laser = true;
// true sets the value high (off for my transistor)
char val = 0;
const int laser_pin = 7;
int out1 = 9; //servo pins
int out2 = 11;
boolean newData= true;
Servo servo_x;
Servo servo_y;
int pos_x =0;
int pos_y =0;
int x_prev = 90;
int y_prev = 90;
void setup() {
Serial.begin(9600);
Serial.println("<Arduino is ready>");
servo_x.attach(out1);
servo_y.attach(out2);
pinMode(laser_pin, OUTPUT);
digitalWrite(laser_pin, laser); //turn laser off on startup
}
void loop(){
while(Serial.available()>0){
pos_x = Serial.parseInt();
pos_y = Serial.parseInt();
val = Serial.read(); // this catches the newline escape characters
if ( (pos_x<0) || (pos_y<0) ){
//toggle laser
if(laser){
laser = false;
}
else {laser = true;}
digitalWrite(laser_pin, laser);
}
else if( (val == '\n') || (val == '\r') ){
if(pos_x != x_prev){ //only write to the motors if something has changed
servo_x.write(pos_x);
x_prev = pos_x;
}
if(pos_y != y_prev){
servo_y.write(pos_y);
y_prev = pos_y;
}
}
}
}
Any suggestions would be appreciated, thanks.
Would help if you provided the sequence of commands that's not working.
Your Arduino code tries to read two int's, but you're only sending one with the laser command. That'll get out of synch (parseInt doesn't care about the ends of lines, it's happy to go to the next line)
Try
Having your laser button send "-1,-1" so all lines have two numbers on them
Better, create a better-structured format for your lines: Start with a letter that says whether it's a servo or laser command, then read what's needed, then make sure you find a '\n', then repeat.

DirectShow ISampleGrabber: samples are upside-down and color channels reverse

I have to use MS DirectShow to capture video frames from a camera (I just want the raw pixel data).
I was able to build the Graph/Filter network (capture device filter and ISampleGrabber) and implement the callback (ISampleGrabberCB). I receive samples of appropriate size.
However, they are always upside down (flipped vertically that is, not rotated) and the color channels are BGR order (not RGB).
I tried setting the biHeight field in the BITMAPINFOHEADER to both positive and negative values, but it doesn't have any effect. According to MSDN documentation, ISampleGrapper::SetMediaType() ignores the format block for video data anyways.
Here is what I see (recorded with a different camera, not DS), and what DirectShow ISampleGrabber gives me: The "RGB" is actually in red, green and blue respectively:
Sample of the code I'm using, slightly simplified:
// Setting the media type...
AM_MEDIA_TYPE* media_type = 0 ;
this->ds.device_streamconfig->GetFormat(&media_type); // The IAMStreamConfig of the capture device
// Find the BMI header in the media type struct
BITMAPINFOHEADER* bmi_header;
if (media_type->formattype != FORMAT_VideoInfo) {
bmi_header = &((VIDEOINFOHEADER*)media_type->pbFormat)->bmiHeader;
} else if (media_type->formattype != FORMAT_VideoInfo2) {
bmi_header = &((VIDEOINFOHEADER2*)media_type->pbFormat)->bmiHeader;
} else {
return false;
}
// Apply changes
media_type->subtype = MEDIASUBTYPE_RGB24;
bmi_header->biWidth = width;
bmi_header->biHeight = height;
// Set format to video device
this->ds.device_streamconfig->SetFormat(media_type);
// Set format for sample grabber
// bmi_header->biHeight = -(height); // tried this for either and both interfaces, no effect
this->ds.sample_grabber->SetMediaType(media_type);
// Connect filter pins
IPin* out_pin= getFilterPin(this->ds.device_filter, OUT, 0); // IBaseFilter interface for the capture device
IPin* in_pin = getFilterPin(this->ds.sample_grabber_filter, IN, 0); // IBaseFilter interface for the sample grabber filter
out_pin->Connect(in_pin, media_type);
// Start capturing by callback
this->ds.sample_grabber->SetBufferSamples(false);
this->ds.sample_grabber->SetOneShot(false);
this->ds.sample_grabber->SetCallback(this, 1);
// start recording
this->ds.media_control->Run(); // IMediaControl interface
I'm checking return types for every function and don't get any errors.
I'm thankful for any hint or idea.
Things I already tried:
Setting the biHeight field to a negative value for either the capture device filter or the sample grabber or for both or for neither - doesn't have any effect.
Using IGraphBuilder to connect the pins - same problem.
Connecting the pins before changing the media type - same problem.
Checking if the media type was actually applied by the filter by querying it again - but it apparently is applied or at least stored.
Interpreting the image as total byte reversed (last byte first, first byte last) - then it would be flipped horizontally.
Checking if it's a problem with the video camera - when I test it with VLC (DirectShow capture) it looks normal.
My quick hack for this:
void Camera::OutputCallback(unsigned char* data, int len, void *instance_)
{
Camera *instance = reinterpret_cast<Camera*>(instance_);
int j = 0;
for (int i = len-4; i > 0; i-=4)
{
instance->buffer[j] = data[i];
instance->buffer[j + 1] = data[i + 1];
instance->buffer[j + 2] = data[i + 2];
instance->buffer[j + 3] = data[i + 3];
j += 4;
}
Transport::RTPPacket packet;
packet.payload = instance->buffer;
packet.payloadSize = len;
instance->receiver->Send(packet);
}
It's correct on RGB32 color space, for other color spaces this code need to be corrected
I noticed that when using the I420 color space turning disappears.
In addition, most current codecs (VP8) is used as a format raw I/O I420 color space.
I wrote a simple mirroring frame function in color space I420.
void Camera::OutputCallback(unsigned char* data, int len, uint32_t timestamp, void *instance_)
{
Camera *instance = reinterpret_cast<Camera*>(instance_);
Transport::RTPPacket packet;
packet.rtpHeader.ts = timestamp;
packet.payload = data;
packet.payloadSize = len;
if (instance->mirror)
{
Video::ResolutionValues rv = Video::GetValues(instance->resolution);
int k = 0;
// Chroma values
for (int i = 0; i != rv.height; ++i)
{
for (int j = rv.width; j != 0; --j)
{
int l = ((rv.width * i) + j);
instance->buffer[k++] = data[l];
}
}
// U values
for (int i = 0; i != rv.height/2; ++i)
{
for (int j = (rv.width/2); j != 0; --j)
{
int l = (((rv.width / 2) * i) + j) + rv.height*rv.width;
instance->buffer[k++] = data[l];
}
}
// V values
for (int i = 0; i != rv.height / 2; ++i)
{
for (int j = (rv.width / 2); j != 0; --j)
{
int l = (((rv.width / 2) * i) + j) + rv.height*rv.width + (rv.width/2)*(rv.height/2);
if (l == len)
{
instance->buffer[k++] = 0;
}
else
{
instance->buffer[k++] = data[l];
}
}
}
packet.payload = instance->buffer;
}
instance->receiver->Send(packet);
}