-YAYA- 发表于 2023-8-22 11:40:21

如何使用电容式触摸定制LED Twitch标牌

本帖最后由 -YAYA- 于 2023-8-22 11:53 编辑

如何使用电容式触摸定制LED Twitch标牌
【硬件准备】


[*]Arduino Mega 2560
[*]Infineon PSoC 4100S Capsense先锋套件
[*]LED灯带
[*]电阻器10k欧姆×3
[*]逻辑电平N沟道MOSFET×3
[*]Breadboard 面包板
[*]12V电源
[*]9V电源
[*]跳线(通用)

除此之外,我还使用了一台3d打印机,如果没有这个条件的话,也有别的可行方案。

【项目理念】

这是一个可点亮的多色标牌,用户在Twitch上直播时会激活,它可以通过命令由Twitch聊天控制,也可以通过使用PSoC 4系列先锋套件(我指的是我用作PSoC 4100s的设备)通过电容触摸亲自控制。

我喜欢闪闪发亮的东西,所以我愿意花大量的时间和精力来制作这样一个又大又亮的灯光的项目。再加上有游戏、3d打印和各种电子产品的加成,我对这个项目十分感兴趣。它有趣还实用,可以提前让亲朋好友知道有一个直播正在进行,以避免尴尬的时刻。我非常喜欢想一些能与设备互动的方法,在Twitch上也会有有趣的互动功能,所以做这个项目的想法就自然而然地展开了。现在是时候开始进入项目了!

【项目步骤】

电容式触控(PSoC设置)


我从启动和运行PSoC方面开始,并使用PSoC Creator 4.4软件启动和运行。该设备配有一个非常漂亮的颜色选择器和两个按钮,非常适合这次的产品。它还附带了一个示例Color Gamut项目,这很有用。不过,我并没有仅仅利用基本功能,而是改变了按钮的工作方式,让自己有更多的控制权。

颜色托盘是很好的选择,但要获得特定的颜色可能比较难。特别是因为我是个大块头,我的手就可以占据托盘的一半。因此,我将其中一个按钮设置为通过选择颜色旋转。这是通过保持一个颜色索引来实现的,这样的话当我按下按钮时,它会增加索引,直到它把每种颜色都过了一遍,然后会返回到1。

同样,我想控制灯的亮度与LED的开关。因此,我使用另一个按钮来保持亮度指数,该指数为0-5。我们可以通过将索引值乘以51得到0-255的值,如果是0的话会关闭符号。

对于项目的其他方面,我使用了Arduino设置来直接控制LED并监听Twitch消息,因此集成PSoC设备的理想设置是通过串行消息将设备的输入转发给Arduino。我在引脚选项卡中添加了UART的TX引脚,并添加了通过UART发送串行消息的代码。这是为了发送色域中的红色、绿色和蓝色(RGB)着色值、我们设置的颜色指数和亮度指数。由于我们已经设置了串行消息,我们将确切地知道在Arduino一侧要查找什么。TX引脚用于传输信息,RX引脚接收信息,因此设置是使用跳线将PSoC 4100s的TX引脚连接到Arduino设备的RX引脚。


LED设置(点亮它!)

我引用了“makeuseof”的一篇关于设置LED灯带的文章。这是一个理想的设置,因为它可以放置不是专门为Arduino制作的LED条,这正是我碰巧可以使用的。简而言之,它使用逻辑电平MOSFET和10k电阻器组合一个简单的测试板设置,可以安全地将LED带与Arduino连接起来。这也需要一个单独的12v电源。

我等到项目已经基本上全部完成后才进行最后的润色。使用一个定制的发光标牌比使用普通的LED灯带要有趣得多,所以我决定把这两者结合起来。我使用的标牌是我设计过的3D打印标志。幸运的是,我之前已经打印出来了,这非常方便,因为我的打印机由于一个悲惨的软塞事故而完全无法使用。虽然3D打印标志很酷,但如果你在跟进的过程中发现这不是你的菜,也有很多其他选项可以使用类似泡沫板之类的材料来实现相同的基本设置。

添加LED灯带只是将其插入不直接可见的标志中。其实我的LED条有点太旧了,我无法使用它粘性的一面,必须用胶带把它粘在适当的位置。但这也有好处,因为我可以把胶带放在视线之外。


所有灯光都通过Arduino控制。基本上,你通过为Arduino提供红色、蓝色和绿色的值来点亮标志。例如,如果红色值为255,蓝色值为0,绿色值为0,那么红色会以全亮度显示。但你也可以通过这些数值控制亮度。以相同的例子,如果红色值为50,蓝色值为0,绿色值为0,那么红色会变暗。在代码中,我们保留这些值,以便可以确定正在使用的颜色,这样如果我们增加亮度,就能保持当前显示的颜色。

Arduino设置


我们已经有了灯光,现在是时候设置Arduino了。此时,我们可以使用Arduino监听来自PSoC设备的串行消息,并相应地更新标牌。我们处理传入的消息,并使用RGB值来更新颜色。我们可以使用亮度索引乘以51来创建一个0-255的值,并使用我在前一部分中描述的逻辑来更新LED的亮度。最后,我们很快将开始添加Twitch命令,但颜色索引与我已关联到Twitch聊天命令的颜色选项相匹配,这样在索引循环时,颜色会从一个变为另一个。

通常情况下,我喜欢用便宜的零件看看我能完成多少工作,但对于这个特定的项目,我打算长期使用并不断添加和调整。因此,我在组件上稍微花了一些钱。我使用了Arduino Mega,它具有额外的串行端口和更大的程序容量。虽然事实证明这并不是必要的,但正如我们将要介绍的,我认为程序会更庞大。我有时也遇到Wi-Fi不稳定的问题,所以我使用了一个以太网扩展板来确保稳定和可靠的连接。当然,我也承认我只是想找个借口尝试一下以太网扩展板。

当然,你的以太网电缆实际上必须正常工作才行。

有趣的是,你的以太网电缆必须真正为你的以太网连接工作才能合作。一旦不起作用的电缆被扔进垃圾桶,并且使用了一根好的电缆,以太网的设置就非常简单了(正如你在所附代码中看到的那样)!这意味着是时候让Twitch也能控制LED了。

以下是该项目的完整示意图:


Twitch设置

第一个关键方面是能够使用Arduino收听Twitch聊天消息。为此,你需要Twitch聊天机器人设置来提供访问权限。你可以转到以下URL,然后单击“注册你的应用程序”进行设置:https://dev.twitch.tv/console.


你还需要一个有效的oauth令牌,并且可以使用以下内容https://twitchapps.com/tokengen/设置。

一旦我们使用以太网上网,我们就从连接Twitch开始。此时,我们可以在Twitch聊天中发送一条消息,这既是一种让聊天者知道他们可以使用这些命令的方式,也是一种可视化代码工作的方式。之后,我们开始在Twitch聊天中收听消息。注意:通过设置一个连续循环来实现这一点似乎更简单,所以我在这里添加了一个监听PSoC消息的调用,因为在此期间它不会运行主循环。


设置Twitch命令的标准方法是在命令后面加一个“!”。在这种情况下,我设置了它!color<value>和!亮度<值>命令。监听Twitch消息的代码会查找这些消息,并通过调用我们之前设置的更改颜色和亮度的函数来处理命令。如果这是一个无效的命令,则不会发生任何事情。

由于提供了代码,并且我已经完成了调试过程,所以应该能正常运作了。如果你对有任何问题,你可以使用Postman,它容易上手并且能提非常清晰地报错,因为有时确实很难看出Arduino出了什么问题。

直播状态设置

这仍然需要添加根据直播状态自动打开和关闭标牌的功能。我本以为这将是项目中最容易的部分之一。毕竟,我们已经在与Twitch建立连接并以更高级的方式控制灯光了。会出什么问题呢?

但是事与愿违,尽管在Postman中确认一切都正确,在尝试建立连接时,我完全没有收到任何回应。我目前的理解是,这在很大程度上是由于Arduino(或者至少我尝试过的库)与SSL存在兼容问题,而这确实是一个https调用。

我用Python快速编写了一个程序进行了测试,并立即得到了结果。幸运的是,在此期间我有了一个全新的想法。在准备这个项目之前,我一直在参与其他有趣的与Twitch相关的项目,并且最近我还建立了一个用Python运行的主持剧本杀的聊天机器人。这意味着我有一个Python程序,我完全可以在其中加入一点额外的逻辑来设置直播开始和结束的信息。

我希望项目整体上是美观的。一旦我意识到我可以删除为此添加的所有Arduino代码,并添加一个简单的额外部分来监听聊天消息并开闭灯光(项目中已经存在这两个功能),我意识到这实际上能使整个项目变得更加简洁。我创建了一个简单的、通用的Python程序,可供使用和修改,并将其包含在项目中。它可以持续在一个单独的进程中运行。

我对剧本杀聊天机器人做出了一定的修改,在Twitch聊天中发送一条消息,说明直播模式开启时应该如何使用该机器人,并发送一条简单的消息,表示直播已结束。




你可能会想到一个问题,那就是 Twitch 用户可以使用相同的消息来欺骗 Arduino。然而,那只是打开和关闭灯光,这与我提供给他们的功能相同。这几乎就像是一个彩蛋,用来奖励聪明的聊天者。但是,如果它真的引发了某种问题,我总是可以稍微修改一下Arduino代码,来查找是谁发送了这条消息。

收尾工作

现在是时候通过一些收尾工作来让这个项目画上一个完美的句号了!

一切从焊接开始。PSoC 设备是唯一需要可访问以供使用的设备,因为这是我控制标牌的方式。为了完成这项工作,我使用了一根更长的电缆,然后可以将其隐藏起来。但在开始这个过程之前,我在外壳上刻了一个小缺口,以便电缆通过,这样我就可以重新安装外壳的后部。完成这一步之后,我将电缆焊接到了之前的 TX 引脚上。

为了适应直播,我把标牌放在一个盒子上。我在盒子上切了一个洞,将多余的 LED 条穿过去,然后从侧面穿出去。然后我重新连接了 LED 条,将 Arduino 和面包板移到桌子底下看不见的地方。电缆沿着桌子底部延伸,这样的话就只能看见标牌和 PSoC 设备。

最后一步是在标牌上附上背板,以确保它发出亮光。为此,我只是使用了一块薄的纸板,把它涂成与标牌的其余部分相匹配的颜色。添加了背板后,标牌的效果明显提升,让标牌变得更加明亮。

至此,我们已经完成了整个项目。标牌在 Twitch 直播开始时打开,监听 Twitch 命令和从 PSoC 设备接收的串行消息,以改变标牌的颜色和亮度,在直播结束时会自动关闭。



【代码】

Arduino代码-它控制LED条并监听PSoC消息和Twitch命令
#include <SPI.h>
#include <Ethernet.h>

// Replace with the twitch bot's username
#define TWITCH_BOT_USERNAME "<your bot username>"

// Replace with the twitch bot's OAuth key
#define OAUTH_KEY "oauth:<your oauth token here>"

// Twitch IRC server and port
#define TWITCH_IRC_SERVER "irc.chat.twitch.tv"
#define TWITCH_IRC_PORT 6667

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// Ethernet client object
EthernetClient client;

#define RED_LED 6
#define BLUE_LED 5
#define GREEN_LED 9
int brightness = 255;

int greenValue = 0;
int redValue = 0;
int blueValue = 0;

int fadeSpeed = 10;
bool isLedOn = false;
boolean turnLedOn = false;
long previousCheck = 0L;
String channel = "<your channel here>";
String clientId = "<your client id here>";
#define TWITCH_CLIENT_ID "<your client id here>"
#define TWITCH_LOGIN "<your channel here>"

bool isStreamLive = false;
bool internetConnected = false;
int brightnessIndex, colorIndex, psocRed, psocGreen, psocBlue = -1;

unsigned long delayBetweenRequests = 60000; // Time between requests (1 minute)

void sendMessage(String message) {
client.print("PRIVMSG #" + channel + " :" + message + "\r\n");
}

void turnOnOffLed(bool turnOn) {
for (int i = 0; i < 256; i++) {
    analogWrite(GREEN_LED, brightness);
    analogWrite(RED_LED, brightness);
    analogWrite(BLUE_LED, brightness);
    if (turnOn)
      brightness++;
    else
      brightness--;
    delay(fadeSpeed);
}
isLedOn = turnOn;
}

void setLedColor(int red, int green, int blue) {
//Update these values as well so we can use them for brightness settings
greenValue = green;
redValue = red;
blueValue = blue;
for (int i = 0; i < 256; i++) {
    analogWrite(GREEN_LED, green);
    analogWrite(RED_LED, red);
    analogWrite(BLUE_LED, blue);
    delay(fadeSpeed);
}
isLedOn = true;
}

void updateLEDStrip() {
if (turnLedOn) {
    if (!isLedOn) {
      turnOnOffLed(true);
    }
    for (int i = 0; i < 256; i++) {
      analogWrite(GREEN_LED, greenValue);
      analogWrite(RED_LED, redValue);
      analogWrite(BLUE_LED, blueValue);
    }
}
else if (isLedOn)
    turnOnOffLed(false);

}

void changeBrightness(int brightness) {
if (brightness >= 0 && brightness <= 255) {
    int newRedValue = redValue * brightness / 255;
    int newGreenValue = greenValue * brightness / 255;
    int newBlueValue = blueValue * brightness / 255;

    int totalColorValue = newRedValue + newGreenValue + newBlueValue;
    float redRatio = (float)newRedValue / (float)totalColorValue;
    float greenRatio = (float)newGreenValue / (float)totalColorValue;
    float blueRatio = (float)newBlueValue / (float)totalColorValue;

    newRedValue = redRatio * brightness;
    newGreenValue = greenRatio * brightness;
    newBlueValue = blueRatio * brightness;

    setLedColor(newRedValue, newGreenValue, newBlueValue);
}
else {
    // Handle error if the brightness value is not within the range of 0-255
    Serial.println("ERROR: Invalid brightness value. Brightness must be between 0 and 255.");
}
}

//Function to change the color of the lights
void changeColor(String currentColor, int colorIndex) {
Serial.println("New color: " + currentColor);
if (currentColor == "red" || colorIndex == 1) {
    //Set the lights to red
    setLedColor(255, 0, 0);
} else if (currentColor == "green" || colorIndex == 2) {
    //Set the lights to green
    setLedColor(0, 255, 0);
} else if (currentColor == "blue" || colorIndex == 3) {
    //Set the lights to blue
    setLedColor(0, 0, 255);
} else if (currentColor == "yellow" || colorIndex == 4) {
    //Set the lights to yellow
    setLedColor(255, 255, 0);
} else if (currentColor == "purple" || colorIndex == 5) {
    //Set the lights to purple
    setLedColor(128, 0, 128);
} else if (currentColor == "orange" || colorIndex == 6) {
    //Set the lights to orange
    setLedColor(255, 165, 0);
} else if (currentColor == "white" || colorIndex == 7) {
    //Set the lights to white
    setLedColor(255, 255, 255);
} else if (currentColor == "off") {
    //Turn the lights off
    turnOnOffLed(false);
} else {
    Serial.println(currentColor + " is an invalid color");
}
}

//Dont let twitch users do anything funky
String cleanString(String input) {
input.trim(); // remove extra spaces from the beginning and end of the string
input.replace(" ", ""); // remove all spaces in the string
input.toLowerCase(); // set the string to all lowercase

// remove any illegal characters
for (int i = 0; i < input.length(); i++) {
    if (!isAlphaNumeric(input)) {
      input.remove(i, 1);
      i--;
    }
}
return input;
}

void updateLedFromTwitchChatMessage(String incomingMessage) {
//Check for the "!color" command
if (incomingMessage.startsWith("!color")) {
    //Extract the color value from the incoming message
    String color = cleanString(incomingMessage.substring(7));
    Serial.println("Color " + color);
    changeColor(color, -1);
}
//Check for the "brightness" command
if (incomingMessage.startsWith("!brightness")) {
    //Extract the brightness value from the incoming message
    String cleanedString = cleanString(incomingMessage.substring(11));
    int newBrightness = cleanedString.toInt();
    //Check if the new brightness value is valid
    if ((newBrightness != 0 || cleanedString == "0") && newBrightness >= 0 && newBrightness <= 255) {

      //Change the brightness of the lights
      changeBrightness(newBrightness);
    } else {
      Serial.println(cleanedString + " is not a valid brightness");
    }
}
}

void connectToTwitch() {
// Connect to Twitch IRC server
if (client.connect(TWITCH_IRC_SERVER, TWITCH_IRC_PORT)) {
    client.print("PASS " + String(OAUTH_KEY) + "\r\n");
    client.print("NICK " + String(TWITCH_BOT_USERNAME) + "\r\n");
    client.print("JOIN #"+channel"+\r\n");
} else {
    // Connection failed
    Serial.println("ERROR: Connection to Twitch failed.");
    Serial.println("Error code: " + String(client.getWriteError()));
}
}

void receiveTwitchMessages() {
while (client.connected()) {
    if (client.available()) {
      String response = client.readStringUntil('\n');
      if (response == "PING :tmi.twitch.tv\r\n") {
      client.print("PONG :tmi.twitch.tv\r\n");
      } else {
      Serial.println(response);
      int startIndex = response.indexOf(":", 1);
      int endIndex = response.indexOf("\r\n");
      if (endIndex < 0)
          endIndex = response.length();

      if (startIndex != -1 && endIndex != -1) {
          String message = response.substring(startIndex + 1, endIndex);
          Serial.println("The message is " + message);
          if (message.startsWith("!")) {
            Serial.println("Sending message: " + message);
            updateLedFromTwitchChatMessage(message);
          } else if (message.indexOf("Press !mystery to start off a mystery anytime! (I'm a bot)") >= 0 && !isStreamLive) {
            isStreamLive = true;
            turnOnOffLed(isStreamLive);
          } else if (message.indexOf("The stream is now offline.") >= 0 && isStreamLive) {
            isStreamLive = false;
            turnOnOffLed(isStreamLive);
          }
      }
      }
    }
    //The loop is inaccessible, so just call for psoc messages here instead
    receivePsocMessages();    //Via serial
}
client.stop();
}

void receivePsocMessages() {
// Check for incoming serial data from the PSOC

if (Serial1.available() > 0) {
    int redHexValue, greenHexValue, blueHexValue, b, c;
    String incomingData = Serial1.readStringUntil('\n');

    Serial.print("incomingData ");
    Serial.println(incomingData);
    if (incomingData.startsWith("RGB:")) {

      // Extract the red, green, and blue values from the incoming data
      sscanf(incomingData.c_str(), "RGB:%02X%02X%02X", &redHexValue, &greenHexValue, &blueHexValue);

      Serial.print("RGB " + String(redHexValue) + " " + String(greenHexValue) + " " + String(blueHexValue));

      if (psocRed != redValue || psocGreen != greenValue || psocBlue != blueValue) {
      turnLedOn = true;
      psocRed, redValue = redHexValue;
      psocGreen, greenValue = greenHexValue;
      psocBlue, blueValue = blueHexValue;
      updateLEDStrip();
      }
    } else if (incomingData.startsWith("Brightness Index: ")) {

      // Extract the red, green, and blue values from the incoming data
      sscanf(incomingData.c_str(), "Brightness Index: %d", &b);
      Serial.print("Brightness Index: " + String(b));

      //Change the brightness of the lights
      if (b != brightnessIndex) {
      changeBrightness(51 * b); //goes from 0-255
      brightnessIndex = b;
      }
    } else if (incomingData.startsWith("Color Index: ")) {

      // Extract the red, green, and blue values from the incoming data
      sscanf(incomingData.c_str(), "Color Index: %d", &c);
      Serial.print("Color Index: " + String(c));

      if (c != colorIndex) {
      changeColor("", c);
      colorIndex = c;

      }
    }
}
}
void setup() {
//pinMode(led, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);

turnOnOffLed(true);
delay(5000);
turnOnOffLed(false);
Serial.begin(115200);
Serial1.begin(115200);
setupConnection();
Serial.println("Setup done");
}
void setupConnection() {
// start Ethernet and UDP
if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.Sorry, can't run without hardware. :(");
    } else if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
} else {
    Serial.println("Internet is connected");
    internetConnected = true;
}
connectToTwitch();
sendMessage("Hi Twitch - You can change the light colors with !color <color> commands! (sent via a bot)");

}

void loop() {
if (millis() - previousCheck >= 120000L) { // check every 2 minutes
    previousCheck = millis();
    if (!internetConnected) {
      Serial.println("Attempting to setup connection");
      setupConnection();
    }
}
receiveTwitchMessages();//Via IRC on ethernet
receivePsocMessages();    //Via serial
}
Python代码-这是代码的通用版本,你可以使用它来检查流状态,并在Twitch Chat中发送消息,然后Arduino代码可以监听这些消息 import requests
import time
from twitchio.ext import commands
from twitchio.client import Client
from twitchio import Message

# Replace with your Twitch client ID and OAuth token
TWITCH_CLIENT_ID = 'your_client_id'
TWITCH_OAUTH_TOKEN = 'your_oauth_token'
TWITCH_LOGIN = 'your_login_name'
TWITCH_CHANNEL = 'twitch_channel_name'

# Keep track of the current stream status
stream_live = False

CHECK_INTERVAL = 60# seconds


def is_stream_live():
    url = f'https://api.twitch.tv/helix/streams?user_login={TWITCH_LOGIN}'
    headers = {
      'Client-ID': TWITCH_CLIENT_ID,
      'Authorization': f'Bearer {TWITCH_OAUTH_TOKEN}'
    }
    response = requests.get(url, headers=headers)
    data = response.json()
    print(data)
    if data.get('data') and data['data'].get('type') == 'live':
      return True
    return False


def send_message(msg):
    bot = commands.Bot(
      irc_token='your_irc_token',
      client_id='your_client_id',
      nick='your_bot_name',
      prefix='!',
      initial_channels=
    )
    bot.run()
    bot._ws.send_privmsg(TWITCH_CHANNEL, msg)


def main():
    stream_live = False
    while True:
      live = is_stream_live()
      if live != stream_live:
            stream_live = live
            msg = 'The stream has started! Get in here for good times!' if live else 'The stream has ended. Hope you enjoyed!'
            send_message(msg)
            print(msg)
      time.sleep(CHECK_INTERVAL)


if __name__ == '__main__':
    main()
PSoC代码-这是Color Gamut示例项目的更新主.c文件,其中按钮被修改,然后数据通过UART发送到Arduino设备
/******************************************************************************
* File Name: main.c
*
* Version: 1.00
*
* Description:This code example demonstrates how to use CapSense trackpad
*               to input RGB color code for color mixing with PSoC 4 S-Series device.
*
* Related Document: CE214025 Trackpad with Color Gamut.pdf
*
* Hardware Dependency: See code example document CE214025 Trackpad with Color Gamut.pdf
*
******************************************************************************
* Copyright (2016), Cypress Semiconductor Corporation.
******************************************************************************
* This software, including source code, documentation and related materials
* ("Software") is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress source code and derivative works for the sole purpose of creating
* custom software in support of licensee product, such licensee product to be
* used only in conjunction with Cypress's integrated circuit as specified in the
* applicable agreement. Any reproduction, modification, translation, compilation,
* or representation of this Software except as specified above is prohibited
* without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes to the Software without notice.
* Cypress does not assume any liability arising out of the application or use
* of Software or any product or circuit described in the Software. Cypress does
* not authorize its products for use as critical components in any products
* where a malfunction or failure may reasonably be expected to result in
* significant injury or death ("ACTIVE Risk Product"). By including Cypress's
* product in a ACTIVE Risk Product, the manufacturer of such system or application
* assumes all risk of such use and in doing so indemnifies Cypress against all
* liability. Use of this Software may be limited by and subject to the applicable
* Cypress software license agreement.
*****************************************************************************/
/*******************************************************************************
*   Included Headers
*******************************************************************************/
#include <project.h>

/* Include boolean function definition */
#include <stdbool.h>

/* Include color mixing API declarations */
#include "colormixing.h"

/* Include sprintf API definitions */
#include <stdio.h>

/*******************************************************************************
*   Macros and #define Constants
*******************************************************************************/

/*The UART variables*/
char string;   //Used to send the brightness data
#define SEND_INTERVAL       (2000u)


/* Intensity control button status */   
#define BUTTON_ON                   (0x01u)
#define BUTTON_OFF                  (0x00u)

/* Multiplier value for color mixing to avoid floating point math */   
#define POS_MULT_100                (100u)   

/* Number of active sensors when palm is placed on trackpad */
#define ACTIVE_SENSORS_PALM          (4u)

/* Minimum and maximum intensity values. This value is divided by 10 and is multiplied
*by MAX_COMPAREVALUE   
*/   
#define MAX_INTENSITY_LEVEL         (16u)

/* Default intensity value */   
#define DEFAULT_LED_INTENSITY       (MAX_COMPAREVALUE/2)

/*LED wil be driven for the specified number of loops after no touch*/
#define MAX_LOOPS_TO_DRIVE_LED      (75u)

/*Index for brightness control array*/
#define MAX_INTENSITY_INDEX         (3u)
#define MIN_INTENSITY_INDEX         (0u)

/*LED brightness macros*/
#define LED_BRIGHTNESS_12_5         (2u)
#define LED_BRIGHTNESS_25_0         (4u)
#define LED_BRIGHTNESS_37_5         (6u)
#define LED_BRIGHTNESS_100_0      (16u)

/*Offset correction macros*/
#define MIN_X_COORDINATE            (15u)
#define MAX_X_COORDINATE            (75u)
#define MIN_Y_COORDINATE            (8u)
#define MAX_Y_COORDINATE            (77u)
#define Y_COORDINATE_OFFSET         (7u)
#define Y_COORDINATE_NEED_OFFSET    (29u)
#define MAX_X_COORDINATE_FOR_COLOR_MIXING   (64u)

/*******************************************************************************
*   Module Variable and Constant Declarations with Applicable Initializations
*******************************************************************************/
   
/* Finite state machine states for device operation */
typedef enum
{
    SENSOR_SCAN = 0x01u,    /* Sensor is scanned in this state */
    WAIT_FOR_SCAN_COMPLETE = 0x02u, /* CPU is put to sleep in this state */
    PROCESS_DATA = 0x03u,   /* Sensor data is processed */
} DEVICE_STATE;

const uint8 brightnessLevels[] = {LED_BRIGHTNESS_12_5,LED_BRIGHTNESS_25_0
                                    ,LED_BRIGHTNESS_37_5 ,LED_BRIGHTNESS_100_0};

uint8 brightnessIndex = MAX_INTENSITY_INDEX;

/*******************************************************************************
*Function Declarations
*******************************************************************************/

/* Function prototype for CapSense parameter initialization */
void capSenseInit(void);

/* Function prototype to scan CapSense sensors */
void capSenseProcess(void);

/* Function prototype to initialize TCPWM components */
void prismInit(void);

/* Function that checks if trackpad or button sensors are ON */
bool anyWidgetActive(void);

/* Function that performs color mixing */
void colorMixingProcess(void);

/*******************************************************************************
*   Module Variable and Constant Declarations with Applicable Initializations
*******************************************************************************/

/* Contains current x, y coordinates of trackpad */
uint16 trackpadXPos = CapSense_SLIDER_POS_NONE;
uint16 trackpadYPos = CapSense_SLIDER_POS_NONE;   

/* Variable to store color mixing error status */
uint8 colorMixingError;

/* Variable to store interrupt state */
uint32 interruptState = 0u;

/* Variable to store the current trackpad XY coordinates.
*The XY coordinate is multiplied with 100 to avoid floating point math
*/
XY_COORDINATE currentXYCoordinate;

/* Variable to store last valid touch coordinates */
XY_COORDINATE lastValidXYCoordinate;

/* Variable to store RGB led dimming values */
uint16 rgbLEDDimmingValue;

uint16 timeoutCounter;

/* Variable to store RGB LED coordinates on the color gamut
*The coordinates are multiplied by 10000 to avoid floating point math
*/
LED_COORDINATE rgbLEDXYPosition;

/* Variable to store On/Off condition of trackpad */
uint8 trackpadStatus = 0;

/* Variable to control the maximum intensity of RGB LED
*Variable takes the following values 2, 4, 8 and 16
*The resulting PWM compare value is multiplied with this value
*and normalized to achieve duty cycle of 100%, 50%, 25% and 12.5%
*/
uint8 intensityCtrl = MAX_INTENSITY_LEVEL;

/* Variable to store the status of intensity control buttons */
uint8 button0CurrState = BUTTON_OFF;
uint8 button0PrevState = BUTTON_OFF;
uint8 button1CurrState = BUTTON_OFF;
uint8 button1PrevState = BUTTON_OFF;


/* Minimum and maximum brightness index */
#define MIN_BRIGHTNESS_INDEX 0
#define MAX_BRIGHTNESS_INDEX 5

/* Minimum and maximum color index */
#define MIN_COLOR_INDEX 1
#define MAX_COLOR_INDEX 7

/* Current color index */
uint8 colorIndex = MIN_COLOR_INDEX;



/******************************************************************************
* Function Name: main
*******************************************************************************
*
* Summary: This function implements the state machine for device operation.
*
* Parameters:
*None.
*
* Return:
*None.
*
* Theory:
*   main() performs following functions:
*1: Initialize the CapSense, EZI2C and PWM Components
*2: Scans trackpad, buttons and performs color mixing and brightness control using
*   RGB LED
*
* Side Effects: None
*
* Note:None
*
*******************************************************************************/

int main()
{   
    /* Start the firmware state machine with sensor scan */
    DEVICE_STATE currentState = SENSOR_SCAN;
   
    /* Variable to hold active sensors count */
    uint16 sensorCount = 0u;
   
    /* Variable to check active sensor bits */
    uint16 mask = 0u;
   
    /* Temporary variable */
    uint16 tempVar = 0u;   
   
    /* Enable interrupts. This is required for CapSense and I2C operation */
    CyGlobalIntEnable;
   
    /* Initialize I2C component for CapSense tuner */
    EZI2C_Start();
   
    /* Set up communication data buffer to CapSense data structure to
    * expose to I2C master at primary slave address request
    */
    EZI2C_EzI2CSetBuffer1(sizeof(CapSense_dsRam), sizeof(CapSense_dsRam),\
                         (uint8 *)&CapSense_dsRam);
   
    /* Start the CapSense component and autocalibrate IDAC values */
    capSenseInit();
   
    /* Initialize PWM components */
    prismInit();
   
    /* Load XY coordinates of RGB LED on color gamut into local variables */
    initializeLedCoordinates(rgbLEDXYPosition);
   
    SW_Tx_UART_1_Start();
   
    for(;;)
    {      
      switch(currentState)
      {
            case SENSOR_SCAN:
                        {
                /* Initiate new scan only if the CapSense hardware is idle */
                if(CapSense_NOT_BUSY == CapSense_IsBusy())
                {
                  /* Update CapSense parameters set via CapSense tuner */
                  CapSense_RunTuner();      
                  
                  /*Scan trackpad and buttons*/
                  CapSense_ScanAllWidgets();
                  
                  /* Set the state machine to wait state until the scan is complete */
                  currentState = WAIT_FOR_SCAN_COMPLETE;   
                }
                  break;
                        }
            
                        case WAIT_FOR_SCAN_COMPLETE:
                        {
                /* Device is in CPU Sleep until CapSense scanning is complete or
                *device is woken-up by either CapSense interrupt or I2C interrupt
                */
                /* Disable interrupts, so that ISR is not serviced while
                *checking for CapSense scan status. Otherwise, interrupt might
                *get serviced after checking for IsBusy condition and device
                *might not wakeup since CapSense interrupt is already serviced
                */
                interruptState = CyEnterCriticalSection();
                           
                /* Check if CapSense scanning is complete */
                if(CapSense_NOT_BUSY != CapSense_IsBusy())
                {
                  /* If CapSense scannning is in progress, put CPU to sleep
                  *Device wakesup because of CapSense scan complete interrupt
                  */
                  CySysPmSleep();
                }
                /* If CapSense scanning is complete, process the CapSense data */
                else
                {
                  /* If current widget is trackpad or mode is slow scan mode, process the sensor data */
                  currentState = PROCESS_DATA;
                }
                /* Enable interrupts for servicing ISR */
                CyExitCriticalSection(interruptState);
                  break;
            }         
            
                        case PROCESS_DATA:
            {            
                     
                /* Set next state to Sensor Scan */
                currentState = SENSOR_SCAN;
                CapSense_ProcessAllWidgets();
                /* The below code resets trackpad baseline if a palm is detected on the trackpad */
                if(CapSense_IsWidgetActive(CapSense_TRACKPAD_WDGT_ID))
                {
                  /* Initialize mask variable */
                  mask = 1u;
                  
                  /* Set active sensor count to zero */
                  sensorCount = 0u;
                  
                  /* Loop through all the row and column sensors */
                  for(tempVar = CapSense_TRACKPAD_COL0_ID; tempVar <= CapSense_TRACKPAD_ROW6_ID; tempVar++)
                  {
                        /* Check each bit for active sensor condition */
                        if(CapSense_SNS_STATUS0_VALUE & mask)
                        {
                            /* Increment sensor count for each active sensor */
                           sensorCount++;
                        }
                        /* If all the column sensors are searched and active sensorCount is not greater than threshold
                        *reset the sensorCount variable to detect active row sensors
                        */
                        if((tempVar == CapSense_TRACKPAD_COL6_ID) && (sensorCount <= ACTIVE_SENSORS_PALM))
                        {
                            sensorCount = 0u;
                        }
                        
                        /* If active sensor count in either a row or column has exceed the threshold
                        *reset all the trackpad sensor baselines
                        */
                        if(sensorCount > ACTIVE_SENSORS_PALM)
                        {
                            CapSense_InitializeWidgetBaseline(CapSense_TRACKPAD_WDGT_ID);
                            break;
                        }
                        /* Update the mask variable until all the bits are scanned for active status */
                        mask = mask << 1u;
                  }                           
                }
                /* Check if Left button is active */
                if(CapSense_IsWidgetActive(CapSense_INTENSITYUP_WDGT_ID))
                  button0CurrState = BUTTON_ON;
                else
                  button0CurrState = BUTTON_OFF;

                /* Check if Right button is active */
                if(CapSense_IsWidgetActive(CapSense_INTENSITYDOWN_WDGT_ID))
                  button1CurrState = BUTTON_ON;
                else
                  button1CurrState = BUTTON_OFF;

                /* Check for rising edge of Left button statusand toggle brightness value 0-5 */
                if((button0CurrState == BUTTON_ON) && (button0PrevState == BUTTON_OFF))
                {                  
                  if(brightnessIndex >= MAX_BRIGHTNESS_INDEX)
                  {
                        brightnessIndex = MIN_BRIGHTNESS_INDEX;
                  }
                  else
                  {
                        brightnessIndex++;
                  }
                }
                /* Check for rising edge of Right button statusand toggle color settings 1-7 */
                else if((button1CurrState == BUTTON_ON) && (button1PrevState == BUTTON_OFF))
                {
                  if(colorIndex >= MAX_COLOR_INDEX)
                  {
                        colorIndex = MIN_COLOR_INDEX;
                  }
                  else
                  {
                        colorIndex++;
                  }
                }

            /* Initialize previous button state to current button state */
            button0PrevState = button0CurrState;
            button1PrevState = button1CurrState;               
         
            /* If trackpad or button sensor is not active, increment the LED timeout counter */
         if(!anyWidgetActive())
             {
                timeoutCounter++;

                /* Check if sensor is inactive for a duration greater than MAX_LOOPS_TO_DRIVE_LED */
                if(timeoutCounter >= MAX_LOOPS_TO_DRIVE_LED)
                {                        
                  /* Set LED pin drive mode to high-z to stop driving LEDs */
                  Red_LED_SetDriveMode(Red_LED_DM_ALG_HIZ);
                  Green_LED_SetDriveMode(Green_LED_DM_ALG_HIZ);
                  Blue_LED_SetDriveMode(Blue_LED_DM_ALG_HIZ);                        
                }
            }
            /* If either trackpad or button sensors are active, perform color mixing */
            else
            {                  
                /* Because sensor is active, reset the counter */
                timeoutCounter = 0;

                /* If widget is active, perform color mixing */
                colorMixingProcess();
            }                              
                break;
      }
         
            default:
                        {
            /*******************************************************************
             * Unknown state. Unexpected situation.
             ******************************************************************/
                  CYASSERT(0);
                  break;
                        }
      }
    }
}

/******************************************************************************
* Function Name: capSenseInit
*******************************************************************************
*
* Summary: This API initializes CapSense block
*
* Parameters:
*None.
*
* Return:
*None.
*
* Theory:
*   capSenseInit() performs following functions:
*1: Starts the CapSense block
*2: Scan the trackpad widget and initialize the previous touch coordinate values
*
* Side Effects: None
*
* Note: None
*
*******************************************************************************/
void capSenseInit(void)
{
    /* Variable to store the XY coordinates */
    uint32 tempCoordinates;
   
    /* Initialize CapSense block */
    CapSense_Start();
   
    CapSense_InitializeWidgetBaseline(CapSense_INTENSITYUP_WDGT_ID);
    CapSense_InitializeWidgetBaseline(CapSense_INTENSITYDOWN_WDGT_ID);
   
    /* Scan the trackpad widget and initialize the previous touch coordinate values*/
    trackpadStatus =CapSense_IsWidgetActive(CapSense_TRACKPAD_WDGT_ID);
   
    /* Get XY Coordinates */
    tempCoordinates = CapSense_GetXYCoordinates(CapSense_TRACKPAD_WDGT_ID);
   
    /* Load XY position to variable. Note: the XY position is interchanged as the
    *columns and rows in the PCB layout is inverse of X, Y coordinate of color gamut
    */
    trackpadYPos = LO16(tempCoordinates);
    trackpadXPos = HI16 (tempCoordinates);
   
    /* Initialize last valid coordinates so that RGB LED glows
    *when button is touched before trackpad after device power-up or reset.
    */
    lastValidXYCoordinate.currentX = RED_BASE_COLOR_X;
    lastValidXYCoordinate.currentY = RED_BASE_COLOR_Y;
   
    /* Set default led intensity value */
    currentXYCoordinate.ledIntensity = DEFAULT_LED_INTENSITY;
}

/******************************************************************************
* Function Name: prismInit
*******************************************************************************
*
* Summary: This API initializes PWM components
*
* Parameters:
*None.
*
* Return:
*None.
*
* Theory:
*prismInit() performs following functions:
*1: Starts the TCPWM block
*2: Initializes the TCPWM compare value to minimum value
*
* Side Effects: None
*
* Note: None
*
*******************************************************************************/
void prismInit(void)
{
    /* Start TCPWM Blocks */
    PrISM_Red_Start();
    PrISM_Green_Start();
    PrISM_Blue_Start();
   
    /* Set TCPWM compare value to zero initially */
    PrISM_Red_WriteCompare(MIN_COMPAREVALUE);
    PrISM_Green_WriteCompare(MIN_COMPAREVALUE);
    PrISM_Blue_WriteCompare(MIN_COMPAREVALUE);

}
/*******************************************************************************
* Function Name: anyWidgetActive
********************************************************************************
*
* Summary:
*This API checks if any widget is active
*
* Parameters:
*None.
*
* Theory: This API checks if any of the IntensityUp, IntensityDown or trackpad sensors
*         are active and returns true if any one of them is active.
*
* Side Effects: None
*
* Note: None
*
*******************************************************************************/

bool anyWidgetActive(void)
{
    /* Check if either trackpad or any of the two button sensors are active */
    if(CapSense_IsWidgetActive(CapSense_TRACKPAD_WDGT_ID) || \
       CapSense_IsWidgetActive(CapSense_INTENSITYUP_WDGT_ID) || \
       CapSense_IsWidgetActive(CapSense_INTENSITYDOWN_WDGT_ID))
    {
      return TRUE;
    }
    else
    {
      return FALSE;
    }
}

/*******************************************************************************
* Function Name: colorMixingProcess
********************************************************************************
*
* Summary:
*This API checks if any widget is active and computes the TCPWM compare value
*
* Parameters:
*None.
*
* Theory: This API checks if any of the IntensityUp, IntensityDown or trackpad sensors
*         are active. Based on the status, the color mixing algorithm is executed to
*         compute the compare
*
* Side Effects: None
*
* Note: None
*
*******************************************************************************/
void colorMixingProcess(void)
{
    /* Temporary variable */
    uint32 tempVar;
   
    /* Variable to store XY coordinates of trackpad */
    uint32 tempCoordinates;

    /* Obtain the maximum compare value depending on the intensity level set by user */
    tempVar = (uint32) (intensityCtrl * MAX_COMPAREVALUE);
    tempVar /= MAX_INTENSITY_LEVEL;
    currentXYCoordinate.ledIntensity = (uint16)tempVar;
    lastValidXYCoordinate.ledIntensity = currentXYCoordinate.ledIntensity;   
   
    /* Check if trackpad is active */
    trackpadStatus =CapSense_IsWidgetActive(CapSense_TRACKPAD_WDGT_ID);
   
    /* Get trackpad XY coordinates */
    tempCoordinates = CapSense_GetXYCoordinates(CapSense_TRACKPAD_WDGT_ID);
   
    /* XY position values and store in local variable
    *Note: The XY position is interchanged because the rows and columns are
    *interchanged in the kit
    */
    trackpadYPos = LO16(tempCoordinates);
    trackpadXPos = HI16(tempCoordinates);
   
    /* If trackpad is active, load the XY position for computing dimming values */
    if(trackpadStatus)
    {
      /* Normalization is done because row0 to row6 and column0 to column 6
      *are inverted in the layout      
      */
      trackpadXPos = CapSense_TRACKPAD_Y_RESOLUTION - trackpadXPos;
      trackpadYPos = CapSense_TRACKPAD_X_RESOLUTION - trackpadYPos;
      
      /*Offset correction to get smooth response near color gamut edges*/
      if(trackpadXPos <= MIN_X_COORDINATE)
      {
            trackpadXPos = MIN_X_COORDINATE;
      }      
      else if (trackpadXPos >= MAX_X_COORDINATE_FOR_COLOR_MIXING && trackpadXPos <= MAX_X_COORDINATE)
      {
            trackpadXPos = MAX_X_COORDINATE_FOR_COLOR_MIXING;
      }      
      
      if(trackpadYPos <= MIN_Y_COORDINATE)
      {
            trackpadYPos = MIN_Y_COORDINATE;
      }
      else if (trackpadYPos <= Y_COORDINATE_NEED_OFFSET)
      {
            trackpadYPos = trackpadYPos + Y_COORDINATE_OFFSET;
      }
      else if(trackpadYPos >= MAX_Y_COORDINATE)
      {
            trackpadYPos = MAX_Y_COORDINATE;
      }
      
    }
    else
    {
      trackpadXPos = 0u;
      trackpadYPos = 0u;
    }
   
    /* Multiply the coordinate value by 100 to avoid floating point math */
    currentXYCoordinate.currentX = (int16)(trackpadXPos * POS_MULT_100);
    currentXYCoordinate.currentY = (int16)(trackpadYPos * POS_MULT_100);
   
    /* If finger is on trackpad, use current touch coordinates to compute dimming values */
    if(trackpadStatus)
    {
      /* Compute RGB LED dimming values for a given XY coordinate */
      colorMixingError = rgbColorMix(currentXYCoordinate, rgbLEDXYPosition, rgbLEDDimmingValue);
      
      /* If color mixing was successful, save the current coordinates */
      if(colorMixingError != INVALID_COLOR)
      {
            lastValidXYCoordinate.currentX = currentXYCoordinate.currentX;
            lastValidXYCoordinate.currentY = currentXYCoordinate.currentY;
      }
    }
    /* If IntensityUp or IntensityDown button is pressed, compute dimming value using previous saved touch coordinates */
    else
    {
      colorMixingError = rgbColorMix(lastValidXYCoordinate, rgbLEDXYPosition, rgbLEDDimmingValue);
    }

    /* Update the LED dimming value only when color mixing process is successful */
    if(colorMixingError != INVALID_COLOR)
    {
      /* Set LED pin drive mode to strong to save power */
      Red_LED_SetDriveMode(Red_LED_DM_STRONG);
      Green_LED_SetDriveMode(Green_LED_DM_STRONG);
      Blue_LED_SetDriveMode(Blue_LED_DM_STRONG);
                        
      /* Load the computed dimming value to the PrISM component */
      PrISM_Red_WriteCompare(rgbLEDDimmingValue);
      PrISM_Green_WriteCompare(rgbLEDDimmingValue);
      PrISM_Blue_WriteCompare(rgbLEDDimmingValue);
      
      
      /* Transmit different data types through the UART */
      SW_Tx_UART_1_PutString("RGB:");
      SW_Tx_UART_1_PutHexByte(rgbLEDDimmingValue);
      SW_Tx_UART_1_PutHexByte(rgbLEDDimmingValue);
      SW_Tx_UART_1_PutHexByte(rgbLEDDimmingValue);
      SW_Tx_UART_1_PutCRLF();

      SW_Tx_UART_1_PutString("Brightness Index: ");
      SW_Tx_UART_1_PutHexInt(brightnessIndex);
      SW_Tx_UART_1_PutCRLF();

      SW_Tx_UART_1_PutString("Color Index: ");
      SW_Tx_UART_1_PutHexInt(colorIndex);
      SW_Tx_UART_1_PutCRLF();

      CyDelay(SEND_INTERVAL);
      SW_Tx_UART_1_PutCRLF();
    }
}


/* [] END OF FILE */

希望你会喜欢这个新奇的标牌!祝一切顺利。

原文作者:donutsorelse
原文链接:https://community.dfrobot.com/makelog-313144.html

转载请注明来源信息

Amos Young 发表于 2023-8-23 17:12:30

厉害了,膜拜大神作品

快看擎天猪 发表于 2023-8-24 14:25:00

厉害了,膜拜大神作品

三春牛-创客 发表于 2023-8-26 10:36:25

厉害厉害

三春牛-创客 发表于 2023-8-26 10:37:29

太棒了,赞一个

花生编程 发表于 2023-8-29 17:23:06

厉害厉害!

花生编程 发表于 2023-8-29 17:24:07

不错不错!

快看擎天猪 发表于 2023-8-31 14:24:35

6666666666666666666
页: [1]
查看完整版本: 如何使用电容式触摸定制LED Twitch标牌