Code:
/*
* Made by KinCony IoT: https://www.kincony.com
* CO16 CYBER SHOW - standalone Shorts demo, 32-second automatic loop.
* Target: ESP32-S3, CO16 with PCF8575 relay outputs at 0x22.
* Display: 320x240 ST7789; native SPI3 initialization from CO16-pcb-test.ino.
* Install Adafruit GFX Library and its Adafruit BusIO dependency.
* Board: ESP32S3 Dev Module; retain the settings used for the factory sketch.
* No PCF8575, Ethernet, SD, RTC or ADC libraries required.
*
* IMPORTANT: disconnect all loads from relay terminals before filming.
* These are mechanical relays: use this as a short filming demo, not a
* continuous endurance test. The graphics are synthetic, not sensor data.
* Relay tiles show acknowledged output commands, NOT contact feedback.
*
* Serial Monitor, 115200 baud (no line ending needed):
* 0 = automatic show from beginning; 1..5 = hold a scene
* p = pause/resume (pause switches relays OFF); r = restart auto show
* x = disable relay motion; e = enable relay motion
* Frame rendering uses 153600 bytes of RAM plus a 6400-byte DMA buffer.
* No EEPROM writes, networking, SD operations or other peripheral tests.
*/
#include <Arduino.h>
#include <Wire.h>
#include <math.h>
#include <string.h>
#include <Adafruit_GFX.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_heap_caps.h>
// CO16 ST7789 (GMT020-02-7P) pin mapping from kcs_co16.h.
constexpr gpio_num_t kTftSclk = GPIO_NUM_11;
constexpr gpio_num_t kTftMosi = GPIO_NUM_10;
constexpr gpio_num_t kTftMiso = GPIO_NUM_12;
constexpr gpio_num_t kTftCs = GPIO_NUM_4;
constexpr gpio_num_t kTftDc = GPIO_NUM_0;
constexpr gpio_num_t kTftReset = GPIO_NUM_5;
constexpr gpio_num_t kTftBacklight = GPIO_NUM_40;
constexpr int16_t kDisplayWidth = 320;
constexpr int16_t kDisplayHeight = 240;
constexpr int16_t kTransferChunkHeight = 10;
constexpr uint32_t kDisplaySpiFrequency = 20 * 1000 * 1000;
constexpr uint8_t kCo16Madctl = 0x68; // MX | MV | BGR.
constexpr uint16_t kBlack = 0x0000;
constexpr uint16_t kBlue = 0x001F;
constexpr uint16_t kRed = 0xF800;
constexpr uint16_t kGreen = 0x07E0;
constexpr uint16_t kCyan = 0x07FF;
constexpr uint16_t kMagenta = 0xF81F;
constexpr uint16_t kYellow = 0xFFE0;
constexpr uint16_t kWhite = 0xFFFF;
class Co16St7789 : public Adafruit_GFX {
public:
Co16St7789() : Adafruit_GFX(kDisplayWidth, kDisplayHeight) {}
bool begin() {
gpio_reset_pin(kTftCs);
gpio_set_direction(kTftCs, GPIO_MODE_OUTPUT);
gpio_set_level(kTftCs, 1);
gpio_reset_pin(kTftDc);
gpio_set_direction(kTftDc, GPIO_MODE_OUTPUT);
gpio_set_level(kTftDc, 1);
gpio_reset_pin(kTftBacklight);
gpio_set_direction(kTftBacklight, GPIO_MODE_OUTPUT);
gpio_set_level(kTftBacklight, 0);
spi_bus_config_t busConfig = {};
busConfig.sclk_io_num = kTftSclk;
busConfig.mosi_io_num = kTftMosi;
busConfig.miso_io_num = kTftMiso;
busConfig.quadwp_io_num = -1;
busConfig.quadhd_io_num = -1;
busConfig.max_transfer_sz =
kDisplayWidth * kTransferChunkHeight * sizeof(uint16_t);
esp_err_t err =
spi_bus_initialize(SPI3_HOST, &busConfig, SPI_DMA_CH_AUTO);
if (err != ESP_OK) {
Serial.printf("SPI3 initialization failed: %s\n", esp_err_to_name(err));
return false;
}
spi_device_interface_config_t deviceConfig = {};
deviceConfig.clock_speed_hz = kDisplaySpiFrequency;
deviceConfig.mode = 0;
deviceConfig.spics_io_num = -1;
deviceConfig.queue_size = 1;
err = spi_bus_add_device(SPI3_HOST, &deviceConfig, &spiDevice_);
if (err != ESP_OK) {
Serial.printf("ST7789 SPI device setup failed: %s\n",
esp_err_to_name(err));
return false;
}
drawBuffer_ = static_cast<uint8_t*>(heap_caps_malloc(
kDisplayWidth * kTransferChunkHeight * sizeof(uint16_t),
MALLOC_CAP_DMA));
if (drawBuffer_ == nullptr) {
Serial.println("ST7789 DMA buffer allocation failed");
return false;
}
// GPIO5 is shared with the optional LoRa module. Match the working KCS
// firmware and issue one reset pulse before initializing the controller.
gpio_reset_pin(kTftReset);
gpio_set_direction(kTftReset, GPIO_MODE_OUTPUT);
gpio_set_level(kTftReset, 1);
delay(1);
gpio_set_level(kTftReset, 0);
delay(1);
gpio_set_level(kTftReset, 1);
delay(10);
if (!initializeController()) {
return false;
}
fillScreen(kBlack);
delay(120);
if (!writeCommand(0x29)) { // Display on.
return false;
}
delay(120);
gpio_set_level(kTftBacklight, 1);
initialized_ = true;
return true;
}
void drawPixel(int16_t x, int16_t y, uint16_t color) override {
fillRect(x, y, 1, 1, color);
}
void writePixel(int16_t x, int16_t y, uint16_t color) override {
fillRect(x, y, 1, 1, color);
}
void fillRect(int16_t x, int16_t y, int16_t width, int16_t height,
uint16_t color) override {
if (x < 0) {
width += x;
x = 0;
}
if (y < 0) {
height += y;
y = 0;
}
if (x + width > kDisplayWidth) {
width = kDisplayWidth - x;
}
if (y + height > kDisplayHeight) {
height = kDisplayHeight - y;
}
if (drawBuffer_ == nullptr || width <= 0 || height <= 0) {
return;
}
const uint8_t high = color >> 8;
const uint8_t low = color;
for (int16_t row = y; row < y + height;
row += kTransferChunkHeight) {
const int16_t chunkHeight =
min<int16_t>(kTransferChunkHeight, y + height - row);
const size_t pixelCount = width * chunkHeight;
for (size_t index = 0; index < pixelCount; ++index) {
drawBuffer_[index * 2] = high;
drawBuffer_[index * 2 + 1] = low;
}
if (!setWindow(x, row, x + width, row + chunkHeight) ||
!writePixels(drawBuffer_, pixelCount * sizeof(uint16_t))) {
Serial.println("ST7789 pixel transfer failed");
return;
}
}
}
void writeFillRect(int16_t x, int16_t y, int16_t width, int16_t height,
uint16_t color) override {
fillRect(x, y, width, height, color);
}
// Copy a fully rendered RGB565 frame into the DMA staging buffer.
bool present(const uint16_t* pixels) {
if (!initialized_ || pixels == nullptr) return false;
for (int y = 0; y < kDisplayHeight; y += kTransferChunkHeight) {
const int rows = min<int>(kTransferChunkHeight, kDisplayHeight - y);
const int count = kDisplayWidth * rows;
for (int i = 0; i < count; ++i) {
uint16_t c = pixels[y * kDisplayWidth + i];
drawBuffer_[2 * i] = c >> 8;
drawBuffer_[2 * i + 1] = c & 0xFF;
}
if (!setWindow(0, y, kDisplayWidth, y + rows) ||
!writePixels(drawBuffer_, count * 2)) return false;
}
return true;
}
bool initialized() const { return initialized_; }
private:
bool transmit(const void* data, size_t length, int dcLevel) {
if (data == nullptr || length == 0) {
return true;
}
gpio_set_level(kTftDc, dcLevel);
spi_transaction_t transaction = {};
transaction.length = length * 8;
transaction.tx_buffer = data;
const esp_err_t err = spi_device_polling_transmit(spiDevice_, &transaction);
if (err != ESP_OK) {
Serial.printf("ST7789 SPI transfer failed: %s\n", esp_err_to_name(err));
return false;
}
return true;
}
bool writeCommand(uint8_t command) {
gpio_set_level(kTftCs, 0);
const bool success = transmit(&command, sizeof(command), 0);
gpio_set_level(kTftCs, 1);
return success;
}
bool writeCommandData(uint8_t command, const uint8_t* data, size_t length) {
gpio_set_level(kTftCs, 0);
const bool success = transmit(&command, sizeof(command), 0) &&
transmit(data, length, 1);
gpio_set_level(kTftCs, 1);
return success;
}
bool setWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {
const uint8_t columns[] = {
static_cast<uint8_t>(x1 >> 8), static_cast<uint8_t>(x1),
static_cast<uint8_t>((x2 - 1) >> 8), static_cast<uint8_t>(x2 - 1),
};
const uint8_t rows[] = {
static_cast<uint8_t>(y1 >> 8), static_cast<uint8_t>(y1),
static_cast<uint8_t>((y2 - 1) >> 8), static_cast<uint8_t>(y2 - 1),
};
return writeCommandData(0x2A, columns, sizeof(columns)) &&
writeCommandData(0x2B, rows, sizeof(rows));
}
bool writePixels(const uint8_t* data, size_t length) {
constexpr uint8_t kRamWrite = 0x2C;
gpio_set_level(kTftCs, 0);
const bool success = transmit(&kRamWrite, sizeof(kRamWrite), 0) &&
transmit(data, length, 1);
gpio_set_level(kTftCs, 1);
return success;
}
bool initializeController() {
const uint8_t madctl[] = {kCo16Madctl};
const uint8_t displayFunction[] = {0x0A, 0x82};
const uint8_t pixelFormat[] = {0x55};
const uint8_t porchControl[] = {0x0C, 0x0C, 0x00, 0x33, 0x33};
const uint8_t gateControl[] = {0x35};
const uint8_t vcom[] = {0x28};
const uint8_t lcmControl[] = {0x0C};
const uint8_t vdvVrhEnable[] = {0x01, 0xFF};
const uint8_t vrh[] = {0x10};
const uint8_t vdv[] = {0x20};
const uint8_t frameRate[] = {0x0F};
const uint8_t powerControl[] = {0xA4, 0xA1};
const uint8_t positiveGamma[] = {
0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32,
0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17,
};
const uint8_t negativeGamma[] = {
0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31,
0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E,
};
if (!writeCommand(0x11)) { // Sleep out.
return false;
}
delay(120);
return writeCommand(0x13) &&
writeCommandData(0x36, madctl, sizeof(madctl)) &&
writeCommandData(0xB6, displayFunction, sizeof(displayFunction)) &&
writeCommandData(0x3A, pixelFormat, sizeof(pixelFormat)) &&
(delay(10), true) &&
writeCommandData(0xB2, porchControl, sizeof(porchControl)) &&
writeCommandData(0xB7, gateControl, sizeof(gateControl)) &&
writeCommandData(0xBB, vcom, sizeof(vcom)) &&
writeCommandData(0xC0, lcmControl, sizeof(lcmControl)) &&
writeCommandData(0xC2, vdvVrhEnable, sizeof(vdvVrhEnable)) &&
writeCommandData(0xC3, vrh, sizeof(vrh)) &&
writeCommandData(0xC4, vdv, sizeof(vdv)) &&
writeCommandData(0xC6, frameRate, sizeof(frameRate)) &&
writeCommandData(0xD0, powerControl, sizeof(powerControl)) &&
writeCommandData(0xE0, positiveGamma, sizeof(positiveGamma)) &&
writeCommandData(0xE1, negativeGamma, sizeof(negativeGamma)) &&
writeCommand(0x21); // Display inversion on.
}
spi_device_handle_t spiDevice_ = nullptr;
uint8_t* drawBuffer_ = nullptr;
bool initialized_ = false;
};
Co16St7789 display;
GFXcanvas16 canvas(320, 240);
constexpr uint32_t FRAME_MS = 80; // Target ~12.5 FPS; actual rate depends on SPI.
constexpr uint32_t RELAY_STEP_MS = 250;
constexpr uint32_t SHOW_MS = 32000;
constexpr uint8_t RELAY_ADDRESS = 0x22;
constexpr float TAU = 6.28318530718f;
constexpr uint16_t BG = 0x0001, GRID = 0x0843, CYAN = 0x07FF;
constexpr uint16_t PINK = 0xF81F, WHITE = 0xFFFF, DIM = 0x5B6D;
constexpr uint16_t GREEN = 0x07E0, GOLD = 0xFDE0;
const char* const SCENE_NAMES[] = {
"SYSTEM BOOT", "RELAY RADAR", "ENERGY CORE", "BEAT MATRIX", "ONE BRAIN"
};
uint32_t showTime = 0, lastTick = 0, lastFrame = 0, lastRelay = 0;
uint16_t relayMask = 0;
bool relayEnabled = true, paused = false, relayFault = false;
int manualScene = -1;
// A single two-byte write changes all 16 PCF8575 outputs together.
// P0 is channel 1. HIGH = OFF, LOW = ON, as in the supplied test.
bool writeRelays(uint16_t mask) {
uint16_t raw = uint16_t(~mask);
Wire.beginTransmission(RELAY_ADDRESS);
Wire.write(uint8_t(raw));
Wire.write(uint8_t(raw >> 8));
bool ok = Wire.endTransmission() == 0;
if (ok) relayMask = mask;
return ok;
}
uint16_t channelBit(int i) { return uint16_t(1u << (i & 15)); }
int sceneAt(uint32_t t) {
if (t < 4000) return 0;
if (t < 12000) return 1;
if (t < 20000) return 2;
if (t < 28000) return 3;
return 4;
}
uint32_t sceneStart(int s) {
const uint32_t starts[] = {0, 4000, 12000, 20000, 28000};
return starts[s];
}
uint16_t patternFor(int s, uint32_t t) {
uint32_t step = t / RELAY_STEP_MS;
switch (s) {
case 0: return channelBit(step % 16);
case 1: return channelBit(step % 16);
case 2: {
int p = step % 14;
if (p > 7) p = 14 - p;
return channelBit(p) | channelBit(15 - p);
}
case 3: {
const uint16_t beats[] = {0x1111, 0, 0x2222, 0, 0x4444, 0, 0x8888, 0};
return beats[step % 8];
}
default: return t < 3000 ? channelBit(15 - (step % 16)) : 0;
}
}
void label(int x, int y, const char* text, int size, uint16_t color) {
canvas.setTextSize(size);
canvas.setTextColor(color);
canvas.setCursor(x, y);
canvas.print(text);
}
void center(int y, const char* text, int size, uint16_t color) {
label((320 - int(strlen(text)) * 6 * size) / 2, y, text, size, color);
}
void background(uint32_t t) {
canvas.fillScreen(BG);
for (int x = 0; x < 320; x += 20) canvas.drawFastVLine(x, 30, 158, GRID);
for (int y = 30; y < 188; y += 20) canvas.drawFastHLine(0, y, 320, GRID);
// Deterministic drifting stars, without allocations or random-state changes.
for (int i = 0; i < 36; ++i) {
int x = (i * 73 + t / (15 + i % 7)) % 320;
int y = 34 + (i * 47) % 149;
canvas.drawPixel(x, y, i % 3 ? DIM : CYAN);
}
}
void drawBoot(uint32_t t) {
center(42, "KinCony", 4, CYAN);
center(80, "CO16", 3, WHITE);
center(112, "INITIALIZING THE SHOW", 1, DIM);
canvas.drawRoundRect(36, 132, 248, 18, 4, CYAN);
int progress = int((t % 4000) * 242 / 3999);
canvas.fillRect(39, 135, progress, 12, PINK);
char line[32];
snprintf(line, sizeof(line), "16 CHANNELS / %02lu%%", (unsigned long)((t % 4000) * 100 / 3999));
center(163, line, 1, WHITE);
}
void drawRadar(uint32_t t) {
const int cx = 96, cy = 108;
for (int r = 20; r <= 64; r += 22) canvas.drawCircle(cx, cy, r, 0x0329);
canvas.drawFastHLine(29, cy, 135, 0x0329);
canvas.drawFastVLine(cx, 41, 135, 0x0329);
float angle = (t % 4000) * TAU / 4000.0f - TAU / 4;
for (int j = 12; j >= 0; --j) {
float a = angle - j * 0.035f;
canvas.drawLine(cx, cy, cx + int(cosf(a) * 63), cy + int(sinf(a) * 63),
j == 0 ? CYAN : uint16_t((12 - j + 3) << 5));
}
for (int i = 0; i < 16; ++i) {
float a = i * TAU / 16 - TAU / 4;
int x = cx + int(cosf(a) * 54), y = cy + int(sinf(a) * 54);
bool on = relayMask & channelBit(i);
canvas.fillCircle(x, y, on ? 5 : 2, on ? GOLD : DIM);
if (on) canvas.drawCircle(x, y, 8, CYAN);
}
label(181, 51, "SCAN", 3, CYAN);
label(182, 83, "16 RELAY NODES", 1, WHITE);
char buf[16];
snprintf(buf, sizeof(buf), "CH %02lu", (unsigned long)((t / RELAY_STEP_MS) % 16 + 1));
label(182, 110, buf, 2, GOLD);
label(182, 144, "COMMAND TRACKER", 1, DIM);
label(182, 160, "250ms / STEP", 1, DIM);
}
void drawCore(uint32_t t) {
float phase = t * 0.002f;
for (int i = 0; i < 4; ++i) {
int r = 24 + i * 13;
for (int k = 0; k < 46; ++k) {
float a = phase * (i % 2 ? -1 : 1) + k * TAU / 60;
float b = a + TAU / 65;
canvas.drawLine(160 + int(cosf(a) * r), 109 + int(sinf(a) * r),
160 + int(cosf(b) * r), 109 + int(sinf(b) * r), i % 2 ? PINK : CYAN);
}
}
canvas.fillCircle(160, 109, 18 + int(3 * sinf(phase * 3)), 0x0410);
label(148, 102, "16", 2, WHITE);
for (int i = 0; i < 8; ++i) {
int h = 5 + int((sinf(phase * 2 + i * 0.6f) + 1) * 24);
canvas.fillRect(14 + i * 6, 139 - h, 4, h, CYAN);
canvas.fillRect(260 + i * 6, 139 - h, 4, h, PINK);
}
label(12, 155, "CH 1-8", 1, DIM);
label(255, 155, "CH 9-16", 1, DIM);
center(176, "DUAL-WAVE SYNCHRONIZATION", 1, WHITE);
}
void drawBeat(uint32_t t) {
// Synthetic frequency bars. No microphone, FFT, or audio synchronization.
float phase = t * 0.004f;
for (int i = 0; i < 16; ++i) {
float v = (sinf(phase + i * 0.67f) + sinf(phase * 0.63f - i * 0.41f) + 2) / 4;
int levels = 2 + int(v * 14);
for (int j = 0; j < levels; ++j)
canvas.fillRect(9 + i * 19, 166 - j * 6, 13, 4, j > 11 ? PINK : (j > 6 ? CYAN : 0x0410));
}
center(43, "BEAT MATRIX", 2, WHITE);
center(65, "120 BPM / SYNTHETIC VISUALIZER", 1, DIM);
canvas.fillCircle(299, 45, 4, (t % 500 < 250) ? GOLD : DIM);
}
void drawFinale(uint32_t t) {
for (int i = 0; i < 32; ++i) {
float a = i * TAU / 32;
int r = 20 + (int(t / 12) + i * 17) % 130;
canvas.drawLine(160 + int(cosf(a) * r), 108 + int(sinf(a) * r * 0.50f),
160 + int(cosf(a) * (r + 7)), 108 + int(sinf(a) * (r + 7) * 0.50f), GRID);
}
center(56, "16 CHANNELS", 3, CYAN);
center(94, "ONE BRAIN.", 3, WHITE);
center(136, "KinCony CO16 / ESP32-S3", 1, PINK);
center(165, "www.kincony.com", 1, DIM);
}
void render(int scene, uint32_t t) {
background(t);
switch (scene) {
case 0: drawBoot(t); break;
case 1: drawRadar(t); break;
case 2: drawCore(t); break;
case 3: drawBeat(t); break;
default: drawFinale(t); break;
}
canvas.fillRect(0, 0, 320, 29, BG);
label(8, 9, "CO16", 2, CYAN);
label(80, 12, SCENE_NAMES[scene], 1, WHITE);
label(271, 12, manualScene < 0 ? "AUTO" : "HOLD", 1, PINK);
canvas.drawFastHLine(8, 28, 304, CYAN);
// Two rows of numbered output tiles, all drawn from the last ACKed command.
canvas.fillRect(0, 190, 320, 50, BG);
for (int i = 0; i < 16; ++i) {
int x = 8 + (i % 8) * 39, y = 191 + (i / 8) * 18;
bool on = relayMask & channelBit(i);
canvas.fillRoundRect(x, y, 32, 15, 3, on ? CYAN : GRID);
char number[4];
snprintf(number, sizeof(number), "%02d", i + 1);
label(x + 10, y + 4, number, 1, on ? BG : DIM);
}
const char* status = relayFault ? "I2C ERROR / OUTPUT UNKNOWN" :
paused ? "PAUSED / RELAYS OFF" : !relayEnabled ? "SCREEN ONLY / RELAYS OFF" : "RELAY COMMANDS / VISUAL DEMO";
center(231, status, 1, relayFault ? GOLD : DIM);
if (!display.present(canvas.getBuffer())) {
writeRelays(0);
Serial.println("Display transfer failed. Demo halted; OFF command attempted.");
while (true) { writeRelays(0); delay(1000); }
}
}
void handleSerial() {
while (Serial.available()) {
char c = Serial.read();
if (c == 'p') paused = !paused;
if (c == 'x') relayEnabled = false;
if (c == 'e') relayEnabled = true;
if (c == '0' || c == 'r') { manualScene = -1; showTime = 0; paused = false; }
if (c >= '1' && c <= '5') { manualScene = c - '1'; showTime = 0; paused = false; }
if (c == 'p' || c == 'x' || c == '0' || c == 'r' || (c >= '1' && c <= '5')) {
if (!writeRelays(0)) relayFault = true;
lastRelay = millis();
}
}
}
void setup() {
Serial.begin(115200);
Wire.begin(8, 18);
Wire.setClock(100000);
Wire.setTimeOut(20);
relayFault = !writeRelays(0);
// Deselect other devices sharing the TFT wires BEFORE enabling SPI3.
// Do not initialize SD, LoRa, or software SPI on this bus in this demo.
const int inactiveCs[] = {9, 13, 14, 42};
for (int pin : inactiveCs) { digitalWrite(pin, HIGH); pinMode(pin, OUTPUT); }
if (!canvas.getBuffer() || !display.begin()) {
Serial.println("Display/buffer initialization failed. Demo halted.");
while (true) { writeRelays(0); delay(1000); }
}
canvas.setTextWrap(false);
Serial.println("CO16 CYBER SHOW: 0=auto 1..5=scene p=pause x=relays off e=relays on r=restart");
lastTick = lastRelay = millis();
}
void loop() {
handleSerial();
uint32_t now = millis();
uint32_t dt = now - lastTick;
lastTick = now;
if (!paused) showTime = (showTime + dt) % SHOW_MS;
int scene = manualScene < 0 ? sceneAt(showTime) : manualScene;
uint32_t localTime = manualScene < 0 ? showTime - sceneStart(scene) : showTime;
// Limit real relay updates independently of display rendering.
if (now - lastRelay >= RELAY_STEP_MS) {
lastRelay = now;
uint16_t wanted = (paused || !relayEnabled || relayFault) ? 0 : patternFor(scene, localTime);
if (relayFault || wanted != relayMask) {
if (!writeRelays(wanted)) {
relayFault = true; // Latched until reset. Retry OFF, never resume motion.
Serial.println("Relay I2C error: motion disabled; retrying OFF.");
writeRelays(0);
}
}
}
if (now - lastFrame >= FRAME_MS) {
lastFrame = now;
render(scene, localTime);
}
delay(1);
}[attachment=10732]
BIN file (you can use esp32 download tool download to ESP32-S3 with address 0x0 then directly to use) download:
11-CO16_Cyber_Show.ino.merged.zip (Size: 218.48 KB / Downloads: 2)
YouTube: https://www.youtube.com/c/KinCony
Online Store: https://shop.kincony.com
Alibaba Store: https://kincony.en.alibaba.com/
Online Store: https://shop.kincony.com
Alibaba Store: https://kincony.en.alibaba.com/

