mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-26 17:46:14 +01:00
Compare commits
No commits in common. "dd16e0da972a414cac66755b61b6922691f88030" and "d3d6cc97fb68ee2314a3d4a3e10f92542044c124" have entirely different histories.
dd16e0da97
...
d3d6cc97fb
|
@ -405,18 +405,16 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||
return;
|
||||
}
|
||||
if (params==2) { // <o [-]vpin count>
|
||||
for (auto pix=vpin;pix<vpin+p[1];pix++) IODevice::write(pix,setON);
|
||||
for (auto pix=vpin;pix<=vpin+p[1];pix++) IODevice::write(pix,setON);
|
||||
return;
|
||||
}
|
||||
if (params==4 || params==5) { // <z [-]vpin r g b [count]>
|
||||
auto count=p[4]?p[4]:1;
|
||||
if (p[1]<0 || p[1]>0xFF) break;
|
||||
if (p[2]<0 || p[2]>0xFF) break;
|
||||
if (p[3]<0 || p[3]>0xFF) break;
|
||||
// strange parameter mangling... see IO_NeoPixel.h NeoPixel::_writeAnalogue
|
||||
int colour_RG=(p[1]<<8) | p[2];
|
||||
uint16_t colour_B=p[3];
|
||||
for (auto pix=vpin;pix<vpin+count;pix++) IODevice::writeAnalogue(pix,colour_RG,setON,colour_B);
|
||||
if (params==4 || params==5) { // <z [-]vpin r g b [count]>
|
||||
uint16_t colourcode=((p[1] & 0x1F)<<11) |
|
||||
((p[2] & 0x1F)<<6) |
|
||||
((p[3] & 0x1F)<<1);
|
||||
if (setON) colourcode |= 0x0001;
|
||||
// driver treats count 0 as 1
|
||||
for (auto pix=vpin;pix<=vpin+p[4];pix++) IODevice::writeAnalogue(pix,colourcode,0,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
20
EXRAIL2.cpp
20
EXRAIL2.cpp
|
@ -998,14 +998,8 @@ void RMFT2::loop2() {
|
|||
}
|
||||
break;
|
||||
|
||||
case OPCODE_NEOPIXEL:
|
||||
// OPCODE_NEOPIXEL,V([-]vpin),OPCODE_PAD,V(colour_RG),OPCODE_PAD,V(colour_B),OPCODE_PAD,V(count)
|
||||
{
|
||||
VPIN vpin=operand>0?operand:-operand;
|
||||
auto count=getOperand(3);
|
||||
for (auto pix=vpin;pix<vpin+count;pix++)
|
||||
IODevice::writeAnalogue(pix,getOperand(1),operand>0,getOperand(2));
|
||||
}
|
||||
case OPCODE_NEOPIXEL: // OPCODE_NEOPIXEL,V(vpin),OPCODE_PAD,V(rgbcolour)
|
||||
IODevice::writeAnalogue(operand,getOperand(1));
|
||||
break;
|
||||
|
||||
#ifndef IO_NO_HAL
|
||||
|
@ -1205,11 +1199,11 @@ int16_t RMFT2::getSignalSlot(int16_t id) {
|
|||
}
|
||||
|
||||
if (sigtype== NEOPIXEL_SIGNAL_FLAG) {
|
||||
// redpin,amberpin,greenpin are the 3 RG values but with no blue permitted. . (code limitation hack)
|
||||
int colour_RG=redpin;
|
||||
if (rag==SIGNAL_AMBER) colour_RG=amberpin;
|
||||
if (rag==SIGNAL_GREEN) colour_RG=greenpin;
|
||||
IODevice::writeAnalogue(sigid, colour_RG,true,0);
|
||||
// redpin,amberpin,greenpin are the 3 rgbs
|
||||
VPIN colour=redpin;
|
||||
if (rag==SIGNAL_AMBER) colour=amberpin;
|
||||
if (rag==SIGNAL_GREEN) colour=greenpin;
|
||||
IODevice::writeAnalogue(sigid, colour);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,8 @@
|
|||
#define LCN(msg)
|
||||
#define MESSAGE(msg)
|
||||
#define MOVETT(id,steps,activity)
|
||||
#define NEOPIXEL(id,r,g,b,count...)
|
||||
#define NEOPIXEL(id,colour)
|
||||
#define NEOPIXEL_OFF(id,colour)
|
||||
#define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour)
|
||||
#define ACON(eventid)
|
||||
#define ACOF(eventid)
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
//const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0;
|
||||
//const byte TRACK_POWER_1=1, TRACK_POWER_ON=1;
|
||||
|
||||
// NEOPIXEL RG generator for NEOPIXEL_SIGNAL
|
||||
#define NeoRG(red,green) ((red & 0xff)<<8) | (green & 0xff)
|
||||
// NEOPIXEL RGB generator
|
||||
#define NeoRGB(red,green,blue) (((red & 0x1F)<<11) | ((green & 0x1F)<<6) | ((blue & 0x1F)<<1) )
|
||||
|
||||
// Pass 1 Implements aliases
|
||||
#include "EXRAIL2MacroReset.h"
|
||||
|
@ -435,7 +435,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) {
|
|||
#undef DCCX_SIGNAL
|
||||
#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) id | RMFT2::DCCX_SIGNAL_FLAG,redAspect,amberAspect,greenAspect,
|
||||
#undef NEOPIXEL_SIGNAL
|
||||
#define NEOPIXEL_SIGNAL(id,redcolour,ambercolour,greencolour) id | RMFT2::NEOPIXEL_SIGNAL_FLAG,redcolour, ambercolour, greencolour,
|
||||
#define NEOPIXEL_SIGNAL(id,redcolour,ambercolour,greencolour) id | RMFT2::NEOPIXEL_SIGNAL_FLAG,redcolour | NEOPIXEL_FLAG_ON, ambercolour | NEOPIXEL_FLAG_ON, greencolour | NEOPIXEL_FLAG_ON,
|
||||
#undef VIRTUAL_SIGNAL
|
||||
#define VIRTUAL_SIGNAL(id) id,0,0,0,
|
||||
|
||||
|
@ -558,11 +558,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||
#define LCN(msg) PRINT(msg)
|
||||
#define MESSAGE(msg) PRINT(msg)
|
||||
#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0),
|
||||
#define NEOPIXEL(id,r,g,b,count...) OPCODE_NEOPIXEL,V(id),\
|
||||
OPCODE_PAD,V(((r & 0xff)<<8) | (g & 0xff)),\
|
||||
OPCODE_PAD,V((b & 0xff)),\
|
||||
OPCODE_PAD,V(#count[0]?(count+0):1),
|
||||
|
||||
#define NEOPIXEL(id,colour) OPCODE_NEOPIXEL,V(id),OPCODE_PAD,V(colour| NEOPIXEL_FLAG_ON),
|
||||
#define NEOPIXEL_OFF(id,colour) OPCODE_NEOPIXEL,V(id),OPCODE_PAD,V(colour& ^NEOPIXEL_FLAG_ON),
|
||||
#define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour)
|
||||
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
|
||||
#define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3),
|
||||
|
|
106
IO_NeoPixel.h
106
IO_NeoPixel.h
|
@ -133,6 +133,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static const uint16_t NEOPIXEL_ON_FLAG=0x0001;
|
||||
|
||||
static const byte SEESAW_NEOPIXEL_BASE=0x0E;
|
||||
static const byte SEESAW_NEOPIXEL_STATUS = 0x00;
|
||||
|
@ -151,35 +152,20 @@ private:
|
|||
_firstVpin = firstVpin;
|
||||
_nPins=nPins;
|
||||
_I2CAddress = i2cAddress;
|
||||
|
||||
// calculate the offsets into the seesaw buffer for each colour depending
|
||||
// on the pixel strip type passed in mode.
|
||||
|
||||
_brightness=2; // TODO 0,1,2,3
|
||||
_redOffset=4+(mode >> 4 & 0x03);
|
||||
_greenOffset=4+(mode >> 2 & 0x03);
|
||||
_blueOffset=4+(mode & 0x03);
|
||||
if (4+(mode >>6 & 0x03) == _redOffset) _bytesPerPixel=3;
|
||||
else _bytesPerPixel=4; // string has a white byte.
|
||||
|
||||
_kHz800=(mode & NEO_KHZ400)==0;
|
||||
_showPendimg=false;
|
||||
|
||||
// Each pixel requires 3 bytes RGB memory.
|
||||
// Although the driver device can remember this, it cant do off/on without
|
||||
// forgetting what the on colour was!
|
||||
pixelBuffer=(RGB *) malloc(_nPins*sizeof(RGB));
|
||||
stateBuffer=(byte *) calloc((_nPins+7)/8,sizeof(byte)); // all pixels off
|
||||
if (pixelBuffer==nullptr || stateBuffer==nullptr) {
|
||||
DIAG(F("NeoPixel I2C:%s not enough RAM"), _I2CAddress.toString());
|
||||
return;
|
||||
}
|
||||
// preset all pins to white so a digital on/off will do something even if no colour set.
|
||||
memset(pixelBuffer,0xFF,_nPins*sizeof(RGB));
|
||||
// In dccex there are only 2 bytes per pixel
|
||||
pixelBuffer=(uint16_t *) calloc(_nPins,sizeof(uint16_t)); // all pixels off
|
||||
addDevice(this);
|
||||
}
|
||||
|
||||
void _begin() {
|
||||
|
||||
// Initialise Neopixel device
|
||||
I2CManager.begin();
|
||||
if (!I2CManager.exists(_I2CAddress)) {
|
||||
|
@ -212,41 +198,45 @@ private:
|
|||
_showPendimg=false;
|
||||
}
|
||||
|
||||
|
||||
// read back pixel colour (rarely needed I suspect)
|
||||
int _readAnalogue(VPIN vpin) override {
|
||||
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||
auto pin=vpin-_firstVpin;
|
||||
return pixelBuffer[pin];
|
||||
}
|
||||
|
||||
// read back pixel on/off
|
||||
int _read(VPIN vpin) override {
|
||||
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||
return isPixelOn(vpin-_firstVpin);
|
||||
auto pin=vpin-_firstVpin;
|
||||
return pixelBuffer[pin] & NEOPIXEL_ON_FLAG;
|
||||
}
|
||||
|
||||
// Write digital value. Sets pixel on or off
|
||||
void _write(VPIN vpin, int value) override {
|
||||
if (_deviceState == DEVSTATE_FAILED) return;
|
||||
auto pixel=vpin-_firstVpin;
|
||||
auto pin=vpin-_firstVpin;
|
||||
if (value) {
|
||||
if (isPixelOn(pixel)) return;
|
||||
setPixelOn(pixel);
|
||||
if (pixelBuffer[pin] & NEOPIXEL_ON_FLAG) return;
|
||||
pixelBuffer[pin] |= NEOPIXEL_ON_FLAG;
|
||||
}
|
||||
else { // set off
|
||||
if (!isPixelOn(pixel)) return;
|
||||
setPixelOff(pixel);
|
||||
if (!(pixelBuffer[pin] & NEOPIXEL_ON_FLAG)) return;
|
||||
pixelBuffer[pin] &= (~NEOPIXEL_ON_FLAG);
|
||||
}
|
||||
transmit(pixel);
|
||||
transmit(pin);
|
||||
}
|
||||
|
||||
// Write analogue value.
|
||||
// The convoluted parameter mashing here is to allow passing the RGB and on/off
|
||||
// information through the generic HAL _writeAnalog interface which was originally
|
||||
// designed for servos and short integers
|
||||
void _writeAnalogue(VPIN vpin, int colour_RG, uint8_t onoff, uint16_t colour_B) override {
|
||||
// Write analogue (integer) value
|
||||
void _writeAnalogue(VPIN vpin, int colour, uint8_t ignore1, uint16_t ignore2) override {
|
||||
(void) ignore1;
|
||||
(void) ignore2;
|
||||
if (_deviceState == DEVSTATE_FAILED) return;
|
||||
RGB newColour={(byte)((colour_RG>>8) & 0xFF), (byte)(colour_RG & 0xFF), (byte)(colour_B & 0xFF)};
|
||||
auto pixel=vpin-_firstVpin;
|
||||
if (pixelBuffer[pixel]==newColour && isPixelOn(pixel)==(bool)onoff) return; // no change
|
||||
|
||||
if (onoff) setPixelOn(pixel); else setPixelOff(pixel);
|
||||
pixelBuffer[pixel]=newColour;
|
||||
transmit(pixel);
|
||||
auto newColour=(uint16_t)colour;
|
||||
auto pin=vpin-_firstVpin;
|
||||
if (pixelBuffer[pin]==newColour) return;
|
||||
pixelBuffer[pin]=newColour;
|
||||
transmit(pin);
|
||||
}
|
||||
|
||||
// Display device information and status.
|
||||
|
@ -257,12 +247,6 @@ private:
|
|||
_deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F(""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool isPixelOn(int16_t pixel) {return stateBuffer[pixel/8] & (0x80>>(pixel%8));}
|
||||
void setPixelOn(int16_t pixel) {stateBuffer[pixel/8] |= (0x80>>(pixel%8));}
|
||||
void setPixelOff(int16_t pixel) {stateBuffer[pixel/8] &= ~(0x80>>(pixel%8));}
|
||||
|
||||
// Helper function for error handling
|
||||
void reportError(uint8_t status, bool fail=true) {
|
||||
DIAG(F("NeoPixel I2C:%s Error:%d (%S)"), _I2CAddress.toString(),
|
||||
|
@ -272,42 +256,30 @@ private:
|
|||
}
|
||||
|
||||
|
||||
void transmit(uint16_t pixel, bool show=true) {
|
||||
void transmit(uint16_t pin, bool show=true) {
|
||||
byte buffer[]={SEESAW_NEOPIXEL_BASE,SEESAW_NEOPIXEL_BUF,0x00,0x00,0x00,0x00,0x00};
|
||||
uint16_t offset= pixel * _bytesPerPixel;
|
||||
uint16_t offset= pin * _bytesPerPixel;
|
||||
buffer[2]=(byte)(offset>>8);
|
||||
buffer[3]=(byte)(offset & 0xFF);
|
||||
|
||||
if (isPixelOn(pixel)) {
|
||||
auto colour=pixelBuffer[pixel];
|
||||
buffer[_redOffset]=colour.red;
|
||||
buffer[_greenOffset]=colour.green;
|
||||
buffer[_blueOffset]=colour.blue;
|
||||
} // else leave buffer black (in buffer preset to zeros above)
|
||||
auto colour=pixelBuffer[pin];
|
||||
if (colour & NEOPIXEL_ON_FLAG) {
|
||||
buffer[_redOffset]=(colour>>11 & 0x1F) <<_brightness;
|
||||
buffer[_greenOffset]=(colour>>6 & 0x1F) <<_brightness;
|
||||
buffer[_blueOffset]=(colour>>1 & 0x1F) <<_brightness;
|
||||
} // else leave buffer black
|
||||
|
||||
// Transmit pixel to driver
|
||||
I2CManager.write(_I2CAddress,buffer,4 +_bytesPerPixel);
|
||||
_showPendimg=true;
|
||||
|
||||
}
|
||||
struct RGB {
|
||||
byte red;
|
||||
byte green;
|
||||
byte blue;
|
||||
bool operator==(const RGB& other) const {
|
||||
return red == other.red && green == other.green && blue == other.blue;
|
||||
}
|
||||
};
|
||||
|
||||
RGB* pixelBuffer = nullptr;
|
||||
byte* stateBuffer = nullptr; // 1 bit per pixel
|
||||
bool _showPendimg;
|
||||
|
||||
// mapping of RGB onto pixel buffer for seesaw.
|
||||
uint16_t* pixelBuffer = nullptr;
|
||||
byte _brightness;
|
||||
byte _bytesPerPixel;
|
||||
byte _redOffset;
|
||||
byte _greenOffset;
|
||||
byte _blueOffset;
|
||||
bool _showPendimg;
|
||||
bool _kHz800;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
NeoPixel support
|
||||
|
||||
The IO_NeoPixel.h driver supports the adafruit neopixel seesaw board. It turns each pixel into an individual VPIN which can be given a colour and turned on or off using the new <o> command or the NEOPIXEL Exrail macro. Exrail SIGNALS can also drive a single pixel signal or multiple separate pixels.
|
||||
|
||||
|
||||
1. Defining the hardware driver:
|
||||
Add a driver definition in myAutomation.h for each adafruit I2C driver.
|
||||
|
||||
HAL(neoPixel, firstVpin, numberOfPixels [, mode [, i2caddress])
|
||||
Where mode is selected from the various pixel string types which have varying
|
||||
colour order or refresh frequency. For MOST strings this mode will be NEO_GRB but for others refer to the comments in IO_NeoPixel.h
|
||||
If omitted the node and i2caddress default to NEO_GRB, 0x60.
|
||||
|
||||
HAL(NeoPixel,1000,20)
|
||||
This is a NeoPixel driver defaulting to I2C aqddress 0x60 for a GRB pixel string. Pixels are given vpin numbers from 1000 to 1019.
|
||||
HAL(NeoPixel,1020,20,NEO_GRB,0x61)
|
||||
This is a NeoPixel driver on i2c address 0x61
|
||||
|
||||
2. Setting pixels from the < > commands.
|
||||
By default, each pixel in the string is created as white but switched off.
|
||||
Each pixel has a vpin starting from the first vpin in the HAL definitions.
|
||||
|
||||
<o vpin> switches pixel on (same as <z vpin>) e.g. <o 1005>
|
||||
<o -vpin> switches pixel off (same as <z -vpin>) e.g. <o -1003>
|
||||
(the z commands work on pixels the same as other gpio pins.)
|
||||
|
||||
<o [-]vpin count> switches on/off count pixels starting at vpin. e.g <o 1000 5>
|
||||
Note: it IS acceptable to switch across 2 strings of pixels if they are contiguous vpin ranges. It is also interesting that this command doesnt care if the vpins are NeoPixel or any other type, so it can be used to switch a range of other pin types.
|
||||
|
||||
<o [-]vpin red green blue [count]> sets the colour and on/off status of a pin or pins. Each colour is 0..255 e.g. <o 1005 255 255 0> sets pin 1005 to bright yellow and ON, <0 -1006 0 0 255 10> sets pins 1006 to 1015 (10 pins) to bright blue but OFF.
|
||||
Note: If you set a pin to a colour, you can turn it on and off without having to reset the colour every time. This is something the adafruit seesaw library can't do and is just one of several reasons why we dont use it.
|
||||
|
||||
3. Setting pixels from EXRAIL
|
||||
The new NEOPIXEL macro provides the same functionality as the <o [-]vpin red green blue [count]> command above.
|
||||
NEOPIXEL([-]vpin, red, green, blue [,count])
|
||||
|
||||
Setting pixels on or off (without colour change) can be done with SET/RESET [currently there is no set range facility but that may be added as a general exrail thing... watch this space]
|
||||
|
||||
Because the pixels obey set/reset, the BLINK command can also be used to control blinking a pixel.
|
||||
|
||||
4. EXRAIL pixel signals.
|
||||
There are two types possible, a mast with separate fixed colour pixels for each aspect, or a mast with one multiple colour pixel for all aspects.
|
||||
|
||||
For separate pixels, the colours should be established at startup and a normal SIGNALH macro used.
|
||||
|
||||
AUTOSTART
|
||||
SIGNALH(1010,1011,1012)
|
||||
NEOPIXEL(1010,255,0,0)
|
||||
NEOPIXEL(1011,128,128,0)
|
||||
NEOPIXEL(1012,0,255,0)
|
||||
RED(1010) // force signal state otherwise all 3 lights will be on
|
||||
DONE
|
||||
|
||||
For signals with 1 pixel, the NEOPIXEL_SIGNAL macro will create a signal
|
||||
NEOPIXEL_SIGNAL(vpin,redfx,amberfx,greenfx)
|
||||
|
||||
*** This is experimental and may change****
|
||||
In order to fit the existing signal code, the fx colours above are restricted to the red and green pixel values (ie no blue channel)
|
||||
The fx values above can be created by the NeoRG macro so a bright red would be NeoRG(255,0) bright green Ng(0,255) and amber something like NeoRG(128,128)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user