Skip to content

Commit

Permalink
Adds NeoPixel driver + usage example
Browse files Browse the repository at this point in the history
  • Loading branch information
m1cr0lab committed Sep 11, 2021
1 parent 1eb2057 commit 11e5873
Show file tree
Hide file tree
Showing 7 changed files with 480 additions and 2 deletions.
9 changes: 8 additions & 1 deletion lib/ESPboy/ESPboy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ void ESPboy::_initMCP23017() {
// TFT chip select
_mcp.pinMode(_MCP23017_TFT_CS_PIN, OUTPUT);
_mcp.digitalWrite(_MCP23017_TFT_CS_PIN, LOW);

// NeoPixel LED
pixel.begin(_mcp);

}

Expand All @@ -38,12 +41,14 @@ void ESPboy::_initTFT() {
tft.init();

#if ESPBOY_FAST_SPI

// ST7735 40MHz overclocking tradeoff:
// vscroll init setting needs to be replayed
tft.startWrite();
tft.writeCommand(0x37); // vscroll command
tft.writeData16(1); // vscroll start address
tft.endWrite();

#endif

}
Expand Down Expand Up @@ -166,7 +171,7 @@ void ESPboy::splash() {

uint8_t ESPboy::readButtons() {

return ~_mcp.readGPIOAB() & 255;
return ~(_mcp.readGPIOAB() & 0xff);

}

Expand All @@ -180,6 +185,8 @@ void ESPboy::update() {

if (_fader != _Fader::NONE) _fade();

pixel.update();

_updateFPS();

}
Expand Down
4 changes: 3 additions & 1 deletion lib/ESPboy/ESPboy.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <Arduino.h>
#include <Adafruit_MCP4725.h>
#include <Adafruit_MCP23017.h>
#include "NeoPixel.h"
#include "tft-config.h"
#include "espboy-logo.h"

Expand Down Expand Up @@ -46,7 +47,8 @@ class ESPboy {

public:

LGFX tft;
LGFX tft;
NeoPixel pixel;

void begin(uint8_t brightness = 0xff);
void splash();
Expand Down
263 changes: 263 additions & 0 deletions lib/ESPboy/NeoPixel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
/**
* ----------------------------------------------------------------------------
* ESPboy library
* ----------------------------------------------------------------------------
* Copyright (c) 2021 Stéphane Calderoni (https://github.com/m1cr0lab)
* ----------------------------------------------------------------------------
* NeoPixel driver
* ----------------------------------------------------------------------------
*/
#include "NeoPixel.h"

void NeoPixel::begin(Adafruit_MCP23017 &mcp) {

pinMode(_LED_PIN, OUTPUT);

_mcp = &mcp;
_mcp->pinMode(_MCP23017_LOCK_PIN, OUTPUT);

_fx = _FX::NONE;

setBrightness(0x40);
clear();

}

void NeoPixel::update() {

switch (_fx) {

case _FX::FLASH: _flash(); break;
case _FX::BREATHE: _breathe(); break;
case _FX::RAINBOW: _rainbow(); break;
default:;

}

}

void NeoPixel::setBrightness(uint8_t b) { _brightness = b + 1; }

uint32_t NeoPixel::rgb(uint8_t red, uint8_t green, uint8_t blue) {

return (green << 16) | (red << 8) | blue;

}

/**
* @param hue [0-359]
* @param sat [0-255]
* @param val [0-255]
*/
uint32_t NeoPixel::hsv(uint16_t hue, uint8_t sat, uint8_t val) {

if (!sat) return rgb(val, val, val);

hue = (hue << 5) / 45;

uint8_t r, g, b;

uint8_t sextant = hue / 43;
uint8_t remainder = (hue - (sextant * 43)) * 6;

uint8_t p = (val * ~sat) >> 8;
uint8_t q = (val * ~(sat * remainder) >> 8) >> 8;
uint8_t t = (val * ~(sat * ~remainder) >> 8) >> 8;

switch (sextant) {

case 0: r = val; g = t; b = p; break;
case 1: r = q; g = val; b = p; break;
case 2: r = p; g = val; b = t; break;
case 3: r = p; g = q; b = val; break;
case 4: r = t; g = p; b = val; break;
default: r = val; g = p; b = q;

}

return rgb(r, g, b);

}

void NeoPixel::clear() { show(0); }
void NeoPixel::reset() { _fx = _FX::NONE; clear(); }

uint8_t NeoPixel::_sine(uint8_t i) {

switch (i >> 6) {

case 0: return pgm_read_byte(_FAST_SINE + i); // [ 0 - 63]
case 1: return ~pgm_read_byte(_FAST_SINE + (~i & 0x3f)); // [ 64 - 127]
case 2: return ~pgm_read_byte(_FAST_SINE + ( i & 0x3f)); // [128 - 191]
default: return pgm_read_byte(_FAST_SINE + (~i & 0x3f)); // [192 - 255]

}

}

void NeoPixel::_flash() {

if (!_fx_looping && !_fx_count) { _fx = _FX::NONE; return; }

if (millis() - _fx_start_ms < _fx_duration_ms) return;

if (_flashing) { clear(); _flashing = false; _fx_count--; return; }

if (millis() - _fx_start_ms < _fx_period_ms) return;

_fx_start_ms += _fx_period_ms;
_flashing = true;
show(_fx_color);

}

void NeoPixel::flash(uint32_t color, uint16_t duration_ms, uint8_t count, uint16_t period_ms) {

_fx = _FX::FLASH;
_fx_color = color;
_fx_start_ms = millis();
_fx_duration_ms = duration_ms;
_fx_period_ms = period_ms;
_fx_count = count;
_fx_looping = count == 0;
_flashing = true;

show(color);

}

void NeoPixel::_breathe() {

if (!_fx_looping && !_fx_count) { reset(); return; }

if (millis() - _fx_start_ms < _fx_duration_ms) return;

if (++_fx_offset == 256) { _fx_offset = 0; _fx_count--; }

_fx_start_ms += _fx_duration_ms;

uint32_t color = _fx_color;
uint8_t lumin = _sine(_fx_offset) + 1;

if (lumin) {
uint8_t c, *p = (uint8_t*)&color;
for (uint8_t i=0; i<3; ++i) {
c = *p;
*p++ = (c * lumin) >> 8;
}
}

show(color);

}

void NeoPixel::breathe(uint32_t color, uint16_t wait_ms, uint8_t count) {

_fx = _FX::BREATHE;
_fx_offset = 0;
_fx_color = color;
_fx_start_ms = millis();
_fx_duration_ms = wait_ms;
_fx_count = count;
_fx_looping = count == 0;

}

void NeoPixel::_rainbow() {

if (!_fx_looping && !_fx_count) { reset(); return; }

if (millis() - _fx_start_ms < _fx_duration_ms) return;

if (++_fx_hue == 360) { _fx_hue = 0; _fx_count--; }

_fx_start_ms += _fx_duration_ms;

show(hsv(_fx_hue));

}

void NeoPixel::rainbow(uint32_t wait_ms, uint8_t count) {

_fx = _FX::RAINBOW;
_fx_hue = 0;
_fx_start_ms = millis();
_fx_duration_ms = wait_ms;
_fx_count = count;
_fx_looping = count == 0;

show(hsv(_fx_hue));

}

/**
* Inspired by these references:
* @see https://github.com/adafruit/Adafruit_NeoPixel/blob/master/esp8266.c
* @see https://github.com/ESPboy-edu/ESPboy_Classes/blob/main/ESPboy_LED/ESPboyLED.cpp
*/
void IRAM_ATTR NeoPixel::show(uint32_t color) {

static const uint32_t pin_mask = 1 << _LED_PIN;
static uint32_t t, c, start, mask;

if (_brightness) {

uint8_t c, *p = (uint8_t*)&color;
for (uint8_t i=0; i<3; ++i) {
c = *p;
*p++ = (c * _brightness) >> 8;
}

}

mask = 0x800000;
start = 0;

GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pin_mask); // light on the onboard LED
_mcp->digitalWrite(_MCP23017_LOCK_PIN, HIGH); // and open the transistor lock

os_intr_lock();

for (uint8_t i = 0; i < 24; ++i) {

t = color & mask ? CYCLES_800_T1H : CYCLES_800_T0H;

while ((c = ESP.getCycleCount()) - start < CYCLES_800); // wait for bit start
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pin_mask); // then set LED pin to HIGH

start = c;

while (ESP.getCycleCount() - start < t); // wait for HIGH duration
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pin_mask); // then set LED pin to LOW

mask >>= 1;

}

os_intr_unlock();

_mcp->digitalWrite(_MCP23017_LOCK_PIN, LOW); // close the transistor lock
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pin_mask); // light off the onboard LED

}

/**
* ----------------------------------------------------------------------------
* First experiments with ESPboy2
* ----------------------------------------------------------------------------
* Copyright (c) 2021 Stéphane Calderoni (https://github.com/m1cr0lab)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* ----------------------------------------------------------------------------
*/
Loading

0 comments on commit 11e5873

Please sign in to comment.