0

RGBW Neopixels strange behavior on power on
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

RGBW Neopixels strange behavior on power on

by syr123 on Fri Aug 11, 2017 5:13 am

Hi there. I'm going to give a summary up top, and then absolutely as much information as I possibly can in cast that might help solve this, as I get frustrated when posts don't have full context, but also don't want to put people off taking a stab at this - it's the last thing I have to crack in this project!

To begin, I've made a video of the problem I'm experiencing:
https://www.youtube.com/watch?v=xHl_tgrwg6A

I've attached a crude schematic, as well.
IMG_2733.JPG
IMG_2733.JPG (611.28 KiB) Viewed 428 times


Issue: When applying power and data to my Neopixels, I sometimes get the wrong color or frozen animation only on startup.

Setup:

- I am running a Python app I wrote which sends commands over USB serial to an Arduino Mega (2560). The Mega is powered off USB.

- The Mega has its hardware Serial1 Tx/Rx pins hooked up to a USB jack. I have an 8V DC regulator (fed by a 12V, 8A wall wart, the regulator is capable of 12A) that shares ground with the Mega. Its Vcc (8V) and Gnd go to the USB jack, too.

- After a 3ft USB cable (which is really only being used as a generic 4 conductor cable), I connect the Tx/Rx to the Serial of an Arduino Micro, which is powered with the 8V and Gnd via it's Vin and Gnd pins. The 8V also goes into a standard LM317T circuit configured with two resistors to output 5V. It shares Gnd with the USB (thus also the 8V and Mega, and the Micro).

- The 5V goes to power two short (60 pixels per meter, black PCB) RGBW Neopixel strips, each 9 pixels long. I have a 1000 uf polarized cap across the Power / Gnd to the Neopixels. The data pin of the pixel strips is tied together and connected to pin 10 of the Micro via a 370 ohm resistor, as I want the strips to be identical. I seem to have the issue even if I only use one strip.

- In this application, the whole strips are the same color, but the number of lights lit up and brightness varies. So, the commands go from the Mac to the Mega, which forwards them over USB to the Micro to tell to set the R, G, B, or W channel, and I have another command for "latching" that into the lights, which actually causes the Micro to call color sets and show on the strip object.

The reason for the forwarding is these lights will live in a peripheral connected to the main enclosure that contains the Mega. I'm trying to make my application impervious to the USB connection being broken and unbroken, so the lights will always work when plugged in.

THEORIES

* I had an idea that maybe the order the pins are contacting the jack is varying depending on how I plug it in, and that is causing variation.

* I wondered if there is a timing issue going on related to how long it takes after I connect USB for the LM317T to start outputting a solid 5V, and how that relates to when the code starts controlling the lights (if this seems suspicious to anyone, I suppose I could sample this on an analog pin of the micro and not init the lights until it hits around 5V). I tried delaying the setup function on the Micro by 2 seconds just to see if that stabilized things, but that didn't seem to reliably fix it.

* I saw in the Uber guide something about trying to always connect ground to the pixels first, then power, but I don't have much control over that because all these connections are made when the USB is plugged in.

I know it's an awful lot, but in case there's something you want to check, I'm including the code below for the Python app, the Mega, and the Micro.

Python
Code: Select all | TOGGLE FULL SIZE
# Commands for talking to the lab and syringe
# Calling the construct commands adds the command to the serial write queue, which
# can be polled and written at a speed of your choice from the main app

import Queue
import time
import serial
from lib.utils import get_all_from_queue, get_item_from_queue, clamp

serial_write_queue = Queue.Queue()
serial_read_queue = Queue.Queue()

serial_port = None

def SetSerialPort(portReference):
    global serial_port
    serial_port = portReference

def GetWriteQueueItems():
    return list(get_all_from_queue(serial_write_queue))

def GetReadQueueItems():
    return list(get_all_from_queue(serial_read_queue))

def WriteSerialQueue():
    if serial_port == None:
        return

    qdata = GetWriteQueueItems()
    for command in qdata:
        try:
            # Can try writing command directly (byte array)
            serial_port.write( command )
        except serial.serialutil.SerialException:
            # Fail silently if port not available. Could close
            # port and stop serial here, but since we have nothing
            # to auto reopen port, we just fail
            return

# Module level state of the most recent values read in
# from serial. Each command read makes a copy of this
# and adds it to the serial queue to be read by check_server_data_queues in Engine
sensors = { "he1" : 0,
            "he2" : 0,
            "he3" : 0,
            "he4" : 0,
            "he5" : 0,
            "he6" : 0,
            "he7" : 0,
            "he8" : 0,
            "he9" : 0,
            "mbut1" : 0,
            "mbut2" : 0,
            "mbut3" : 0,
            "mbut4" : 0,
            "mbut5" : 0,
            "mbut6" : 0,
            "mbut7" : 0,
            "mbut8" : 0,
            "mbut9" : 0,
            "syrpot" : 0,
            "enc1but" : 0,
            "enc1" : 0
          }

channel_to_sensor = {
                    0  : "he9",
                    1  : "he8",
                    2  : "he5",   
                    3  : "he3",
                    4  : "he6",
                    5  : "he7",   
                    6  : "he1",
                    7  : "he2",
                    8  : "he4", 
                    9  : "mbut1",
                    10 : "mbut2",
                    11 : "mbut3",
                    12 : "mbut7",
                    13 : "mbut8",
                    14 : "mbut9",
                    15 : "enc1but",
                    16 : "enc1",
                    17 : "mbut6",
                    18 : "mbut5",
                    19 : "mbut4",
                    20 : "syrpot"
                    }


# 9 lights * 2 strips * 4 channels (RGBW)    =  72 channels on syringe
# 9 wells * 10 lights * 2 strips * 4 channels = 720 channels on lab
# 792 total channels, addressable with 10 bit channels

# I considered using the command topology:
# 3 bit commands = 8 commands
# 10 bit channels = 1024 channels
# 8 bit values = 255 possible values
# So that each channel of each light could be set.
#
# 792 channels would take 792 commands, plus two latch commands to set all the colors
# that's 794 * 3 bytes * 8 bits = 19056 bits/symbols
# I'm running at 57600 the fastest reliable rate. That means I could only send
# 3 full updates of all channels per second this way, which just isn't fast enough.
# For that reason, I'm changing in favor of a larger command space, so I can do things
# in software to handle colors, and play only with the level/height from the Engine side.
#
# If I wanted to go back to the idea of addressing every channel, I suppose this could
# be made more efficient by usign an indexed color palette
#
# Instead going with:
# 3 byte commands
#
#           Bit #
#         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
# Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
# Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
# Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

# 6 bit commands  - 64 commands

    #Commands 0 - 32 reserved for messages going from Engine to Mega/Micro
    #Commands 33 - 63 reserves for messages going from Mega/Micro to here

# 5 bit channels  - 32 channels
# 10 bit values   - 1024 resolution

#Channels:

#0 - Syringe
#1 - Well 1
#2 - Well 2
#3 - Well 3
#4 - Well 4
#5 - Well 5
#6 - Well 6
#7 - Well 7
#8 - Well 8
#9 - Well 9


#0 - Set R - Command #, Target a well or syringe channel, red PWM intensity
def ConstructSetRCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00000001 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#1 - Set G - Command #, Target a well or syringe channel, green PWM intensity
def ConstructSetGCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00000101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#2 - Set B - Command #, Target a well or syringe channel, blue PWM intensity
def ConstructSetBCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00001001 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#3 - Set W - Command #, Target a well or syringe channel, white PWM intensity
def ConstructSetWCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00001101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#4 - Latch Lab and Syringe - Command #, no op channel, no op value
def ConstructLatchCommand():
    serial_write_queue.put('\x11\x00\x00')

#5 - Latch Syringe - Command #, no op channel, no op value
def ConstructSetHeightCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00010101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)





def serialDataAvailable():
    try:
        checkPort = serial_port.inWaiting()
    except IOError:
        return False

    return checkPort > 0

# Regarding this code being called by a timer, QTimer docs say:
# "If Qt is unable to deliver the requested number of timer clicks,
# it will silently discard some."
# Not entirely clear if this means because it can't schedule them because
# the main event loop or some other system process is going, or because
# it's trying to only run
def ReadSerialPort():
    if serial_port == None:
        return

    while serialDataAvailable():

        # A command is 3 bytes.
        # Start trying to parse command if more than 2 bytes are
        # available.
        if serial_port.inWaiting() > 2:

            #XXX for some reason when I sent no data, it found a str
            # of length zero? try this and catch it?
            # XXX REMOVE ALL THESE TRIES WHAT IS GOING ON?????
            # SERIOUSLY WHAT
            try:
                byte1 = ord(serial_port.read())
            except TypeError:
                return

            # If we're not to a byte with the status bit (rightmost)
            # set to 1 keep reading bytes and essentially throwing
            # them away. If this means we run out of bytes in the buffer,
            # exit the loop
            while (not (byte1 & 1)) and serial_port.inWaiting() > 0:
                try:
                    byte1 = ord(serial_port.read())
                except TypeError:
                    return

            # Make sure we exited the previous loop because the
            # status bit (rightmost) of the first byte was 1, and
            # not just because we ran out of bytes in the serial buffer.
            if byte1 & 1:
                try:
                    byte2 = ord(serial_port.read())
                    byte3 = ord(serial_port.read())
                except TypeError:
                    return


                # At this point, we have a 3 byte command: start parsing
                command  = ( 0b11111100 & byte1 ) >> 2
                channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 )
                value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 )

                # Command 33, from Mega: Channel and sensor value
                if command == 33:


                    print channel, value

                    # Normalize analog syringe pot
                    if channel == 20:
                        value /= 1023.
                        value = 1.0 - value

                    value = min(1.0, value)
                    value = max(0.0, value)


                    try:
                        sensor_key = channel_to_sensor[channel]
                        global sensors
                        sensors[sensor_key] = value
                        entry = [time.clock(), sensors.copy()]
                        serial_read_queue.put( entry )

                        # XXX DELETE ME #XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
                        f = open("/Users/mtf/Desktop/sensors", "w")
                        f.write( str(pprint.pformat(entry)) )
                        f.close()

                    except KeyError:
                        print "Bogus channel"
                        print channel

                # Command 34, from Mega, Syringe connection state (0/1), channel 0
                if command == 34:
                    print "Syringe connection message"
                    print channel, value
                    print












### XXX XXX XXX DELETE ME

def enumerate_serial_ports():
    from serial.tools import list_ports
    return [x[0] for x in list_ports.comports()]

ports = enumerate_serial_ports()
portname = None
for x in ports:
    if x.find("usbmodem14241") != -1:
        portname = x


ssss = serial.Serial(    port = portname,
                         baudrate = 57600,
                         bytesize = serial.EIGHTBITS,
                         parity = serial.PARITY_NONE,
                         stopbits = serial.STOPBITS_ONE,
                         # timeout 0 means non-blocking mode (return immediately on read)
                         # If we don't get data, the timer function immediately returns to be
                         # called again. Since we're polling at an interval, a timeout isn't important.
                         # If we were in an infinite loop where the blocking timeout helped us wait
                         # the interval before trying to read again, we might use a timeout.
                         # Note that timeout = None will block / wait forever: the function would not
                         # return until data came in
                         timeout = 0.0)
SetSerialPort(ssss)

import time
import math
import LabColor
import os
import pprint

smess = []

# Arduino resets after connected, so we
# give it time to to just chill -- THIS WOULD BE MORE RELIABLE
# IF WE ACTUALLY WAITED ON A MESSAGE TO CONFIRM CONNECTION AND READINESS
time.sleep(1.5)

# Flush any data out of the Arduino
serial_port.flush()
         

def setSyringeToColorAndHeight(r, g, b, w, h):
    ConstructSetRCommand(0, r)
    ConstructSetGCommand(0, g)
    ConstructSetBCommand(0, b)
    ConstructSetWCommand(0, w)
    WriteSerialQueue()

    ConstructSetHeightCommand(0, h)
    WriteSerialQueue()
   
    ConstructLatchCommand()
    WriteSerialQueue()

c1 = LabColor.Color(1.0, 0.0, 0.0)
c2 = LabColor.Color(0, 0.0, 1.0)

def WriteToMega():
    n = LabColor.hsl_color_lerp(c1, c2, (math.sin(5*time.time())+1)*.5)
    # Set the syringe light to yellow and half height
    height = int(((math.sin(time.time()*2.0)+1)*.5) * 1000.0)
   
    setSyringeToColorAndHeight(int(n.r*255.0), int(n.g*255.0), int(n.b*255.0), 0, height)

def ReadFromMega():
    ReadSerialPort()

while True:

    # Sleep a bit so we don't flood the buffers with commands
    # Timer takes care of this in the real app
    time.sleep(.01)

    WriteToMega()
    ReadFromMega()
   
    # READ
    #if serial_port != None:
    #    while serialDataAvailable():
    #        if serial_port.inWaiting() > 0:
    #            smess.append( serial_port.read() )

    #    if len(smess) > 0:
    #        print "".join(smess)
    #        smess = []

#### XXX XXX XXX DELETE ME


Mega
Code: Select all | TOGGLE FULL SIZE
// Notes
//
// analogReference(EXTERNAL); can be used when an external ground
// is applied to the Arduino's AREF pin
//
// Serial.write can be used to directly write bytes, which
// may be better than using ASCII values with print
//
// baud is 'symbols per second'
// There can be a distinction between BIT rate and BAUD rate, but for
// asynchronous communication they are effectively the same thing.
//
// If we want to, say, send 2 byte readings of 10 bit values with sensor channel
// every 5000 microseconds (5 ms)
// That's 200 sends per second * 2 bytes * 8 bits = 3200 baud,
// so we'd select 4800 baud for the Arduino, smallest available rate >= 3200 baud

// Some inspiration from:
// http://balau82.wordpress.com/2011/03/26/capturing-an-analog-signal-with-arduino-and-python/

// ********************************************************
// LIGHTING
// ********************************************************

// MegaBrite LEDs

// ints are 16 bit (2-byte) on ATMega
// unsigned int LEDChannels[NumLEDs][3] = {0};

// Maps sensible well light indices to the way they are actually wired
// in the daisy chain:
// Sensible
// 0  1  2
// 3  4  5
// 6  7  8 
//
// As wired
// 6  7  8
// 5  4  3
// 0  1  2 
//
// Since we have to push through a chain,
// the last light must come first, so
// think of the indices like this and remap
// to 'sensible':
// 2  1  0
// 3  4  5
// 8  7  6 

byte LEDRemap[9] = { 2, 1, 0, 3, 4, 5, 8, 7, 6 };
byte SyringeCommand[3];

int byte1, byte2, byte3, forward;
int command, channel, value;

bool syringe_connected = 0;

// ********************************************************
// END LIGHTING
// ********************************************************

// ********************************************************
// Wells and Buttons
// ********************************************************

#define SENSOR_CHANGE_THRESHOLD 0

// 9 wells, 6 arcade buttons
#define NUM_CHANNELS 15

#define WELL1_PIN 22
#define WELL1_CHANNEL 0
#define WELL2_PIN 23
#define WELL2_CHANNEL 1
#define WELL3_PIN 24
#define WELL3_CHANNEL 2
#define WELL4_PIN 25
#define WELL4_CHANNEL 3
#define WELL5_PIN 26
#define WELL5_CHANNEL 4
#define WELL6_PIN 27
#define WELL6_CHANNEL 5
#define WELL7_PIN 28
#define WELL7_CHANNEL 6
#define WELL8_PIN 29
#define WELL8_CHANNEL 7
#define WELL9_PIN 30
#define WELL9_CHANNEL 8

#define ARCADE1_PIN 31
#define ARCADE1_CHANNEL 9
#define ARCADE2_PIN 32
#define ARCADE2_CHANNEL 10
#define ARCADE3_PIN 33
#define ARCADE3_CHANNEL 11
#define ARCADE4_PIN 34
#define ARCADE4_CHANNEL 12
#define ARCADE5_PIN 35
#define ARCADE5_CHANNEL 13
#define ARCADE6_PIN 37
#define ARCADE6_CHANNEL 14

// ********************************************************
// TEMPORARY OLD SYRINGE
// ********************************************************

//#define SYRINGE_BUTTON1_PIN 8
//#define SYRINGE_BUTTON1_CHANNEL 17
//#define SYRINGE_BUTTON2_PIN 9
//#define SYRINGE_BUTTON2_CHANNEL 18
//#define SYRINGE_BUTTON3_PIN 10
//#define SYRINGE_BUTTON3_CHANNEL 19
//#define SYRINGE_POT_PIN 0
//#define SYRINGE_POT_CHANNEL 20

// ********************************************************
// END TEMPORARY OLD SYRINGE
// ********************************************************

// How often to read and send sensor values in
// microseconds
#define SENSOR_SEND_MICROS 5000

unsigned long previousMicros = 0;
unsigned long elapsedTime = 0;

// Hold previous sensor values such that we only
// send on change
int sensorValues[NUM_CHANNELS];
int sensorValue = 0;

// ********************************************************
// End Wells and Buttons
// ********************************************************

byte SensorCommand[3];

#define BAUD_RATE 57600
#define SYRINGE_SERIAL_BAUD_RATE 57600

void setup() {

  // ********************************************************
  // LIGHTING
  // ********************************************************
   
  // TODO REPLACE WITH NEOPIXEL SETUP
   
  // ********************************************************
  // END LIGHTING
  // ********************************************************
   
  // ********************************************************
  // SENSORS
  // ********************************************************
   
  // init sensor values to bogus value 
  for(int i=0; i < NUM_CHANNELS; i++){
    sensorValues[i] = -1;
  }

  // Serial1 Tx / Rx pullup to pull lines up to 5V,
  // the idle state
  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);
 
  // Well Reed Switches
  pinMode(WELL1_PIN, INPUT_PULLUP);
  pinMode(WELL2_PIN, INPUT_PULLUP);
  pinMode(WELL3_PIN, INPUT_PULLUP);
  pinMode(WELL4_PIN, INPUT_PULLUP);
  pinMode(WELL5_PIN, INPUT_PULLUP);
  pinMode(WELL6_PIN, INPUT_PULLUP);
  pinMode(WELL7_PIN, INPUT_PULLUP);
  pinMode(WELL8_PIN, INPUT_PULLUP);
  pinMode(WELL9_PIN, INPUT_PULLUP);

  // Arcade Buttons
  pinMode(ARCADE1_PIN, INPUT_PULLUP);
  pinMode(ARCADE2_PIN, INPUT_PULLUP);
  pinMode(ARCADE3_PIN, INPUT_PULLUP);
  pinMode(ARCADE4_PIN, INPUT_PULLUP);
  pinMode(ARCADE5_PIN, INPUT_PULLUP);
  pinMode(ARCADE6_PIN, INPUT_PULLUP);

  // ********************************************************
  // TEMPORARY OLD SYRINGE
  // ********************************************************

  //pinMode(SYRINGE_BUTTON1_PIN, INPUT_PULLUP);
  //pinMode(SYRINGE_BUTTON2_PIN, INPUT_PULLUP);
  //pinMode(SYRINGE_BUTTON3_PIN, INPUT_PULLUP);

  // ********************************************************
  // END TEMPORARY OLD SYRINGE
  // ********************************************************

  // ********************************************************
  // END SENSORS
  // ********************************************************

  // ********************************************************
  // SERIAL
  // ********************************************************

  // Serial connection to the Mac
  Serial.begin(BAUD_RATE);
  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  while(!Serial);

  // Serial conneciton to the Syringe / Arduino Micro
  Serial1.begin(SYRINGE_SERIAL_BAUD_RATE);
  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  while(!Serial1);
 
  //Serial.println("Started...");
 
  // delay(100);
  // ********************************************************
  // END SERIAL
  // ********************************************************
}

void loop() {

    // Read all sensors and send out values
    readSensors();
   
    // Check for commands from Mac
    checkSerialPortForCommand();
   
    // Any commands coming in for the syringe are
    // intended to broadcast sensor values, and can
    // be forwarded to the Mac
    forwardSerialBytesFromSyringe();

}

void readSensors() {
 
  // Elapsed time calculation done with difference and unsigned long to avoid
  // issues when micros overflows back to 0. See this tip:
  // https://arduino.stackexchange.com/questions/22994/resetting-millis-and-micros
 
  elapsedTime = micros() - previousMicros;

  if( elapsedTime >= SENSOR_SEND_MICROS) {

    // Arcade Buttons
    digitalSensorRead(ARCADE1_PIN, ARCADE1_CHANNEL, 1);
    digitalSensorRead(ARCADE2_PIN, ARCADE2_CHANNEL, 1);
    digitalSensorRead(ARCADE3_PIN, ARCADE3_CHANNEL, 1);
    digitalSensorRead(ARCADE4_PIN, ARCADE4_CHANNEL, 1);
    digitalSensorRead(ARCADE5_PIN, ARCADE5_CHANNEL, 1);
    digitalSensorRead(ARCADE6_PIN, ARCADE6_CHANNEL, 1);
   
    // Well Reed Switches
    digitalSensorRead(WELL1_PIN, WELL1_CHANNEL, 1);
    digitalSensorRead(WELL2_PIN, WELL2_CHANNEL, 1);
    digitalSensorRead(WELL3_PIN, WELL3_CHANNEL, 1);
    digitalSensorRead(WELL4_PIN, WELL4_CHANNEL, 1);
    digitalSensorRead(WELL5_PIN, WELL5_CHANNEL, 1);
    digitalSensorRead(WELL6_PIN, WELL6_CHANNEL, 1);
    digitalSensorRead(WELL7_PIN, WELL7_CHANNEL, 1);
    digitalSensorRead(WELL8_PIN, WELL8_CHANNEL, 1);
    digitalSensorRead(WELL9_PIN, WELL9_CHANNEL, 1);
   
    // ********************************************************
    // TEMPORARY OLD SYRINGE
    // ********************************************************
    //digitalSensorRead(SYRINGE_BUTTON1_PIN, SYRINGE_BUTTON1_CHANNEL, 1);
    //digitalSensorRead(SYRINGE_BUTTON2_PIN, SYRINGE_BUTTON2_CHANNEL, 1);
    //digitalSensorRead(SYRINGE_BUTTON3_PIN, SYRINGE_BUTTON3_CHANNEL, 1);
   
    //analogSensorRead(SYRINGE_POT_PIN, SYRINGE_POT_CHANNEL);

    // ********************************************************
    // END TEMPORARY OLD SYRINGE
    // ********************************************************

    previousMicros = micros();
   
  }
}

// Construct CMD 33, to send a channel and value to the Mac
void sendChannelAndValue(int channel, int value) {
   
    SensorCommand[0] = 0b10000101 | (channel & 0b10000) >> 3;
    SensorCommand[1] = ((channel & 0b001111) << 4) | ((value & 0b1110000000) >> 6 );
    SensorCommand[2] = (value & 0b0001111111) << 1;
    Serial.write(SensorCommand, 3);

}

// Construct CMD 34, which lets the Engine know the syringe is CONNECTED or DISCONNECTED
void sendSyringeConnectedMessageToEngine(int value) {
 
    SensorCommand[0] = 0b10001001;
    SensorCommand[1] = (value & 0b1110000000) >> 6 ;
    SensorCommand[2] = (value & 0b0001111111) << 1;
    Serial.write(SensorCommand, 3);
   
}

void digitalSensorRead(int pin, int channel, byte invert) {
    sensorValue = digitalRead(pin);
   
    // The second condition, checking that the old value is >= 0, makes sure we don't
    // send the initial state when all of sensorValues = -1 at init
    if( abs(sensorValue - sensorValues[channel]) > SENSOR_CHANGE_THRESHOLD && sensorValues[channel] >= 0 ){
      if(invert == 1){
        sendChannelAndValue(channel, 1-sensorValue);
      }
      else {
        sendChannelAndValue(channel, sensorValue);
      }
    }
    sensorValues[channel] = sensorValue;
}

void analogSensorRead(int pin, int channel) {
    sensorValue = analogRead(pin);
   
    //if( abs(sensorValue - sensorValues[channel]) > 2 && sensorValues[channel] >= 0 ){
    sendChannelAndValue(channel, sensorValue);
    //}
    sensorValues[channel] = sensorValue;
}

void forwardSerialBytesFromSyringe() {
  //if ( syringe_connected ) {
    while ( Serial1.available() > 0 ) {
   
      forward = Serial1.read();
      Serial.write(forward);
     
    }
  //}
 
}

// Commands go a little something like this:
//           Bit #
//         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
// Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
// Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
// Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

void checkSerialPortForCommand() {
   
  while ( Serial.available() > 0 ) {
   
    // Serial receive buffer is 64 bytes. A command is 3 bytes.
    // Start trying to parse command if more than 2 bytes are
    // available.
    if ( Serial.available() > 2 ) {
      byte1 = Serial.read();
     
      // If we're not to a byte with the status bit (rightmost)
      // set to 1 keep reading bytes and essentially throwing
      // them away. If this means we run out of bytes in the buffer,
      // exit the loop
      while( !(byte1 & 1) && Serial.available() > 0 ) {
        byte1 = Serial.read();
      }
     
      // Make sure we exited the previous loop because the
      // status bit (rightmost) of the first byte was 1, and
      // not just because we ran out of bytes in the serial buffer.
      if ( byte1 & 1 ) {
        byte2 = Serial.read();
        byte3 = Serial.read();
       
        // At this point, we have a 3 byte command: start parsing
        command  = ( 0b11111100 & byte1 ) >> 2;
        channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 );
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );

        // Set R
        if ( command == 0 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 0 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Set G
        else if ( command == 1 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 1 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }       

        // Set B
        else if ( command == 2 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 2 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Set W
        else if ( command == 3 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 3 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Latch
        else if ( command == 4 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 4 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }
       
        // Set Height
        else if ( command == 5 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 5 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }
       
      }
    }
  }
}


Micro
Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Firmware for the Arduino Micro in syringe with ATmega 32U4

// If IDE upload with autoreset isn't working, try:
// https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
// These differences affect the way you use the physical reset button to perform
// an upload if the auto-reset isn't working. Press and hold the reset button
// on the Leonardo or Micro, then hit the upload button in the Arduino software.
// Only release the reset button after you see the message "Uploading..." appear
// in the software's status bar. When you do so, the bootloader will start,
// creating a new virtual (CDC) serial port on the computer. The software
// will see that port appear and perform the upload using it.

#define NUMBER_OF_SYRINGE_LEDS 9

#define LINEAR_POT A3 // Analog A3

#define SYRINGE_BUTTON_1 4 // Digital 4
#define SYRINGE_BUTTON_2 7
#define SYRINGE_BUTTON_3 12

#define SYRINGE_SERIAL_BAUD_RATE 57600
#define SENSOR_SEND_MICROS 5000

#define NEOPIXEL_PIN 10
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMBER_OF_SYRINGE_LEDS,
                                            NEOPIXEL_PIN,
                                            NEO_GRBW + NEO_KHZ800);


// Internal values set by forwarded commands
int R = 0;
int G = 0;
int B = 0;
int W = 0;
int height = 0;

float modr = 0;
int divr = 0;

int SYRINGE_VALUE = 0;
int SYRINGE_BUTTON_1_VALUE = 0;
int SYRINGE_BUTTON_2_VALUE = 0;
int SYRINGE_BUTTON_3_VALUE = 0;

byte Command[3];

byte clr;
int byte1, byte2, byte3;
int command, channel, value;
int sensorValue;

unsigned long previousMicros = 0;
unsigned long elapsedTime = 0;

void setup() {     
 
  // Syringe Buttons
  pinMode(SYRINGE_BUTTON_1, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_2, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_3, INPUT_PULLUP);
 
  // On the Micro, Serial1 is the hardware serial port, Serial is the virtual
  // serial driver.
  Serial1.begin(SYRINGE_SERIAL_BAUD_RATE);

  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  while(!Serial1);

  // BEGIN LIGHTING
  strip.begin();
  // Initialize all pixels to 'off'
  strip.show();
 
}

void loop() {

  //readSensors();
  //delay(100);
  checkSerialPortForCommand();
 
}

void readSensors() {

  // Elapsed time calculation done with difference and unsigned long to avoid
  // issues when micros overflows back to 0. See this tip:
  // https://arduino.stackexchange.com/questions/22994/resetting-millis-and-micros
 
  elapsedTime = micros() - previousMicros;
 
  if( elapsedTime >= SENSOR_SEND_MICROS) {
   
    // See OXullo Intersecans' answer on Quora
    // http://www.quora.com/Why-is-a-little-delay-needed-after-analogRead-in-Arduino
    // For explanation of why we're calling analogRead twice and ignoring the
    // first result. Effectively, the ADC is connceted to the analog pins via a MUX
    // with with a cap. If the cap isn't given time to discharge, it can influence
    // the ADC readings

    // SLIDE LINEAR POT
    analogRead(LINEAR_POT);
    sensorValue = analogRead(LINEAR_POT);
    SYRINGE_VALUE = sensorValue;
    //sendChannelAndValue(22, sensorValue);

    // SYRINGE BUTTONS
    sensorValue = digitalRead( SYRINGE_BUTTON_1 );
    SYRINGE_BUTTON_1_VALUE = sensorValue;
    //sendChannelAndValue(19, !sensorValue);
   
    sensorValue = digitalRead( SYRINGE_BUTTON_2 );
    SYRINGE_BUTTON_2_VALUE = sensorValue;
    //sendChannelAndValue(20, !sensorValue);
   
    sensorValue = digitalRead( SYRINGE_BUTTON_3 );
    SYRINGE_BUTTON_3_VALUE = sensorValue;
    //sendChannelAndValue(21, !sensorValue);

    previousMicros = micros();

  }

}

void latchLight() {

  // Height is 10 bit int (0 - 1023)
  // This modr / divr business is used to fade the intensity
  // of the leading edge light to reduce stepping. It's effectively
  // a 1 neopixel wide filter
 
  modr = (height % 102) / 102.0f;
  divr = height / 102;

  for(uint16_t i=0; i<strip.numPixels(); i++) {

      if( i < divr ) {
        strip.setPixelColor(i, strip.Color(R,G,B,W));
      }
      else {
        if ( i == divr ){
          strip.setPixelColor(i, strip.Color(R*modr, G*modr, B*modr, W*modr));
        }
        else {
          strip.setPixelColor(i, strip.Color(0,0,0,0));   
        }
      }
   
  }
 
  strip.show();

}

// Construct CMD 4, to send a channel and value to the lab, then the Mac
void sendChannelAndValue(int channel, int value) {
     
    Command[0] = 0b00100001 | (channel & 0b110000) >> 3;
    Command[1] = ((channel & 0b001111) << 4) | ((value & 0b1110000000) >> 6 );
    Command[2] = (value & 0b0001111111) << 1;
    //Serial1.write(Command, 3);

}

// Commands go a little something like this:
//           Bit #
//         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
// Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
// Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
// Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

void checkSerialPortForCommand() {
   
  while ( Serial1.available() > 0 ) {
   
    // Serial receive buffer is 64 bytes. A command is 3 bytes.
    // Start trying to parse command if more than 2 bytes are
    // available.
    if ( Serial1.available() > 2 ) {
      byte1 = Serial1.read();
     
      // If we're not to a byte with the status bit (rightmost)
      // set to 1 keep reading bytes and essentially throwing
      // them away. If this means we run out of bytes in the buffer,
      // exit the loop
      while( !(byte1 & 1) && Serial1.available() > 0 ) {
        byte1 = Serial1.read();
      }
     
      // Make sure we exited the previous loop because the
      // status bit (rightmost) of the first byte was 1, and
      // not just because we ran out of bytes in the serial buffer.
      if ( byte1 & 1 ) {
        byte2 = Serial1.read();
        byte3 = Serial1.read();
       
        // At this point, we have a 3 byte command: start parsing
        command  = ( 0b11111100 & byte1 ) >> 2;
        channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 );
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );

        // Set R channel
        if ( command == 0 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              R = value;
          }
        }

        // Set G channel
        else if ( command == 1 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              G = value;
          }
        }       

        // Set B channel
        else if ( command == 2 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              B = value;
          } 
        }

        // Set W channel
        else if ( command == 3 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              W = value;
          }
        }

        // Latch
        else if ( command == 4 ) {
            latchLight();   
        }
       
        // Set height
        else if ( command == 5 ) {
            height = value;   
        }
       
      }
    }
  }
}


Here are photos of the setup as wired for now:

Image
Image
Image
Image
Image
free image cdn

syr123
 
Posts: 4
Joined: Fri Aug 11, 2017 4:41 am

Re: RGBW Neopixels strange behavior on power on

by adafruit_support_rick on Fri Aug 11, 2017 9:00 am

A few things. How are the USB plugs wired? USB plugs are deigned so that the power and ground pins connect first when you plug it in. If you look at a standard A connector, powr and ground are the outer pins, and data are the inner pins. You can see that the power and ground pins are longer so that they connect first. You should make sure that your RX/TX pins are the center pair.

I don't think while(!Serial1) will do anything. That's a usb serial thing.

We don't recommend putting two strips on the same data output pin. Not sure why you have it that way.

I might try throwing away whatever data you receive from the mega for a little while before expecting it to be valid. Put something like this in setup:
Code: Select all | TOGGLE FULL SIZE
for (int i = 0; i < 50; i++)
{
  Serial1.read();
  delay(1);
}

adafruit_support_rick
 
Posts: 34871
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: RGBW Neopixels strange behavior on power on

by syr123 on Mon Aug 14, 2017 3:10 am

Hi there,

Thanks for your suggestions. I've tried each and unfortunately am still experiencing the issue of seeing the strip go all white and have blocky stepping, or freeze on a single color.

- Confirmed the USB is wired as expected - Vcc/Gnd on outer pins, Tx/Rx on the center D+, D- pins.
- Switched to another breakout board for the USB A female, just in case the contact order was bogus on the one I was using.
- Broke the Neopixels out to two pins on the Arduino Micro (9/10), with a separate 470 ohm resistor between the Micro and the Neopixel data line of each.

At this point, tested, still had the issue. Then:

- Commented out while(!Serial1)
- Added the loop you suggested to throw out a bunch of serial reads

When I had this loop iterate 50 times, the problem seemed pretty sporadic. I increased the loop to about 1000 iterations, and I seemed to get the problem with even greater frequency.

I'm including the (marginally modified) Arduino Micro code below, all the other is the same as in my first post.

What else might I try?

Thanks for your time and support so far!

Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Firmware for the Arduino Micro in syringe with ATmega 32U4

// If IDE upload with autoreset isn't working, try:
// https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
// These differences affect the way you use the physical reset button to perform
// an upload if the auto-reset isn't working. Press and hold the reset button
// on the Leonardo or Micro, then hit the upload button in the Arduino software.
// Only release the reset button after you see the message "Uploading..." appear
// in the software's status bar. When you do so, the bootloader will start,
// creating a new virtual (CDC) serial port on the computer. The software
// will see that port appear and perform the upload using it.

#define NUMBER_OF_SYRINGE_LEDS 9

#define LINEAR_POT A3 // Analog A3

#define SYRINGE_BUTTON_1 4 // Digital 4
#define SYRINGE_BUTTON_2 7
#define SYRINGE_BUTTON_3 12

#define SYRINGE_SERIAL_BAUD_RATE 57600
#define SENSOR_SEND_MICROS 5000

#define NEOPIXEL_PIN_1 9
#define NEOPIXEL_PIN_2 10
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(NUMBER_OF_SYRINGE_LEDS,
                                            NEOPIXEL_PIN_1,
                                            NEO_GRBW + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(NUMBER_OF_SYRINGE_LEDS,
                                            NEOPIXEL_PIN_2,
                                            NEO_GRBW + NEO_KHZ800);

// Internal values set by forwarded commands
int R = 0;
int G = 0;
int B = 0;
int W = 0;
int height = 0;

float modr = 0;
int divr = 0;

int SYRINGE_VALUE = 0;
int SYRINGE_BUTTON_1_VALUE = 0;
int SYRINGE_BUTTON_2_VALUE = 0;
int SYRINGE_BUTTON_3_VALUE = 0;

byte Command[3];

byte clr;
int byte1, byte2, byte3;
int command, channel, value;
int sensorValue;

unsigned long previousMicros = 0;
unsigned long elapsedTime = 0;

void setup() {     
 
  // Syringe Buttons
  pinMode(SYRINGE_BUTTON_1, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_2, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_3, INPUT_PULLUP);
 
  // On the Micro, Serial1 is the hardware serial port, Serial is the virtual
  // serial driver.
  Serial1.begin(SYRINGE_SERIAL_BAUD_RATE);

  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  // NOTE - think perhaps this only works for soft serial (Serial, not Serial1)
  //while(!Serial1);

  // Throwing away whatever data you receive from the mega for a
  // little while before expecting it to be valid
  for (int i = 0; i < 1000; i++)
  {
    Serial1.read();
    delay(1);
  }

  // BEGIN LIGHTING
  strip1.begin();
  strip2.begin();
  // Initialize all pixels to 'off'
  strip1.show();
  strip2.show();
}

void loop() {

  //readSensors();
  //delay(100);
  checkSerialPortForCommand();
 
}

void readSensors() {

  // Elapsed time calculation done with difference and unsigned long to avoid
  // issues when micros overflows back to 0. See this tip:
  // https://arduino.stackexchange.com/questions/22994/resetting-millis-and-micros
 
  elapsedTime = micros() - previousMicros;
 
  if( elapsedTime >= SENSOR_SEND_MICROS) {
   
    // See OXullo Intersecans' answer on Quora
    // http://www.quora.com/Why-is-a-little-delay-needed-after-analogRead-in-Arduino
    // For explanation of why we're calling analogRead twice and ignoring the
    // first result. Effectively, the ADC is connceted to the analog pins via a MUX
    // with with a cap. If the cap isn't given time to discharge, it can influence
    // the ADC readings

    // SLIDE LINEAR POT
    analogRead(LINEAR_POT);
    sensorValue = analogRead(LINEAR_POT);
    SYRINGE_VALUE = sensorValue;
    //sendChannelAndValue(22, sensorValue);

    // SYRINGE BUTTONS
    sensorValue = digitalRead( SYRINGE_BUTTON_1 );
    SYRINGE_BUTTON_1_VALUE = sensorValue;
    //sendChannelAndValue(19, !sensorValue);
   
    sensorValue = digitalRead( SYRINGE_BUTTON_2 );
    SYRINGE_BUTTON_2_VALUE = sensorValue;
    //sendChannelAndValue(20, !sensorValue);
   
    sensorValue = digitalRead( SYRINGE_BUTTON_3 );
    SYRINGE_BUTTON_3_VALUE = sensorValue;
    //sendChannelAndValue(21, !sensorValue);

    previousMicros = micros();

  }

}

void latchLight() {

  // Height is 10 bit int (0 - 1023)
  // This modr / divr business is used to fade the intensity
  // of the leading edge light to reduce stepping. It's effectively
  // a 1 neopixel wide filter
 
  modr = (height % 102) / 102.0f;
  divr = height / 102;

  // Assumes strip1 and 2 have the same number of pixels
  for(uint16_t i=0; i<strip1.numPixels(); i++) {

      if( i < divr ) {
        strip1.setPixelColor(i, strip1.Color(R,G,B,W));
        strip2.setPixelColor(i, strip2.Color(R,G,B,W));
      }
      else {
        if ( i == divr ){
          strip1.setPixelColor(i, strip1.Color(R*modr, G*modr, B*modr, W*modr));
          strip2.setPixelColor(i, strip2.Color(R*modr, G*modr, B*modr, W*modr));
        }
        else {
          strip1.setPixelColor(i, strip1.Color(0,0,0,0));   
          strip2.setPixelColor(i, strip2.Color(0,0,0,0)); 
        }
      }
   
  }
 
  strip1.show();
  strip2.show();

}

// Construct CMD 4, to send a channel and value to the lab, then the Mac
void sendChannelAndValue(int channel, int value) {
     
    Command[0] = 0b00100001 | (channel & 0b110000) >> 3;
    Command[1] = ((channel & 0b001111) << 4) | ((value & 0b1110000000) >> 6 );
    Command[2] = (value & 0b0001111111) << 1;
    //Serial1.write(Command, 3);

}

// Commands go a little something like this:
//           Bit #
//         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
// Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
// Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
// Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

void checkSerialPortForCommand() {
   
  while ( Serial1.available() > 0 ) {
   
    // Serial receive buffer is 64 bytes. A command is 3 bytes.
    // Start trying to parse command if more than 2 bytes are
    // available.
    if ( Serial1.available() > 2 ) {
      byte1 = Serial1.read();
     
      // If we're not to a byte with the status bit (rightmost)
      // set to 1 keep reading bytes and essentially throwing
      // them away. If this means we run out of bytes in the buffer,
      // exit the loop
      while( !(byte1 & 1) && Serial1.available() > 0 ) {
        byte1 = Serial1.read();
      }
     
      // Make sure we exited the previous loop because the
      // status bit (rightmost) of the first byte was 1, and
      // not just because we ran out of bytes in the serial buffer.
      if ( byte1 & 1 ) {
        byte2 = Serial1.read();
        byte3 = Serial1.read();
       
        // At this point, we have a 3 byte command: start parsing
        command  = ( 0b11111100 & byte1 ) >> 2;
        channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 );
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );

        // Set R channel
        if ( command == 0 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              R = value;
          }
        }

        // Set G channel
        else if ( command == 1 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              G = value;
          }
        }       

        // Set B channel
        else if ( command == 2 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              B = value;
          } 
        }

        // Set W channel
        else if ( command == 3 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              W = value;
          }
        }

        // Latch
        else if ( command == 4 ) {
            latchLight();   
        }
       
        // Set height
        else if ( command == 5 ) {
            height = value;   
        }
       
      }
    }
  }
}

syr123
 
Posts: 4
Joined: Fri Aug 11, 2017 4:41 am

Re: RGBW Neopixels strange behavior on power on

by adafruit_support_rick on Mon Aug 14, 2017 6:28 am

Hmmm. What happens if you run the standard StrandTest example? I'm wondering if your serial commands are getting scrambled.

adafruit_support_rick
 
Posts: 34871
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: RGBW Neopixels strange behavior on power on

by syr123 on Mon Aug 14, 2017 6:56 pm

Good thinking. I uploaded StrandTest and plugged / unplugged about 50 times. Each time it ran as expected - whole strip filled up with red first, etc.

I suppose that means it might have something to do with my serial messages, which I had figured, it's just I can't quite work out what might be going on with those or the best way to debug them.

It's unclear to me whether in this state the Neopixels are displaying exactly what they're told, and it's just that the messages are corrupted, or that they've somehow gotten into a bogus state from startup and are receiving the correct data and displaying it incorrectly.

In case it's helpful - I rewrote things a bit so the Micro would send a message to the Mega, which would then send it to my Mac every 1 second and print it. I managed to get the same bug to happen, where the lights are all white and going up and down two at a time. But the sent message seems to still arrive at my Mac no problem, and often everything works as expected if I hit the reset button on the Micro... (sometimes I can reset it, and I'll get the same all white, stair steppy look, then reset again and all goes well)

I've also tried getting it into this state, then restarting the Mega, or the Python app on my Mac. Restarting either the problem remains - the only thing that fixes it is restarting the Micro, or equivalently, unplugging and replugging the USB.

EDIT:

As you've seen from before, the commands can send R, G, B, W, tell the strip to latch, or set the height up the strip to light.
For debugging, I've made the Micro report back the value it's getting for each of these commands. When I get into this state I've described, I seem to be receiving the messages properly (and sending works fine).

I updated the Python program to send commands to set all the lights to yellow, and to vary the height up and down over time. Thus, I should be getting blue as 0, a varying height. When I have the Micro report the data its getting to the mega, forward to the mac, and print, I see sometimes in the broken state I see most often that channels that should be 0 are being reported as 127 (for the yellow example, Blue and White should be 0).

It seems like Serial communication is definitely off by a bit or broken somehow, but I can't see how or why..

FURTHER EDIT:

Is there is a flawed assumption in the serial protocol I designed? The way I tell that I'm at the beginning of a message is if the LSB of a command byte is a 1. Is it possible that when the Micro turns on it's not receiving full bytes or something, and interpreting a 1 in the wrong place as my begin command bit? Or does something else in the Serial config handle making sure I'm getting full bytes? I'm generally unclear if it's reliable that the bytes are synced up the way I have this wired.

syr123
 
Posts: 4
Joined: Fri Aug 11, 2017 4:41 am

Re: RGBW Neopixels strange behavior on power on

by adafruit_support_rick on Wed Aug 16, 2017 4:14 pm

Sorry - I've been out of the office.

Serial will give you properly-formed bytes. You don't have to worry about that.

So, you have a status bit on every byte? Is the bit only set to one on the first byte, or are the others also set to 1? Is there any chance that the status bit could be set to 1 on a byte other than byte 1?

Also. I don't know if it's the problem, but this is technically wrong:
Code: Select all | TOGGLE FULL SIZE
      // Make sure we exited the previous loop because the
      // status bit (rightmost) of the first byte was 1, and
      // not just because we ran out of bytes in the serial buffer.
      if ( byte1 & 1 ) {
        byte2 = Serial1.read();
        byte3 = Serial1.read();


You're assuming that bytes 2 and 3 will be available in the buffer, but it's possible that they might not be. You check for at least 3 bytes at the beginning of the function, but then you go into a loop looking for byte1. If you actually iterate that loop, then your 3 byte test will be invalid.

Your R, G, B W and value variables are all int. setPixelColor expects bytes. NeoPixel values can't exceed 255. I don't know why the compiler isn't complaining about passing ints into the function. The color value in your protocol is 10 bits. That's not right. This function will shift two two high-order bits off the end of the byte, and they will be lost anyway:
Code: Select all | TOGGLE FULL SIZE
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );


I can't figure out what this does. I don't know where 102 comes from. I don't know if this does what you want. but I suspect you're losing bits here, as well. This isn't how you do fixed-point arithmetic - you need to keep track of the binary point position and normalize the value after the multiplication.
Code: Select all | TOGGLE FULL SIZE
      else {
        if ( i == divr ){
          strip1.setPixelColor(i, strip1.Color(R*modr, G*modr, B*modr, W*modr));
          strip2.setPixelColor(i, strip2.Color(R*modr, G*modr, B*modr, W*modr));
        }


All that being said, you can't crash NeoPixels with bad color values. They will take any byte value you throw at them. And the library will only send byte values, regardless of what you pass to setPixelColor. So, if your code is crashing and displaying wacky colors, it's likely because of some problem with your comm protocol.

adafruit_support_rick
 
Posts: 34871
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: RGBW Neopixels strange behavior on power on

by syr123 on Thu Aug 17, 2017 2:00 am

Hey! No worries about being out of the office. I’m super appreciative of your time and help.

Good to know the bytes will be properly formed. That takes some of the mystery out of it.

Yes, I have a status bit on every byte. My commands are formed like this:

Code: Select all | TOGGLE FULL SIZE
#           Bit #
#         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
# Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
# Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
# Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS


Good thinking on the 1 status bit on a byte other than byte 1. A couple days ago I put a bunch of asserts on the Python side to check for this, and they’ve yet to fail, so I *think* we’re good on that front.

Really good thinking on the two byte read thing! I made an attempt at modifying the way I try to handle that, I’ve posted all the code changes for that below.

As for the R, G, B, W int, yes, how strange that the computer isn’t complaining. But so far, that seems to be working ok.

My protocol has a 10 bit “value” because I also use the same command protocol to send an ADC reading from the Micro to the Mega to the Mac and want to retain 10 bit precision. So I’m just using the same command design to send the 8 bit values too, and throwing away two bits.

As for the 102 etc, I was using floats. The basic idea with that part is modr ends up being a 0-1 scalar that I mult the ints by, then don’t worry about the remainder getting truncated. That whole bit just lets me use a 10bit ‘height’ value for the strip, and rather than quantizing the height directly to the number of pixels, this effectively fades in the next pixel. I think you’re right that this isn’t the problem - I think it’s in the com protocol.

SO - -

This problem is driving me absolutely nuts. Once things are hooked together and the lights are animating, the protocol works for hours no problem and never fails. It’s only on connect / disconnect of the USB that I get this strange state.

I have considered converting the Mega/Micro communication to I2C, but that still necessitates using serial for the Python/Mega communication. But, if that might help, I could try that.

Alternatively, I’d *love* any tips you have on a better/more reliable/more resilient through connect/disconnect protocol than the one I have now. Here are really the only considerations I have:

The Micro will send a 10bit ADC value to the Mega and then the Mac every 5000 microseconds or so as well as a digitalRead of three simple switches (only when the state changes)

The Mega sends lots of switches at that same rate to the Mac

The Mac will send lighting and height changes to the Mega and Micro at somewhere between 30-60 updates per second. Right now, that takes about 6 commands (18 bytes, commands below) per neopixel. I have 10 strips total (9 off the Mega, 1 off the Micro, so 10 x 6 * 3 = 180 bytes per full update, so at 60 updates a second thats 10800 bytes)

ConstructSetRCommand(0, r)
ConstructSetGCommand(0, g)
ConstructSetBCommand(0, b)
ConstructSetWCommand(0, w)
ConstructSetHeightCommand(0, h)
ConstructLatchCommand()

Do you have any ideas for a different more reliable protocol?

Below is the current state of the code for the Python, Mega, and Micro:

Python
Code: Select all | TOGGLE FULL SIZE
# Commands for talking to the lab and syringe
# Calling the construct commands adds the command to the serial write queue, which
# can be polled and written at a speed of your choice from the main app

import Queue
import time
import serial
from lib.utils import get_all_from_queue, get_item_from_queue, clamp

serial_write_queue = Queue.Queue()
serial_read_queue = Queue.Queue()

serial_port = None

def SetSerialPort(portReference):
    global serial_port
    serial_port = portReference

def GetWriteQueueItems():
    return list(get_all_from_queue(serial_write_queue))

def GetReadQueueItems():
    return list(get_all_from_queue(serial_read_queue))

def WriteSerialQueue():
    if serial_port == None:
        return

    qdata = GetWriteQueueItems()
    for command in qdata:
        try:
            # Can try writing command directly (byte array)
            serial_port.write( command )
        except serial.serialutil.SerialException:
            # Fail silently if port not available. Could close
            # port and stop serial here, but since we have nothing
            # to auto reopen port, we just fail
            return

# Module level state of the most recent values read in
# from serial. Each command read makes a copy of this
# and adds it to the serial queue to be read by check_server_data_queues in Engine
sensors = { "he1" : 0,
            "he2" : 0,
            "he3" : 0,
            "he4" : 0,
            "he5" : 0,
            "he6" : 0,
            "he7" : 0,
            "he8" : 0,
            "he9" : 0,
            "mbut1" : 0,
            "mbut2" : 0,
            "mbut3" : 0,
            "mbut4" : 0,
            "mbut5" : 0,
            "mbut6" : 0,
            "mbut7" : 0,
            "mbut8" : 0,
            "mbut9" : 0,
            "syrpot" : 0,
            "enc1but" : 0,
            "enc1" : 0
          }

channel_to_sensor = {
                    0  : "he9",
                    1  : "he8",
                    2  : "he5",   
                    3  : "he3",
                    4  : "he6",
                    5  : "he7",   
                    6  : "he1",
                    7  : "he2",
                    8  : "he4", 
                    9  : "mbut1",
                    10 : "mbut2",
                    11 : "mbut3",
                    12 : "mbut7",
                    13 : "mbut8",
                    14 : "mbut9",
                    15 : "enc1but",
                    16 : "enc1",
                    17 : "mbut6",
                    18 : "mbut5",
                    19 : "mbut4",
                    20 : "syrpot"
                    }


# 9 lights * 2 strips * 4 channels (RGBW)    =  72 channels on syringe
# 9 wells * 10 lights * 2 strips * 4 channels = 720 channels on lab
# 792 total channels, addressable with 10 bit channels

# I considered using the command topology:
# 3 bit commands = 8 commands
# 10 bit channels = 1024 channels
# 8 bit values = 255 possible values
# So that each channel of each light could be set.
#
# 792 channels would take 792 commands, plus two latch commands to set all the colors
# that's 794 * 3 bytes * 8 bits = 19056 bits/symbols
# I'm running at 57600 the fastest reliable rate. That means I could only send
# 3 full updates of all channels per second this way, which just isn't fast enough.
# For that reason, I'm changing in favor of a larger command space, so I can do things
# in software to handle colors, and play only with the level/height from the Engine side.
#
# If I wanted to go back to the idea of addressing every channel, I suppose this could
# be made more efficient by usign an indexed color palette
#
# Instead going with:
# 3 byte commands
#
#           Bit #
#         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
# Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
# Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
# Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

# 6 bit commands  - 64 commands

    #Commands 0 - 32 reserved for messages going from Engine to Mega/Micro
    #Commands 33 - 63 reserves for messages going from Mega/Micro to here

# 5 bit channels  - 32 channels
# 10 bit values   - 1024 resolution

#Channels:

#0 - Syringe
#1 - Well 1
#2 - Well 2
#3 - Well 3
#4 - Well 4
#5 - Well 5
#6 - Well 6
#7 - Well 7
#8 - Well 8
#9 - Well 9


#0 - Set R - Command #, Target a well or syringe channel, red PWM intensity
def ConstructSetRCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00000001 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )

    assert 1 & ( 0b00000001 | (channel & 0b10000) >> 3 )
    assert not 1 & ( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    assert not 1 & ( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#1 - Set G - Command #, Target a well or syringe channel, green PWM intensity
def ConstructSetGCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00000101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )
   
    assert 1 & ( 0b00000101 | (channel & 0b10000) >> 3 )
    assert not 1 & ( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    assert not 1 & ( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#2 - Set B - Command #, Target a well or syringe channel, blue PWM intensity
def ConstructSetBCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00001001 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )

    assert 1 & ( 0b00001001 | (channel & 0b10000) >> 3 )
    assert not 1 & ( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    assert not 1 & ( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#3 - Set W - Command #, Target a well or syringe channel, white PWM intensity
def ConstructSetWCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00001101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )

    assert 1 & ( 0b00001101 | (channel & 0b10000) >> 3 )
    assert not 1 & ( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    assert not 1 & ( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)

#4 - Latch Lab and Syringe - Command #, no op channel, no op value
def ConstructLatchCommand():
    serial_write_queue.put('\x11\x00\x00')

#5 - Set height command
def ConstructSetHeightCommand(channel, value):
    if channel > 31 or value > 1023:
        print "Channel or value send to Arduino out of bounds, things might get wacky."

    cmd = ""

    # Bitwise OR Cmd #1 with the status bit set to 1 with
    # the two MSB of channel shifted over
    cmd += chr( 0b00010101 | (channel & 0b10000) >> 3 )
    cmd += chr( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    cmd += chr( (value & 0b0001111111) << 1 )

    assert 1 & ( 0b00010101 | (channel & 0b10000) >> 3 )
    assert not 1 & ( ((channel & 0b01111)  << 4) | ((value & 0b1110000000) >> 6 ) )
    assert not 1 & ( (value & 0b0001111111) << 1 )
   
    serial_write_queue.put(cmd)





def serialDataAvailable():
    try:
        checkPort = serial_port.inWaiting()
    except IOError:
        return False

    return checkPort > 0

# Regarding this code being called by a timer, QTimer docs say:
# "If Qt is unable to deliver the requested number of timer clicks,
# it will silently discard some."
# Not entirely clear if this means because it can't schedule them because
# the main event loop or some other system process is going, or because
# it's trying to only run
def ReadSerialPort():
    if serial_port == None:
        return

    while serialDataAvailable():

        if serial_port.inWaiting() > 2:
            byte1 = ord(serial_port.read())
            byte2 = ord(serial_port.read())
            byte3 = ord(serial_port.read())

            if not (byte1 & 1) and (byte2 & 1) :
                if not (serial_port.inwaiting() > 0 ) :
                    return
           
                byte1 = byte2
                byte2 = byte3
                byte3 = ord(serial_port.read())

            elif not (byte1 & 1) and not (byte2 & 1) and (byte3 & 1):
                if not (serial_port.inwaiting() > 1 ) :
                    return

                byte1 = byte3
                byte2 = ord(serial_port.read())
                byte3 = ord(serial_port.read())

            if (not (byte1 & 1)) or (byte2 & 1) or (byte3 & 1) :
                return


            # At this point, we have a 3 byte command: start parsing
            command  = ( 0b11111100 & byte1 ) >> 2
            channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 )
            value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 )

            # Command 33, from Mega: Channel and sensor value
            if command == 33:

                #print channel, value

                if channel == 23:
                    print "red: " + str(value)
                    return
                if channel == 24:
                    print "gre: " + str(value)
                    return
                if channel == 25:
                    print "blu: " + str(value)
                    return
                if channel == 26:
                    print "whi: " + str(value)
                    return
                if channel == 27:
                    print "lat: " + str(value)
                    return
                if channel == 28:
                    print "hgt: " + str(value)
                    return

                # Normalize analog syringe pot
                if channel == 20:
                    value /= 1023.
                    value = 1.0 - value

                value = min(1.0, value)
                value = max(0.0, value)


                try:
                    sensor_key = channel_to_sensor[channel]
                    global sensors
                    sensors[sensor_key] = value
                    entry = [time.clock(), sensors.copy()]
                    serial_read_queue.put( entry )

                    # XXX DELETE ME #XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
                    f = open("/Users/mtf/Desktop/sensors", "w")
                    f.write( str(pprint.pformat(entry)) )
                    f.close()

                except KeyError:
                    print "Bogus channel"
                    print channel

            # Command 34, from Mega, Syringe connection state (0/1), channel 0
            if command == 34:
                print "Syringe connection message"
                print channel, value
                print












### XXX XXX XXX DELETE ME

def enumerate_serial_ports():
    from serial.tools import list_ports
    return [x[0] for x in list_ports.comports()]

ports = enumerate_serial_ports()
portname = None
for x in ports:
    if x.find("usbmodem1411") != -1:
        portname = x


ssss = serial.Serial(    port = portname,
                         baudrate = 57600,
                         bytesize = serial.EIGHTBITS,
                         parity = serial.PARITY_NONE,
                         stopbits = serial.STOPBITS_ONE,
                         # timeout 0 means non-blocking mode (return immediately on read)
                         # If we don't get data, the timer function immediately returns to be
                         # called again. Since we're polling at an interval, a timeout isn't important.
                         # If we were in an infinite loop where the blocking timeout helped us wait
                         # the interval before trying to read again, we might use a timeout.
                         # Note that timeout = None will block / wait forever: the function would not
                         # return until data came in
                         timeout = 0.0)
SetSerialPort(ssss)

import time
import math
import LabColor
import os
import pprint

smess = []

# Arduino resets after connected, so we
# give it time to to just chill -- THIS WOULD BE MORE RELIABLE
# IF WE ACTUALLY WAITED ON A MESSAGE TO CONFIRM CONNECTION AND READINESS
time.sleep(1.5)

# Flush any data out of the Arduino
serial_port.flush()
         

def setSyringeToColorAndHeight(r, g, b, w, h):
    ConstructSetRCommand(0, r)
    ConstructSetGCommand(0, g)
    ConstructSetBCommand(0, b)
    ConstructSetWCommand(0, w)
    WriteSerialQueue()

    ConstructSetHeightCommand(0, h)
    WriteSerialQueue()
   
    ConstructLatchCommand()
    WriteSerialQueue()

c1 = LabColor.Color(1.0, 0.0, 0.0)
c2 = LabColor.Color(0, 0.0, 1.0)

def WriteToMega():
    n = LabColor.hsl_color_lerp(c1, c2, (math.sin(5*time.time())+1)*.5)
    # Set the syringe light to yellow and half height
    height = int(((math.sin(time.time()*2.0)+1)*.5) * 1000.0)
   
    #setSyringeToColorAndHeight(int(n.r*255.0), int(n.g*255.0), int(n.b*255.0), 0, height)
    setSyringeToColorAndHeight( 255, 255, 0, 0, height)
    #setSyringeToColorAndHeight( 255, 0, 0, 0, 512)
    #setSyringeToColorAndHeight( 255, 255, 255, 255, 1023)

def ReadFromMega():
    ReadSerialPort()

while True:

    # Sleep a bit so we don't flood the buffers with commands
    # Timer takes care of this in the real app
    time.sleep(.01)

    WriteToMega()
    ReadFromMega()
   
    # READ
    #if serial_port != None:
    #    while serialDataAvailable():
    #        if serial_port.inWaiting() > 0:
    #            smess.append( serial_port.read() )

    #    if len(smess) > 0:
    #        print "".join(smess)
    #        smess = []

#### XXX XXX XXX DELETE ME



Mega
Code: Select all | TOGGLE FULL SIZE
// Notes
//
// analogReference(EXTERNAL); can be used when an external ground
// is applied to the Arduino's AREF pin
//
// Serial.write can be used to directly write bytes, which
// may be better than using ASCII values with print
//
// baud is 'symbols per second'
// There can be a distinction between BIT rate and BAUD rate, but for
// asynchronous communication they are effectively the same thing.
//
// If we want to, say, send 2 byte readings of 10 bit values with sensor channel
// every 5000 microseconds (5 ms)
// That's 200 sends per second * 2 bytes * 8 bits = 3200 baud,
// so we'd select 4800 baud for the Arduino, smallest available rate >= 3200 baud

// Some inspiration from:
// http://balau82.wordpress.com/2011/03/26/capturing-an-analog-signal-with-arduino-and-python/

// ********************************************************
// LIGHTING
// ********************************************************

// MegaBrite LEDs

// ints are 16 bit (2-byte) on ATMega
// unsigned int LEDChannels[NumLEDs][3] = {0};

// Maps sensible well light indices to the way they are actually wired
// in the daisy chain:
// Sensible
// 0  1  2
// 3  4  5
// 6  7  8 
//
// As wired
// 6  7  8
// 5  4  3
// 0  1  2 
//
// Since we have to push through a chain,
// the last light must come first, so
// think of the indices like this and remap
// to 'sensible':
// 2  1  0
// 3  4  5
// 8  7  6 

byte LEDRemap[9] = { 2, 1, 0, 3, 4, 5, 8, 7, 6 };
byte SyringeCommand[3];

int byte1, byte2, byte3, forward;
int command, channel, value;

bool syringe_connected = 0;

// ********************************************************
// END LIGHTING
// ********************************************************

// ********************************************************
// Wells and Buttons
// ********************************************************

#define SENSOR_CHANGE_THRESHOLD 0

// 9 wells, 6 arcade buttons
#define NUM_CHANNELS 15

#define WELL1_PIN 22
#define WELL1_CHANNEL 0
#define WELL2_PIN 23
#define WELL2_CHANNEL 1
#define WELL3_PIN 24
#define WELL3_CHANNEL 2
#define WELL4_PIN 25
#define WELL4_CHANNEL 3
#define WELL5_PIN 26
#define WELL5_CHANNEL 4
#define WELL6_PIN 27
#define WELL6_CHANNEL 5
#define WELL7_PIN 28
#define WELL7_CHANNEL 6
#define WELL8_PIN 29
#define WELL8_CHANNEL 7
#define WELL9_PIN 30
#define WELL9_CHANNEL 8

#define ARCADE1_PIN 31
#define ARCADE1_CHANNEL 9
#define ARCADE2_PIN 32
#define ARCADE2_CHANNEL 10
#define ARCADE3_PIN 33
#define ARCADE3_CHANNEL 11
#define ARCADE4_PIN 34
#define ARCADE4_CHANNEL 12
#define ARCADE5_PIN 35
#define ARCADE5_CHANNEL 13
#define ARCADE6_PIN 37
#define ARCADE6_CHANNEL 14

// ********************************************************
// TEMPORARY OLD SYRINGE
// ********************************************************

//#define SYRINGE_BUTTON1_PIN 8
//#define SYRINGE_BUTTON1_CHANNEL 17
//#define SYRINGE_BUTTON2_PIN 9
//#define SYRINGE_BUTTON2_CHANNEL 18
//#define SYRINGE_BUTTON3_PIN 10
//#define SYRINGE_BUTTON3_CHANNEL 19
//#define SYRINGE_POT_PIN 0
//#define SYRINGE_POT_CHANNEL 20

// ********************************************************
// END TEMPORARY OLD SYRINGE
// ********************************************************

// How often to read and send sensor values in
// microseconds
#define SENSOR_SEND_MICROS 5000

unsigned long previousMicros = 0;
unsigned long elapsedTime = 0;

// Hold previous sensor values such that we only
// send on change
int sensorValues[NUM_CHANNELS];
int sensorValue = 0;

// ********************************************************
// End Wells and Buttons
// ********************************************************

byte SensorCommand[3];

#define BAUD_RATE 57600
#define SYRINGE_SERIAL_BAUD_RATE 57600

void setup() {

  // ********************************************************
  // LIGHTING
  // ********************************************************
   
  // TODO REPLACE WITH NEOPIXEL SETUP
   
  // ********************************************************
  // END LIGHTING
  // ********************************************************
   
  // ********************************************************
  // SENSORS
  // ********************************************************
   
  // init sensor values to bogus value 
  for(int i=0; i < NUM_CHANNELS; i++){
    sensorValues[i] = -1;
  }

  // Serial1 Tx / Rx pullup to pull lines up to 5V,
  // the idle state
  pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);
 
  // Well Reed Switches
  pinMode(WELL1_PIN, INPUT_PULLUP);
  pinMode(WELL2_PIN, INPUT_PULLUP);
  pinMode(WELL3_PIN, INPUT_PULLUP);
  pinMode(WELL4_PIN, INPUT_PULLUP);
  pinMode(WELL5_PIN, INPUT_PULLUP);
  pinMode(WELL6_PIN, INPUT_PULLUP);
  pinMode(WELL7_PIN, INPUT_PULLUP);
  pinMode(WELL8_PIN, INPUT_PULLUP);
  pinMode(WELL9_PIN, INPUT_PULLUP);

  // Arcade Buttons
  pinMode(ARCADE1_PIN, INPUT_PULLUP);
  pinMode(ARCADE2_PIN, INPUT_PULLUP);
  pinMode(ARCADE3_PIN, INPUT_PULLUP);
  pinMode(ARCADE4_PIN, INPUT_PULLUP);
  pinMode(ARCADE5_PIN, INPUT_PULLUP);
  pinMode(ARCADE6_PIN, INPUT_PULLUP);

  // ********************************************************
  // TEMPORARY OLD SYRINGE
  // ********************************************************

  //pinMode(SYRINGE_BUTTON1_PIN, INPUT_PULLUP);
  //pinMode(SYRINGE_BUTTON2_PIN, INPUT_PULLUP);
  //pinMode(SYRINGE_BUTTON3_PIN, INPUT_PULLUP);

  // ********************************************************
  // END TEMPORARY OLD SYRINGE
  // ********************************************************

  // ********************************************************
  // END SENSORS
  // ********************************************************

  // ********************************************************
  // SERIAL
  // ********************************************************

  // Serial connection to the Mac
  Serial.begin(BAUD_RATE);
  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  while(!Serial);

  // Serial conneciton to the Syringe / Arduino Micro
  Serial1.begin(SYRINGE_SERIAL_BAUD_RATE);
  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  while(!Serial1);
 
  //Serial.println("Started...");
 
  // delay(100);
  // ********************************************************
  // END SERIAL
  // ********************************************************
}

void loop() {

    // Read all sensors and send out values
    readSensors();
   
    // Check for commands from Mac
    checkSerialPortForCommand();
   
    // Any commands coming in for the syringe are
    // intended to broadcast sensor values, and can
    // be forwarded to the Mac
    forwardSerialBytesFromSyringe();

}

void readSensors() {
 
  // Elapsed time calculation done with difference and unsigned long to avoid
  // issues when micros overflows back to 0. See this tip:
  // https://arduino.stackexchange.com/questions/22994/resetting-millis-and-micros
 
  elapsedTime = micros() - previousMicros;

  if( elapsedTime >= SENSOR_SEND_MICROS) {

    // Arcade Buttons
    digitalSensorRead(ARCADE1_PIN, ARCADE1_CHANNEL, 1);
    digitalSensorRead(ARCADE2_PIN, ARCADE2_CHANNEL, 1);
    digitalSensorRead(ARCADE3_PIN, ARCADE3_CHANNEL, 1);
    digitalSensorRead(ARCADE4_PIN, ARCADE4_CHANNEL, 1);
    digitalSensorRead(ARCADE5_PIN, ARCADE5_CHANNEL, 1);
    digitalSensorRead(ARCADE6_PIN, ARCADE6_CHANNEL, 1);
   
    // Well Reed Switches
    digitalSensorRead(WELL1_PIN, WELL1_CHANNEL, 1);
    digitalSensorRead(WELL2_PIN, WELL2_CHANNEL, 1);
    digitalSensorRead(WELL3_PIN, WELL3_CHANNEL, 1);
    digitalSensorRead(WELL4_PIN, WELL4_CHANNEL, 1);
    digitalSensorRead(WELL5_PIN, WELL5_CHANNEL, 1);
    digitalSensorRead(WELL6_PIN, WELL6_CHANNEL, 1);
    digitalSensorRead(WELL7_PIN, WELL7_CHANNEL, 1);
    digitalSensorRead(WELL8_PIN, WELL8_CHANNEL, 1);
    digitalSensorRead(WELL9_PIN, WELL9_CHANNEL, 1);
   
    // ********************************************************
    // TEMPORARY OLD SYRINGE
    // ********************************************************
    //digitalSensorRead(SYRINGE_BUTTON1_PIN, SYRINGE_BUTTON1_CHANNEL, 1);
    //digitalSensorRead(SYRINGE_BUTTON2_PIN, SYRINGE_BUTTON2_CHANNEL, 1);
    //digitalSensorRead(SYRINGE_BUTTON3_PIN, SYRINGE_BUTTON3_CHANNEL, 1);
   
    //analogSensorRead(SYRINGE_POT_PIN, SYRINGE_POT_CHANNEL);

    // ********************************************************
    // END TEMPORARY OLD SYRINGE
    // ********************************************************

    previousMicros = micros();
   
  }
}

// Construct CMD 33, to send a channel and value to the Mac
void sendChannelAndValue(int channel, int value) {
   
    SensorCommand[0] = 0b10000101 | (channel & 0b10000) >> 3;
    SensorCommand[1] = ((channel & 0b001111) << 4) | ((value & 0b1110000000) >> 6 );
    SensorCommand[2] = (value & 0b0001111111) << 1;
    Serial.write(SensorCommand, 3);

}

// Construct CMD 34, which lets the Engine know the syringe is CONNECTED or DISCONNECTED
void sendSyringeConnectedMessageToEngine(int value) {
 
    SensorCommand[0] = 0b10001001;
    SensorCommand[1] = (value & 0b1110000000) >> 6 ;
    SensorCommand[2] = (value & 0b0001111111) << 1;
    Serial.write(SensorCommand, 3);
   
}

void digitalSensorRead(int pin, int channel, byte invert) {
    sensorValue = digitalRead(pin);
   
    // The second condition, checking that the old value is >= 0, makes sure we don't
    // send the initial state when all of sensorValues = -1 at init
    if( abs(sensorValue - sensorValues[channel]) > SENSOR_CHANGE_THRESHOLD && sensorValues[channel] >= 0 ){
      if(invert == 1){
        sendChannelAndValue(channel, 1-sensorValue);
      }
      else {
        sendChannelAndValue(channel, sensorValue);
      }
    }
    sensorValues[channel] = sensorValue;
}

void analogSensorRead(int pin, int channel) {
    sensorValue = analogRead(pin);
   
    //if( abs(sensorValue - sensorValues[channel]) > 2 && sensorValues[channel] >= 0 ){
    sendChannelAndValue(channel, sensorValue);
    //}
    sensorValues[channel] = sensorValue;
}

void forwardSerialBytesFromSyringe() {
  //if ( syringe_connected ) {
    while ( Serial1.available() > 0 ) {
   
      forward = Serial1.read();
      Serial.write(forward);
     
    }
  //}
 
}

// Commands go a little something like this:
//           Bit #
//         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
// Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
// Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
// Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

void checkSerialPortForCommand() {
   
  while ( Serial.available() > 0 ) {

    // Serial receive buffer is 64 bytes. A command is 3 bytes.
    // Start trying to parse command if more than 2 bytes are
    // available.
    if ( Serial.available() > 2 ) {
      byte1 = Serial.read();
      byte2 = Serial.read();
      byte3 = Serial.read();

      // It's possible at this point that byte1 wasn't the beginning
      // of a command.
     
      // In this case:
      // Byte 1 doesn't have the LSB status bit set to 1
      // Byte 2 *does*
      if ( !(byte1 & 1)  && (byte2 & 1) ) {
        // We can correct this case by reading one more byte,
        // but if it's not ready we return
        if( ! (Serial.available() > 0 ) ){
          return;
        }
        // Throw out byte1, now byte1 properly has the status bit
        byte1 = byte2;
        // Move byte3 into byte 2
        byte2 = byte3;
        byte3 = Serial.read();
      }

      // Byte 1 doesn't have the status bit
      // Byte 2 doesn't have the status bit
      // Byte 3 *does* have the status bit
      else if ( !(byte1 & 1) && !(byte2 & 1) && (byte3 & 1) ) {
        // We can correct this case by reading two more bytes,
        // but if they aren't ready we return
        if( ! (Serial.available() > 1 ) ){
          return;
        }
        byte1 = byte3;
        byte2 = Serial.read();
        byte3 = Serial.read();
       
      }

      // This should never happen, but it's a safeguard
      if( !(byte1 & 1) || (byte2 & 1) || (byte3 & 1) ) {
        return;
      }
       
        // At this point, we have a 3 byte command: start parsing
        command  = ( 0b11111100 & byte1 ) >> 2;
        channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 );
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );

        // Set R
        if ( command == 0 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 0 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Set G
        else if ( command == 1 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 1 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }       

        // Set B
        else if ( command == 2 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 2 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Set W
        else if ( command == 3 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 3 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }

        // Latch
        else if ( command == 4 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 4 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }
       
        // Set Height
        else if ( command == 5 ) {
          // Channel 0 is syringe. Forward the command
          if( channel == 0 ){
              SyringeCommand[0] = byte1;
              SyringeCommand[1] = byte2;
              SyringeCommand[2] = byte3;
              //Serial.println("Forwarding command 5 to syringe");
              Serial1.write(SyringeCommand, 3);
          }
         
        }
       
      }
   
  }
}


Micro
Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Firmware for the Arduino Micro in syringe with ATmega 32U4

// If IDE upload with autoreset isn't working, try:
// https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
// These differences affect the way you use the physical reset button to perform
// an upload if the auto-reset isn't working. Press and hold the reset button
// on the Leonardo or Micro, then hit the upload button in the Arduino software.
// Only release the reset button after you see the message "Uploading..." appear
// in the software's status bar. When you do so, the bootloader will start,
// creating a new virtual (CDC) serial port on the computer. The software
// will see that port appear and perform the upload using it.

#define NUMBER_OF_SYRINGE_LEDS 9

#define LINEAR_POT A3 // Analog A3

#define SYRINGE_BUTTON_1 4 // Digital 4
#define SYRINGE_BUTTON_2 7
#define SYRINGE_BUTTON_3 12

#define SYRINGE_SERIAL_BAUD_RATE 57600
//#define SENSOR_SEND_MICROS 5000
#define SENSOR_SEND_MICROS 1000000

#define NEOPIXEL_PIN_1 9
#define NEOPIXEL_PIN_2 10
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(NUMBER_OF_SYRINGE_LEDS,
                                            NEOPIXEL_PIN_1,
                                            NEO_GRBW + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(NUMBER_OF_SYRINGE_LEDS,
                                            NEOPIXEL_PIN_2,
                                            NEO_GRBW + NEO_KHZ800);

// Internal values set by forwarded commands
int R = 0;
int G = 0;
int B = 0;
int W = 0;
int height = 0;

float modr = 0;
int divr = 0;

int SYRINGE_VALUE = 0;
int SYRINGE_BUTTON_1_VALUE = 0;
int SYRINGE_BUTTON_2_VALUE = 0;
int SYRINGE_BUTTON_3_VALUE = 0;

byte SensorCommand[3];

byte clr;
int byte1, byte2, byte3;
int command, channel, value;
int sensorValue;

unsigned long previousMicros = 0;
unsigned long elapsedTime = 0;

void setup() {     

  // Serial1 Tx / Rx pullup to pull lines up to 5V,
  // the idle state
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
 
  // Syringe Buttons
  pinMode(SYRINGE_BUTTON_1, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_2, INPUT_PULLUP);
  pinMode(SYRINGE_BUTTON_3, INPUT_PULLUP);

  // Trying this blocking delay to wait for serial
  // to be ready, rather than the old delay(100), based on:
  // https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro
  // NOTE - think perhaps this only works for soft serial (Serial, not Serial1)
  //while(!Serial1);

  // BEGIN LIGHTING
  strip1.begin();
  strip2.begin();
  // Initialize all pixels to 'off'
  strip1.show();
  strip2.show();

  // On the Micro, Serial1 is the hardware serial port, Serial is the virtual
  // serial driver
  Serial1.begin(SYRINGE_SERIAL_BAUD_RATE);

  //while ( Serial1.available() > 0 ) {
  //  Serial1.read();
  //}

  // DEBUG ONLY
  pinMode(LED_BUILTIN, OUTPUT);
 
}

void loop() {

  readSensors();
  checkSerialPortForCommand();
  debugLED();
 
}

void debugLED() {
  long b = micros() % 1000000;
  if ( b > 500000 ) {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  else {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void readSensors() {

  // Elapsed time calculation done with difference and unsigned long to avoid
  // issues when micros overflows back to 0. See this tip:
  // https://arduino.stackexchange.com/questions/22994/resetting-millis-and-micros
 
  elapsedTime = micros() - previousMicros;
 
  if( elapsedTime >= SENSOR_SEND_MICROS) {
   
    // See OXullo Intersecans' answer on Quora
    // http://www.quora.com/Why-is-a-little-delay-needed-after-analogRead-in-Arduino
    // For explanation of why we're calling analogRead twice and ignoring the
    // first result. Effectively, the ADC is connceted to the analog pins via a MUX
    // with with a cap. If the cap isn't given time to discharge, it can influence
    // the ADC readings

    // SLIDE LINEAR POT
    //analogRead(LINEAR_POT);
    //sensorValue = analogRead(LINEAR_POT);
    //SYRINGE_VALUE = sensorValue;
    //sendChannelAndValue(22, sensorValue);

    // SYRINGE BUTTONS
    sensorValue = digitalRead( SYRINGE_BUTTON_1 );
    SYRINGE_BUTTON_1_VALUE = sensorValue;
    sendChannelAndValue(19, !sensorValue);
   
    //sensorValue = digitalRead( SYRINGE_BUTTON_2 );
    //SYRINGE_BUTTON_2_VALUE = sensorValue;
    //sendChannelAndValue(20, !sensorValue);
   
    //sensorValue = digitalRead( SYRINGE_BUTTON_3 );
    //SYRINGE_BUTTON_3_VALUE = sensorValue;
    //sendChannelAndValue(21, !sensorValue);

    previousMicros = micros();

  }

}

void latchLight() {

  // Height is 10 bit int (0 - 1023)
  // This modr / divr business is used to fade the intensity
  // of the leading edge light to reduce stepping. It's effectively
  // a 1 neopixel wide filter
 
  modr = (height % 102) / 102.0f;
  divr = height / 102;

  // Assumes strip1 and 2 have the same number of pixels
  for(uint16_t i=0; i<strip1.numPixels(); i++) {

      if( i < divr ) {
        strip1.setPixelColor(i, strip1.Color(R,G,B,W));
        strip2.setPixelColor(i, strip2.Color(R,G,B,W));
      }
      else {
        if ( i == divr ){
          strip1.setPixelColor(i, strip1.Color(R*modr, G*modr, B*modr, W*modr));
          strip2.setPixelColor(i, strip2.Color(R*modr, G*modr, B*modr, W*modr));
        }
        else {
          strip1.setPixelColor(i, strip1.Color(0,0,0,0));   
          strip2.setPixelColor(i, strip2.Color(0,0,0,0)); 
        }
      }
   
  }
 
  strip1.show();
  strip2.show();

}

// Construct CMD 4, to send a channel and value to the lab, then the Mac
void sendChannelAndValue(int channel, int value) {
        elapsedTime = micros() - previousMicros;
 
  if( elapsedTime >= SENSOR_SEND_MICROS) {
    SensorCommand[0] = 0b10000101 | (channel & 0b10000) >> 3;
    SensorCommand[1] = ((channel & 0b001111) << 4) | ((value & 0b1110000000) >> 6 );
    SensorCommand[2] = (value & 0b0001111111) << 1;
    Serial1.write(SensorCommand, 3);

    previousMicros = micros();

  }
}

// Commands go a little something like this:
//           Bit #
//         |  0   |   1  |   2   |   3   |   4   |   5   |   6   |   7   |
// Byte 1  | CMD1   CMD2   CMD3    CMD4    CMD5    CMD6    CHAN1   STATUS
// Byte 2  | CHAN2  CHAN3  CHAN4   CHAN5   VAL1    VAL2    VAL3    STATUS
// Byte 3  | VAL4   VAL5   VAL6    VAL7    VAL8    VAL9    VAL10   STATUS

void checkSerialPortForCommand() {
   
  while ( Serial1.available() > 0 ) {

    // Serial receive buffer is 64 bytes. A command is 3 bytes.
    // Start trying to parse command if more than 2 bytes are
    // available.
    if ( Serial1.available() > 2 ) {
     
      byte1 = Serial1.read();
      byte2 = Serial1.read();
      byte3 = Serial1.read();

      // It's possible at this point that byte1 wasn't the beginning
      // of a command.
     
      // In this case:
      // Byte 1 doesn't have the LSB status bit set to 1
      // Byte 2 *does*
      if ( !(byte1 & 1)  && (byte2 & 1) ) {
        // We can correct this case by reading one more byte,
        // but if it's not ready we return
        if( ! (Serial1.available() > 0 ) ){
          return;
        }
        // Throw out byte1, now byte1 properly has the status bit
        byte1 = byte2;
        // Move byte3 into byte 2
        byte2 = byte3;
        byte3 = Serial1.read();
      }

      // Byte 1 doesn't have the status bit
      // Byte 2 doesn't have the status bit
      // Byte 3 *does* have the status bit
      else if ( !(byte1 & 1) && !(byte2 & 1) && (byte3 & 1) ) {
        // We can correct this case by reading two more bytes,
        // but if they aren't ready we return
        if( ! (Serial1.available() > 1 ) ){
          return;
        }
        byte1 = byte3;
        byte2 = Serial1.read();
        byte3 = Serial1.read();
       
      }

      // This should never happen, but it's a safeguard
      if( !(byte1 & 1) || (byte2 & 1) || (byte3 & 1) ) {
        return;
      }

        // At this point, we have a 3 byte command: start parsing
        command  = ( 0b11111100 & byte1 ) >> 2;
        channel  = ( ( 0b00000010 & byte1 ) << 3 ) | ( (byte2 & 0b11110000) >> 4 );
        value    = ( ( 0b00001110 & byte2 ) << 6)  | ( (byte3 & 0b11111110) >> 1 );

        // Set R channel
        if ( command == 0 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              R = value;
              //sendChannelAndValue(23, value);
          }
        }

        // Set G channel
        else if ( command == 1 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              G = value;
              //sendChannelAndValue(24, value);
          }
        }       

        // Set B channel
        else if ( command == 2 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              B = value;
              //sendChannelAndValue(25, value);
          } 
        }

        // Set W channel
        else if ( command == 3 ) {
          // Channel should always be 0 if this was forwarded here, but check anyway
          if( channel == 0) {
              W = value;
              //sendChannelAndValue(26, value);
          }
        }

        // Latch
        else if ( command == 4 ) {
            latchLight();
            //sendChannelAndValue(27, value);
        }
       
        // Set height
        else if ( command == 5 ) {
            height = value;
            //sendChannelAndValue(28, value);
        }
       
     
    }
  }
}

syr123
 
Posts: 4
Joined: Fri Aug 11, 2017 4:41 am

Re: RGBW Neopixels strange behavior on power on

by adafruit_support_rick on Sat Aug 19, 2017 12:39 pm

syr123 wrote:The Mac will send lighting and height changes to the Mega and Micro at somewhere between 30-60 updates per second. Right now, that takes about 6 commands (18 bytes, commands below) per neopixel. I have 10 strips total (9 off the Mega, 1 off the Micro, so 10 x 6 * 3 = 180 bytes per full update, so at 60 updates a second thats 10800 bytes)

You can't do that at 57600 bit/second. It's too slow. You have to use at least 115200.

The other thing is that the NeoPixel library disables interrupts when it's sending data to the strips. So, even at 115200, you could be dropping bytes.

Try upping the baud rate and dropping the data rate and see if the problem goes away, or at least gets better.

adafruit_support_rick
 
Posts: 34871
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Please be positive and constructive with your questions and comments.