Цвета Ардуино рэп
Создание музыки использовалась нейросеть Suno AI.
Книга Цветы для Элджернона
Подпишись и смотри ролики:
#gamepointer #ЦветаАрдуино #рэп
Здравствуйте, изначально делал часики ради интереса но в итоге они оказались довольны удобные, поэтому остались у меня на рабочем месте.
Из компонентов:
-digispark attiny85
-DS3231
-Датчик освещенности
-Ws2812 ring 12 led




Это имитация стрелочных часов. Для удобства отметки 12, 3, 6 и 9 часов подсвечены тусклым желтым светом. Часовая стрелка реализована красным светодиодом, минутная - синим, а секундная - зеленым. Минуты и секунды отображаются с шагом в пять делений.
Яркость регулируется автоматически: днем есть хороший запас по яркости, а ночью при минимальной яркости часы не мешают и не слепят.
Также был добавлен микропереключатель, который при удержании отключает датчик освещенности от пина ATTiny85 для прошивки. Теоретически можно было обойтись и без него, но в процессе эксплуатации обнаружились баги, и чтобы не отпаивать провод при каждой перепрошивке, был установлен микрик.
Отдельных кнопок настройки нет - время задается непосредственно при прошивке. За год часы ни разу не сбились, поэтому усложнять конструкцию дополнительными элементами управления не было смысла.
Корпус - вещь сугубо индивидуальная. Не придумав ничего лучше, я собрал всю конструкцию навесным монтажом на толстом медном проводе, из которого также сделал ножки. Питание через Type-C.



Ускоренное видео работы.
За прошедшее время я переделал трассировку платы под новую конфигурацию с той крестовиной и потенциометром.
Останется получить нужные новые компоненты и сделать плату, а затем и 3D-модель корпуса. Кстати, пришлось жертвовать светодиодом, остался 1. Зато теперь добавилось 4 кнопки и энкодер.
Решил также переписать код: появилась идея добавить MIDI-команды как отдельный профиль для использования контроллера в программах, которые такие команды поддерживают.
Добавил ещё окна настроек, но осталось ещё много... Сейчас не очень удобно делать настройки биндов, так как не имею на руках сейчас ту модель, которую я буду настраивать, и дебажить не получится.
Думаю добавить автообновления до релизной версии, но пока нет хоть какой-то контрольной точки в коде для релиза, сделаю чуть позже.
🎛️Следите за проектом в Telegram (ссылка в профиле).
Пишите свои мнения и идеи!
Для здорового образа жизни надо играть в арму 3 (для тех кто не в курсе - типичная сессия это 2 часа бега, 5 минут в засаде, 30 секунд боя и ты убит) через это (сам облизываюсь, но пока дороговато было):
https://rutube.ru/video/67a38e583c95ed3801261d668771718d/
Приобрел для себя степпер, новомодная штука для ходьбы на месте, кому лень выползать на улицу. Сразу скажу, про похудение, правильное питание - это не ко мне. Я просто хочу создать весёлый процесс. А еще чтоб его мог сделать каждый, простота наше всё.
Вот такой аппарат б/у у меня, даже со сломанной ручкой:
Внутри уже есть свой шагомер, по сути можно уже подключиться к нему и отслеживать шаги на компьютере. Но тут возникают сложности. Мы получаем сигнал только тогда, когда уже совершили шаг. А мне нужно отслеживать именно движение тренажера. Как только мы остановились и сигнал должен перестать идти.
Сначала выбор пал на потенциометр с вращением.
Но как грамотно и легко его прикрутить?
Налепил для теста вот так:
Ось со стяжкой крутится, передавая вращение на потенциометр. Тьфу, ерунда какая-то получилась. Какой же тип соединения использовать? Шестерёнки? Это их отдельно на 3д принтере напечатай. Накинуть ремень? Можно, но проблематично.
И тут я увидел ползунковый потенциометр!
А тут и поршень как раз рядышком.
Ну а соединить всё это проще простого! Взял ненужную антенну от роутера и получилась вот такая страшная штука на стяжках.
Поршень стягивается и выпрямляется, а антенна двигает потенциометр. В принципе вместо антенны можно просто взять деревянную реечку. Обязательно делаем прокладку из чего-нибудь, а то поршень нагревается сильно. В дальнейшем уж придумаю занятную коробочку или железные хомуты, но для теста подойдет и так.
Теперь как передать сигнал на компьютер? При изменении данных потенциометра пусть зажимается кнопка W. Выбор пал на Pro Micro ATMEGA32U4.
Отлично эмулирует клавиатуру, да и стоит копейки. Ну и шнур на метра три прикупим. Жаль только у этой модели Micro USB. Теперь всё спаять и пишем код. Тут я не мастер и нейросеть помогла всё собрать. В комментариях оставлю схему(попробую нарисовать) и ссылки на товары(вышло в районе 800р за всё)
К сожалению записать процесс теста сложно, но могу показать как реагирует нажатие степпера в игре:
Гонки просто безумно круто играть!
Как в детстве, хочется нажать сильнее кнопку, чтоб машина ехала ещё быстрее. Тут точно так же!
Начинаешь активно шагать. Забыл сказать, что для руля используем геймпад. Клавиатуру стоя не подержишь)
Пойду тестировать остальные игры)
Я надеюсь получилось полезно и просто. Если есть идеи для улучшения, обязательно пишите!
Для лучшей активности закажу xgun и попробую побегать и пострелять.
Обязательно запишу вам инструкцию и расскажу вам об этом странном геймпаде. Спасибо за внимание! Все делал в первый раз, строго не судите)
Хочу научиться делать аниматроников уровня Диснейленда, что посоветуете?
В какую степь инженерии мне идти?
Продолжение поста про Автоматический полив растений своими руками
Управление максимально простое первая строка статична, вторая строка состоит и 3-ёх экранов, в которых настройка лампы, настройка 1-ого насоса (P1), настройка 2-ого насоса (P2).
Для фитолампы настраиваются время включения и выключения
Для насосов настраиваются следующие параметры:
100s - сколько секунд качает насос воду
3d(2) - период поливания 3 дня (осталось 2 дня до следующего полива, если 0 то значит сегодня)
20h - полив будет включаться в 20:00
Есть в запасе еще 3 реле, не сложно будет добавить еще экран для настройки параметров.
Схема в формате ПДФ в норм качестве - https://disk.yandex.ru/i/qEtdoScnoX2zMQ
Сам скетч такой:
#include <Wire.h>
#include "GyverEncoder.h"
#include <microDS3231.h>
#include <GyverHTU21D.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// ПИНЫ
#define rele1 2 // Лампа
#define rele2 3 // Насос 1
#define rele3 4 // Насос 2 (добавь провод на этот пин)
#define CLK 7
#define DT 6
#define SW 5
Encoder enc1(CLK, DT, SW);
MicroDS3231 rtc;
GyverHTU21D htu;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ПЕРЕМЕННЫЕ НАСТРОЕК
int16_t a1, a2; // Лампа: a1 - старт, a2 - стоп
int16_t b1, b2, b3; // Насос 1: b1 - сек, b2 - дни, b3 - час
int16_t c1, c2, c3; // Насос 2: c1 - сек, c2 - дни, c3 - час
// СИСТЕМНЫЕ ПЕРЕМЕННЫЕ
uint8_t w = 0; // Состояние меню (0-просмотр, 1..8 - настройка)
uint8_t mode = 0; // Экран (0-LAMP, 1-P1, 2-P2)
uint32_t startDay1, startDay2; // Дни отсчета для полива
unsigned long pumpTimer1, pumpTimer2;
bool isWatering1 = false, isWatering2 = false;
bool wateredToday1 = false, wateredToday2 = false;
void setup() {
//rtc.setTime(COMPILE_TIME);
Serial.begin(9600);
htu.begin();
enc1.setType(TYPE2);
Wire.setWireTimeout(3000, true);
pinMode(rele1, OUTPUT);
pinMode(rele2, OUTPUT);
pinMode(rele3, OUTPUT);
digitalWrite(rele1, 1);
digitalWrite(rele2, 1);
digitalWrite(rele3, 1);
lcd.init();
lcd.backlight();
// ЗАГРУЗКА ИЗ EEPROM
EEPROM.get(0, a1); EEPROM.get(2, a2);
EEPROM.get(4, b1); EEPROM.get(6, b2); EEPROM.get(8, b3); EEPROM.get(10, startDay1);
EEPROM.get(14, c1); EEPROM.get(16, c2); EEPROM.get(18, c3); EEPROM.get(20, startDay2);
}
void loop() {
enc1.tick();
static uint32_t sensT;
if (millis() - sensT >= 2000) { sensT = millis(); htu.readTick(); }
handleEncoder();
static uint32_t dispT;
if (millis() - dispT >= 300) { dispT = millis(); updateDisplay(); }
controlHardware();
}
void handleEncoder() {
if (w == 0) {
// Порядок: 0 (LAMP) -> 1 (P1) -> 2 (P2)
if (enc1.isLeft()) { mode++; if (mode > 2) mode = 0; lcd.clear(); }
if (enc1.isRight()) { if (mode == 0) mode = 2; else mode--; lcd.clear(); }
if (enc1.isClick()) {
if (mode == 0) w = 1; // Настройка Лампы
else if (mode == 1) w = 3; // Настройка P1
else if (mode == 2) w = 6; // Настройка P2
}
}
else if (w == 1) {
if (enc1.isRight()) a1--; if (enc1.isLeft()) a1++;
if (a1 > 23) a1 = 0; if (a1 < 0) a1 = 23;
if (enc1.isClick()) { EEPROM.put(0, a1); w = 2; }
}
else if (w == 2) {
if (enc1.isRight()) a2--; if (enc1.isLeft()) a2++;
if (a2 > 23) a2 = 0; if (a2 < 0) a2 = 23;
if (enc1.isClick()) { EEPROM.put(2, a2); w = 0; lcd.clear(); }
}
else if (w == 3) {
if (enc1.isRight()) b1--; if (enc1.isLeft()) b1++;
if (b1 > 300) b1 = 1; if (b1 < 1) b1 = 300;
if (enc1.isClick()) { EEPROM.put(4, b1); w = 4; }
}
else if (w == 4) {
if (enc1.isRight()) b2--; if (enc1.isLeft()) b2++;
if (b2 > 9) b2 = 1; if (b2 < 1) b2 = 9;
if (enc1.isClick()) { EEPROM.put(6, b2); w = 5; }
}
else if (w == 5) {
if (enc1.isRight()) b3--; if (enc1.isLeft()) b3++;
if (b3 > 23) b3 = 0; if (b3 < 0) b3 = 23;
if (enc1.isClick()) {
startDay1 = rtc.getUnix(3) / 86400L;
EEPROM.put(10, startDay1); EEPROM.put(8, b3); w = 0; lcd.clear();
}
}
else if (w == 6) {
if (enc1.isRight()) c1--; if (enc1.isLeft()) c1++;
if (c1 > 300) c1 = 1; if (c1 < 1) c1 = 300;
if (enc1.isClick()) { EEPROM.put(14, c1); w = 7; }
}
else if (w == 7) {
if (enc1.isRight()) c2--; if (enc1.isLeft()) c2++;
if (c2 > 9) c2 = 1; if (c2 < 1) c2 = 9;
if (enc1.isClick()) { EEPROM.put(16, c2); w = 8; }
}
else if (w == 8) {
if (enc1.isRight()) c3--; if (enc1.isLeft()) c3++;
if (c3 > 23) c3 = 0; if (c3 < 0) c3 = 23;
if (enc1.isClick()) {
startDay2 = rtc.getUnix(3) / 86400L;
EEPROM.put(20, startDay2); EEPROM.put(18, c3); w = 0; lcd.clear();
}
}
}
void updateDisplay() {
int8_t hrs = rtc.getHours();
int8_t mins = rtc.getMinutes();
bool blink = (millis() / 500) % 2;
uint32_t currentDay = rtc.getUnix(3) / 86400L;
// --- ВЕРХНЯЯ СТРОКА ---
lcd.setCursor(0, 0);
if (hrs < 10) lcd.print('0'); lcd.print(hrs);
lcd.print(blink ? " " : ":");
if (mins < 10) lcd.print('0'); lcd.print(mins);
lcd.setCursor(6, 0); lcd.print("T:"); lcd.print((int)htu.getTemperature()); lcd.write(223);
lcd.setCursor(11, 0); lcd.print(" ");
lcd.setCursor(12, 0); lcd.print("H:"); lcd.print((int)htu.getHumidity());
// --- НИЖНЯЯ СТРОКА ---
// 1. РЕЖИМ ПРОСМОТРА (Главные экраны)
if (w == 0) {
lcd.setCursor(0, 1);
if (mode == 0) { // Экран LAMP
lcd.print(F("LAMP "));
if (a1 < 10) lcd.print('0'); lcd.print(a1); lcd.print(F(":00-"));
if (a2 < 10) lcd.print('0'); lcd.print(a2); lcd.print(F(":00 "));
}
else { // Экраны P1 или P2
int s = (mode == 1) ? b1 : c1;
int d = (mode == 1) ? b2 : c2;
int h = (mode == 1) ? b3 : c3;
uint32_t sd = (mode == 1) ? startDay1 : startDay2;
int daysToWait = ((currentDay - sd) % d);
lcd.print(mode == 1 ? F("P1") : F("P2"));
lcd.setCursor(2, 1); if (s < 100) lcd.print(' '); if (s < 10) lcd.print(' '); lcd.print(s); lcd.print('s');
lcd.setCursor(7, 1); lcd.print(d); lcd.print('d');
lcd.print('('); lcd.print(daysToWait); lcd.print(')');
lcd.setCursor(13, 1); if (h < 10) lcd.print('0'); lcd.print(h); lcd.print('h');
}
}
// 2. НАСТРОЙКА ЛАМПЫ (Вернули этот блок)
else if (w == 1 || w == 2) {
lcd.setCursor(0, 1);
lcd.print(F("LAMP "));
if (w == 1 && blink) lcd.print(F(" "));
else { if (a1 < 10) lcd.print('0'); lcd.print(a1); }
lcd.print(F(":00-"));
if (w == 2 && blink) lcd.print(F(" "));
else { if (a2 < 10) lcd.print('0'); lcd.print(a2); }
lcd.print(F(":00 "));
}
// 3. НАСТРОЙКА НАСОСОВ (P1: w=3-5, P2: w=6-8)
else if (w >= 3) {
bool isP1 = (w <= 5);
int s = isP1 ? b1 : c1;
int d = isP1 ? b2 : c2;
int h = isP1 ? b3 : c3;
lcd.setCursor(0, 1);
lcd.print(isP1 ? F("P1") : F("P2"));
// Сек (Позиция 2-4)
lcd.setCursor(2, 1);
if ((w == 3 || w == 6) && blink) lcd.print(F(" "));
else {
if (s < 100) lcd.print(' ');
if (s < 10) lcd.print(' ');
lcd.print(s);
}
lcd.print('s'); // Позиция 5
lcd.setCursor(7, 1);
if ((w == 4 || w == 7) && blink) {
lcd.print(F(" ")); // Один пробел для мигания одной цифры
} else {
lcd.print(d);
}
lcd.print(F("d ")); // Позиция 8
// Час запуска (Позиция 10)
lcd.setCursor(10, 1);
if ((w == 5 || w == 8) && blink) {
lcd.print(F(" "));
} else {
if (h < 10) lcd.print('0');
lcd.print(h);
}
lcd.print(F("h ")); // Очистка конца строки
}
}
void controlHardware() {
int8_t hrs = rtc.getHours();
uint32_t currentDay = rtc.getUnix(3) / 86400L;
// ЛАМПА
if (a1 < a2) digitalWrite(rele1, (hrs >= a1 && hrs < a2) ? 0 : 1);
else digitalWrite(rele1, (hrs >= a1 || hrs < a2) ? 0 : 1);
// ПОЛИВ P1
if (hrs == b3 && ((currentDay - startDay1) % b2 == 0) && !wateredToday1) {
digitalWrite(rele2, 0); pumpTimer1 = millis(); isWatering1 = true; wateredToday1 = true;
}
if (hrs != b3) wateredToday1 = false;
if (isWatering1 && (millis() - pumpTimer1 >= (unsigned long)b1 * 1000)) {
digitalWrite(rele2, 1); isWatering1 = false;
}
// ПОЛИВ P2
if (hrs == c3 && ((currentDay - startDay2) % c2 == 0) && !wateredToday2) {
digitalWrite(rele3, 0); pumpTimer2 = millis(); isWatering2 = true; wateredToday2 = true;
}
if (hrs != c3) wateredToday2 = false;
if (isWatering2 && (millis() - pumpTimer2 >= (unsigned long)c1 * 1000)) {
digitalWrite(rele3, 1); isWatering2 = false;
}
}
Всем спасибо за внимание:)
p.s. обновил схему питание насосов было от 5 В, а они на 12В
Я разрабатываю собственный контроллер — устройство, которое упрощает управление звуком на ПК: системной громкостью, отдельными приложениями и специализированными программами вроде OBS.
Проект полностью самостоятельный: я сам проектирую плату, моделирую корпус, пишу софт(python) и прошивку(C++ arduino) — то есть полностью "варюсь" в этом процессе.
А главное — я открыт к вашему мнению. Если будут идеи, как улучшить или дополнить функционал, буду рад услышать и постараюсь внедрить.
Telegram-канал: https://t.me/chumarno
Wombat: @FRIMID
Здесь
Какие идеи у вас есть?
Пишите в комментариях — я всё прочитаю! 👇