//#include <Arduino.h>
#include <Wire.h>
#include <WiFi.h>
#include "time.h"
#include <NeoPixelBus.h> // Swapped to Makuna's NeoPixelBus library
// Network Credentials
//const char* ssid = "Wokwi-GUEST"";
//const char* password = "";
// NTP Server Settings
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
// Hardware Architecture Settings
#define INT_PIN 32
#define PCA9685_MODE1 0x00
#define LED0_ON_L 0x06
#define FADE_STEP 25
#define TICK_DELAY_MS 10
// NeoPixelBus Configuration
const uint16_t PixelCount = 64; // 4 chips * 16 pins = 64 LEDs
// Using NeoEsp32I2s1X800KbpsMethod uses the I2S1 peripheral.
// Default hardware output pin for I2s1 is GPIO 2. You can change this if needed.
const uint8_t PixelPin =13;
NeoPixelBus<NeoGrbFeature, NeoEsp32I2s1X8800KbpsMethod> strip(PixelCount, PixelPin);
// Colors setup using NeoPixelBus color structures
RgbColor colorGreen(0, 150, 0);
RgbColor colorRed(150, 0, 0);
// Multi-Device I2C Address Framework
const uint8_t PCF_ADDRS[] = {0x20, 0x21, 0x22, 0x23};
const uint8_t PCA_ADDRS[] = {0x40, 0x41, 0x42, 0x43};
// Data package scaled for matrix routing
struct LedMessage {
uint8_t chipIdx;
uint8_t channel;
uint16_t pwmValue;
uint8_t inputChip;
uint8_t inputPin;
bool pinState;
};
SemaphoreHandle_t pcfSemaphore = NULL;
QueueHandle_t ledQueue = NULL;
uint16_t currentPWM[4][16] = {0};
uint16_t targetPWM[4][16] = {0};
void IRAM_ATTR onPcfInterrupt() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(pcfSemaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
}
uint16_t pcfReadFrom(uint8_t address) {
Wire.requestFrom(address, (uint8_t)2);
if (Wire.available() == 2) {
return (Wire.read() | (Wire.read() << 8));
}
return 0xFFFF;
}
void pcfWriteTo(uint8_t address, uint16_t data) {
Wire.beginTransmission(address);
Wire.write(data & 0xFF);
Wire.write((data >> 8) & 0xFF);
Wire.endTransmission();
}
void pca9685SetupChip(uint8_t address) {
Wire.beginTransmission(address);
Wire.write(PCA9685_MODE1);
Wire.write(0x20);
Wire.endTransmission();
}
void setLedPwmRaw(uint8_t chipAddress, uint8_t channel, uint16_t pwmValue) {
if (pwmValue > 4095) pwmValue = 4095;
uint8_t regAddress = LED0_ON_L + (channel * 4);
Wire.beginTransmission(chipAddress);
Wire.write(regAddress);
Wire.write(0x00); Wire.write(0x00);
Wire.write(pwmValue & 0xFF);
Wire.write((pwmValue >> 8) & 0xFF);
Wire.endTransmission();
}
void vInputReaderTask(void *pvParameters) {
uint16_t lastPinStates[] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
for (;;) {
if (xSemaphoreTake(pcfSemaphore, portMAX_DELAY) == pdTRUE) {
for (uint8_t chipIdx = 0; chipIdx < 4; chipIdx++) {
uint16_t currentPinStates = pcfReadFrom(PCF_ADDRS[chipIdx]);
if (currentPinStates != lastPinStates[chipIdx]) {
for (uint8_t pinIdx = 0; pinIdx < 16; pinIdx++) {
bool currBit = (currentPinStates & (1 << pinIdx)) != 0;
bool lastBit = (lastPinStates[chipIdx] & (1 << pinIdx)) != 0;
if (currBit != lastBit) {
LedMessage msg;
msg.inputChip = chipIdx;
msg.inputPin = pinIdx;
msg.pinState = currBit;
msg.chipIdx = chipIdx;
msg.channel = pinIdx;
msg.pwmValue = (!currBit) ? 3500 : 0;
xQueueSend(ledQueue, &msg, 0);
}
}
lastPinStates[chipIdx] = currentPinStates;
}
}
}
}
}
void vLedOutputTask(void *pvParameters) {
LedMessage receivedMsg;
TickType_t xLastWakeTime = xTaskGetTickCount();
for (;;) {
bool ws2812NeedsUpdate = false;
if (xQueueReceive(ledQueue, &receivedMsg, pdMS_TO_TICKS(TICK_DELAY_MS)) == pdTRUE) {
if (receivedMsg.chipIdx < 4 && receivedMsg.channel < 16) {
targetPWM[receivedMsg.chipIdx][receivedMsg.channel] = receivedMsg.pwmValue;
}
// Linear pixel calculation
uint16_t pixelIndex = (receivedMsg.inputChip * 16) + receivedMsg.inputPin;
if (pixelIndex < PixelCount) {
if (receivedMsg.pinState == false) {
// Status "0" -> RED
strip.SetPixelColor(pixelIndex, colorRed);
} else {
// Status "1" -> GREEN
strip.SetPixelColor(pixelIndex, colorGreen);
}
ws2812NeedsUpdate = true;
}
}
// Process PCA9685 Fading
for (uint8_t c = 0; c < 4; c++) {
for (uint8_t ch = 0; ch < 16; ch++) {
if (currentPWM[c][ch] != targetPWM[c][ch]) {
if (currentPWM[c][ch] < targetPWM[c][ch]) {
currentPWM[c][ch] = (targetPWM[c][ch] - currentPWM[c][ch] < FADE_STEP) ? targetPWM[c][ch] : currentPWM[c][ch] + FADE_STEP;
} else {
currentPWM[c][ch] = (currentPWM[c][ch] - targetPWM[c][ch] < FADE_STEP) ? targetPWM[c][ch] : currentPWM[c][ch] - FADE_STEP;
}
setLedPwmRaw(PCA_ADDRS[c], ch, currentPWM[c][ch]);
}
}
}
// Hardware DMA/I2S Push (Asynchronous - non-blocking!)
if (ws2812NeedsUpdate) {
strip.Show();
}
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(TICK_DELAY_MS));
}
}
void vWifiNtpDisplayTask(void *pvParameters) {
Serial.print("[WiFi Task] Connecting to "); // Serial.println(ssid);
//WiFi.begin(ssid, password);
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
vTaskDelay(pdMS_TO_TICKS(500));
Serial.print(".");
}
Serial.println("\n[WiFi Task] Connected! Syncing NTP Time...");
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
struct tm timeinfo;
while(!getLocalTime(&timeinfo)){
vTaskDelay(pdMS_TO_TICKS(2000));
}
Serial.println("[WiFi Task] Internal Clock Synchronized.");
for (;;) {
if (getLocalTime(&timeinfo)) {
Serial.printf("[SYSTEM TIME] %04d-%02d-%02d %02d:%02d:%02d\n",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.setClock(400000);
// Initialize the NeoPixelBus Strip
strip.Begin();
for(uint16_t i = 0; i < PixelCount; i++) {
strip.SetPixelColor(i, colorGreen); // Baseline default HIGH status
}
strip.Show();
for (uint8_t i = 0; i < 4; i++) {
pcfWriteTo(PCF_ADDRS[i], 0xFFFF);
pca9685SetupChip(PCA_ADDRS[i]);
for (uint8_t ch = 0; ch < 16; ch++) {
setLedPwmRaw(PCA_ADDRS[i], ch, 0);
}
}
pcfSemaphore = xSemaphoreCreateBinary();
ledQueue = xQueueCreate(50, sizeof(LedMessage));
if (pcfSemaphore != NULL && ledQueue != NULL) {
xTaskCreatePinnedToCore(vInputReaderTask, "ReaderTask", 4000, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(vLedOutputTask, "OutputTask", 4000, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(vWifiNtpDisplayTask, "WifiNtpTask", 4000, NULL, 1, NULL, 0);
pinMode(INT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INT_PIN), onPcfInterrupt, FALLING);
xSemaphoreGive(pcfSemaphore);
}
Serial.println("64-Ch Hardware Architecture (NeoPixelBus Engine) Online.");
}
void loop() {
vTaskDelay(pdMS_TO_TICKS(1000));
}70 addr 000
0x20 addr 000
LCD 0x27
0x21 001
0x22 010
0x42 00010
0x43 00011
pwm1 0x41 - 00001
0x23 011
0x22 010
0x23 011
0x42 00010
0x43 00011
pwm1 0x41 - 00001
0x21 001
0x26 ISR vect
0x24 100