r/arduino

🔥 Hot ▲ 150 r/arduino+2 crossposts

Built an animatronic crow that syncs head movement to any BPM

Hey everybody! So i spent the last 2 months building this animatronic crow. I got inspired by a video where Adam Savage took an animatronic apart using a CT scan, so i figured I'd rebuild one for myself using 3d printing and servos.

What i really liked so far: for the keys on the controller I used Cherry Brown mechanical switches, they feel great to trigger movements with. And instead of normal potentiometers i used digital ones (the kind that can keep spinning forever), so you don't hit a hard limit when controlling things like the beak.

The musical part came to be as I'm also a DJ on the side, I wanted to bring it with me when I play. So I built this controller unit that wirelessly sends commands to the actual crow. Esing two esp32s for that. Sorry, didn't make the best experiences with wireless arduinos, so went with esp32 instead.

Right now he moves manually, syncs his head to any BPM i type in, and the mouth opens and closes. Lip Sync is the next topic I will look into, but that seems quite hard!

u/monkeydance26 — 12 hours ago
▲ 7 r/arduino+3 crossposts

Introduction To Binary Protocols In Robotics

Hi fellow robots, as I work on my projects I discover cool new ways to do things and I thought I'd share something I learned with you guys.

Typically in Arduino projects where you need to read and write to connected devices such as sensors and motors, you'd use serial communication. If you wanted to use Python to talk to the Arduino (to control motors or receive feedback), you'd need a way to bridge the language gap between C++ and Python. Most beginner tutorials would teach you to just send strings of characters back and forth that have to be parsed.

But that's a very rigid and cumbersome way of passing information. If the number of decimal places changes, your message could now be a different length. Each character is one byte, so your message could end up being massive if you have large numbers.

This is where it makes sense to use a binary protocol, where you send a fixed "frame" of data represented as bytes and all devices abide by the protocol. The idea is to define the structure of your message and send data as binary representations.

  • The message "type" can be represented by a single byte (eg. 0x01).
  • If the data or payload is a floating point number, it can be represented by 4 bytes regardless of how big it is (up to a limit).

Now you can always send and received fixed message structures and lengths, known as "packets". This is much more elegant because you always know where to expect each piece of information and how big they are, so you don't need to deal with parsing large strings of characters that vary in length. The difference is especially noticeable once you start sending multiple pieces of information in one packet (eg. speed, position, temperature, voltage, current).

I didn't want to make this post too long so this is just a basic overview. If you're interested in more detail with examples to improve your inter-device communication, check out my article.

u/NameruseTaken — 2 hours ago
▲ 13 r/arduino

Am i cooked?

I dont really know hoe yo solder little pins, only big things. All i wanna know is dis i break something on my arduino, it isnt a big deal i just want to know

u/Hot-Invite8374 — 10 hours ago

Not enough cables

Im working on a school project and encountered the problem of not having enough of the jumper wires. My question is, if it is possible to use less cables? For example, i want to be able to press a button and for it to print the letter a. For this i need 2 cables. Would it be possble for example to somehow connect the cables in a way that i could have two buttons that print a and b independently but using less than 4 cables?

Any help would be appreciated

reddit.com
u/RandomEmKayFan — 12 hours ago
▲ 18 r/arduino

Need help for a game

Hello I’m making an arduino memory game with 4 LEDs and 4 push buttons. Game should start by flickering one of the leds and player should mimic it with buttons. Sequence is generated randomly. If the player fails, all leds will flicker 3 times. I’m kinda new to all of this stuff, and I don’t code(sorry if I don’t understand something). I used Claude for the code.

One of the LEDs flickers randomly when I upload the code. What could be the problem? I don’t think buttons work too.

u/snikcres — 10 hours ago
▲ 46 r/arduino+1 crossposts

Desktop Tamagotchi Clock

Board: Esp32-S3 Waveshare 1.45” display with glass cover from Amazon.

The video explains it’s all. This was a fun project.

If you want my .stl for the case to make your own thing: https://www.printables.com/model/1695589-goober-esp32-146-waveshare-case

If you want my code to create a tamagotchi clock:

https://github.com/FregoMyEggo/Goober-deskbot

I’m hoping this project will catch on and other people will expand on it with their own animation sets and functionality. It’s a pain to create all of the image files, but if anyone makes more make a GitHub fork and expand the project!

u/Independent-Trash966 — 12 hours ago

How do I supply power to this?

Hi I plan to replicate this exact diagram but it is missing the power source from the illustration.

I have the Uno R3 which I understand requires 7-12v power supply.

If I just buy a 7v DC power supply that goes into my wall and plug in the barrel connector into the area circled green, is it as simple as that to make sure this works correctly? The motor I am using is 5v so it would be enough power if I use 7v power supply.

https://imgur.com/a/eQnflyt

u/JimothyLeFleur69420 — 8 hours ago
▲ 0 r/arduino+2 crossposts

Hey, I'm building a virtual electronics lab in Unity to stop burning real boards. Could you help a fellow dev out with a 1-min survey?

forms.gle
u/Yazilim_Adam — 5 hours ago

Help Debugging MiniTV project

I was hoping I might be able to get help debugging my Mini-TV-ESP32 project.

I copied the Mini-TV-esp32 project on YouTube & GitHub, but I can't seem to get it working, and I was hoping someone might be able to help me out. I'm still very green on Arduinos and programming, but I thought I'd give this a try for a project.

I copied the files for the program into Arduino 2.3.8 and followed the video on YouTube. I've been able to test the program and compile and upload to my ESP32, but all that happens is I get a quick chirp from the speaker, and that's it. No activity on the screen or anything.

For hardware, I'm using the following parts, all sourced on Amazon

3.5" SPI TFT Display Module

Max98357 I2S Amplifier board

ESP32 30P Type-c

Micro SD SDHC TF

2W 8 ohm Speaker

Here are some pictures of my test assembly

ESP32

Overall

Display

ESP32 Left Side

Amplifier Board

This is the main sketch for my file, it's called sketch_starfighter.ino. I followed the instructions in the youtube video to use ffmpeg to convert my video file into an mjpeg and aac file. I did make some tweaks b/c my video is 480x320 instead of the creators 288 x 240. I am also using the creators esp32_audio_task.h and mjpeg_decode_draw_task.h files, which I have also copied below

sketch_starfighter.ino

/***
 * Required libraries:
 * https://github.com/moononournation/Arduino_GFX.git
 * https://github.com/pschatzmann/arduino-libhelix.git
 * https://github.com/bitbank2/JPEGDEC.git
 */

//  Audio and video code

//  ffmpeg -i  LSF_Reel.mp4 -ar 44100 -ac 1 -ab 24k -filter:a loudnorm -filter:a "volume=-5dB" LSF_Reel.aac
// ffmpeg -i LSF_Reel.mp4 -vf "fps=25,scale=-1:240:flags=lanczos,crop=320:in_h:(in_w-480)/2:0" -q:v 11 LSF_Reel.mjpeg
// auto fall back to MP3 if AAC file not available
#define AAC_FILENAME "/LSF_Reel.aac"
#define MP3_FILENAME "/LSF_Reel.mp3"
// #define MP3_FILENAME "/EP05.mp3"
#define MJPEG_FILENAME "/LSF_Reel.mjpeg"
// #define MJPEG_FILENAME "/EP05.mjpeg"
// #define MJPEG_FILENAME "/320_30fps.mjpeg"
#define FPS 25
#define MJPEG_BUFFER_SIZE (480 * 320 * 2 / 8)
// #define MJPEG_BUFFER_SIZE (320 * 240 * 2 / 8)
#define AUDIOASSIGNCORE 1
#define DECODEASSIGNCORE 0
#define DRAWASSIGNCORE 1

#include <WiFi.h>
#include <FS.h>
#include <LittleFS.h>
#include <SPIFFS.h>
#include <FFat.h>
#include <SD.h>
#include <SD_MMC.h>

/* Arduino_GFX */
#include <Arduino_GFX_Library.h>
#define GFX_BL DF_GFX_BL // default backlight pin, you may replace DF_GFX_BL to actual backlight pin
Arduino_DataBus *bus = create_default_Arduino_DataBus();
// Arduino_GFX *gfx = new Arduino_ILI9341(bus, DF_GFX_RST, 3 /* rotation */, false /* IPS */);
Arduino_GFX *gfx = new Arduino_ST7796(bus, DF_GFX_RST, 1 /* rotation */, true /* IPS */, 240 /* width */, 288 /* height */, 0 /* col offset 1 */, 20 /* row offset 1 */, 0 /* col offset 2 */, 12 /* row offset 2 */);

/* variables */
static int next_frame = 0;
static int skipped_frames = 0;
static unsigned long start_ms, curr_ms, next_frame_ms;

/* audio */
#include "esp32_audio_task.h"

/* MJPEG Video */
#include "mjpeg_decode_draw_task.h"

// pixel drawing callback
static int drawMCU(JPEGDRAW *pDraw)
{
  // Serial.printf("Draw pos = (%d, %d), size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
  unsigned long s = millis();
  gfx->draw16bitRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
  total_show_video_ms += millis() - s;
  return 1;
} /* drawMCU() */

void setup()
{
  disableCore0WDT();

  WiFi.mode(WIFI_OFF);
  Serial.begin(115200);
  // while (!Serial);

  // Init Display
  gfx->begin(80000000);
  gfx->fillScreen(0x0000);

#ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
#endif

  Serial.println("Init I2S");
  gfx->println("Init I2S");
#if defined(ESP32) && (CONFIG_IDF_TARGET_ESP32)
  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, -1 /* MCLK */, 25 /* SCLK */, 26 /* LRCK */, 32 /* DOUT */, -1 /* DIN */);
#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S2)
  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, -1 /* MCLK */, 4 /* SCLK */, 5 /* LRCK */, 18 /* DOUT */, -1 /* DIN */);
#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32S3)
  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, 42 /* MCLK */, 46 /* SCLK */, 45 /* LRCK */, 43 /* DOUT */, 44 /* DIN */);
#elif defined(ESP32) && (CONFIG_IDF_TARGET_ESP32C3)
  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, -1 /* MCLK */, 10 /* SCLK */, 19 /* LRCK */, 18 /* DOUT */, -1 /* DIN */);
#endif
  if (ret_val != ESP_OK)
  {
Serial.printf("i2s_init failed: %d\n", ret_val);
  }
  i2s_zero_dma_buffer(I2S_NUM_0);

  Serial.println("Init FS");
  gfx->println("Init FS");
  // if (!LittleFS.begin(false, "/root"))
  // if (!SPIFFS.begin(false, "/root"))
  // if (!FFat.begin(false, "/root"))
  SPIClass spi = SPIClass(HSPI);
  // spi.begin(14 /* SCK */, 2 /* MISO */, 15 /* MOSI */, 13 /* CS */);
  spi.begin(14 /* SCK */, 4 /* MISO */, 15 /* MOSI */, 13 /* CS */);
  if (!SD.begin(13, spi, 80000000))
  // if ((!SD_MMC.begin("/root")) && (!SD_MMC.begin("/root")) && (!SD_MMC.begin("/root")) && (!SD_MMC.begin("/root"))) /* 4-bit SD bus mode */
  // if ((!SD_MMC.begin("/root", true)) && (!SD_MMC.begin("/root", true)) && (!SD_MMC.begin("/root", true)) && (!SD_MMC.begin("/root", true))) /* 1-bit SD bus mode */
  {
Serial.println("ERROR: File system mount failed!");
gfx->println("ERROR: File system mount failed!");
  }
  else
  {
bool aac_file_available = false;
Serial.println("Open AAC file: " AAC_FILENAME);
gfx->println("Open AAC file: " AAC_FILENAME);
// File aFile = LittleFS.open(AAC_FILENAME);
// File aFile = SPIFFS.open(AAC_FILENAME);
// File aFile = FFat.open(AAC_FILENAME);
File aFile = SD.open(AAC_FILENAME);
// File aFile = SD_MMC.open(AAC_FILENAME);
if (aFile)
{
aac_file_available = true;
}
else
{
Serial.println("Open MP3 file: " MP3_FILENAME);
gfx->println("Open MP3 file: " MP3_FILENAME);
// aFile = LittleFS.open(MP3_FILENAME);
// aFile = SPIFFS.open(MP3_FILENAME);
// aFile = FFat.open(MP3_FILENAME);
aFile = SD.open(MP3_FILENAME);
// aFile = SD_MMC.open(MP3_FILENAME);
}

if (!aFile || aFile.isDirectory())
{
Serial.println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
gfx->println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
}
else
{
Serial.println("Open MJPEG file: " MJPEG_FILENAME);
gfx->println("Open MJPEG file: " MJPEG_FILENAME);
// File vFile = LittleFS.open(MJPEG_FILENAME);
// File vFile = SPIFFS.open(MJPEG_FILENAME);
// File vFile = FFat.open(MJPEG_FILENAME);
File vFile = SD.open(MJPEG_FILENAME);
// File vFile = SD_MMC.open(MJPEG_FILENAME);
if (!vFile || vFile.isDirectory())
{
Serial.println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
gfx->println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
}
else
{
Serial.println("Init video");
gfx->println("Init video");
mjpeg_setup(&vFile, MJPEG_BUFFER_SIZE, drawMCU,
false /* useBigEndian */, DECODEASSIGNCORE, DRAWASSIGNCORE);

Serial.println("Start play audio task");
gfx->println("Start play audio task");
BaseType_t ret_val;
if (aac_file_available)
{
ret_val = aac_player_task_start(&aFile, AUDIOASSIGNCORE);
}
else
{
ret_val = mp3_player_task_start(&aFile, AUDIOASSIGNCORE);
}
if (ret_val != pdPASS)
{
Serial.printf("Audio player task start failed: %d\n", ret_val);
gfx->printf("Audio player task start failed: %d\n", ret_val);
}

Serial.println("Start play video");
gfx->println("Start play video");
start_ms = millis();
curr_ms = millis();
next_frame_ms = start_ms + (++next_frame * 1000 / FPS / 2);
while (vFile.available() && mjpeg_read_frame()) // Read video
{
total_read_video_ms += millis() - curr_ms;
curr_ms = millis();

if (millis() < next_frame_ms) // check show frame or skip frame
{
// Play video
mjpeg_draw_frame();
total_decode_video_ms += millis() - curr_ms;
curr_ms = millis();
}
else
{
++skipped_frames;
Serial.println("Skip frame");
}

while (millis() < next_frame_ms)
{
vTaskDelay(pdMS_TO_TICKS(1));
}

curr_ms = millis();
next_frame_ms = start_ms + (++next_frame * 1000 / FPS);
}
int time_used = millis() - start_ms;
int total_frames = next_frame - 1;
Serial.println("AV end");
vFile.close();
aFile.close();

int played_frames = total_frames - skipped_frames;
float fps = 1000.0 * played_frames / time_used;
total_decode_audio_ms -= total_play_audio_ms;
// total_decode_video_ms -= total_show_video_ms;
Serial.printf("Played frames: %d\n", played_frames);
Serial.printf("Skipped frames: %d (%0.1f %%)\n", skipped_frames, 100.0 * skipped_frames / total_frames);
Serial.printf("Time used: %d ms\n", time_used);
Serial.printf("Expected FPS: %d\n", FPS);
Serial.printf("Actual FPS: %0.1f\n", fps);
Serial.printf("Read audio: %lu ms (%0.1f %%)\n", total_read_audio_ms, 100.0 * total_read_audio_ms / time_used);
Serial.printf("Decode audio: %lu ms (%0.1f %%)\n", total_decode_audio_ms, 100.0 * total_decode_audio_ms / time_used);
Serial.printf("Play audio: %lu ms (%0.1f %%)\n", total_play_audio_ms, 100.0 * total_play_audio_ms / time_used);
Serial.printf("Read video: %lu ms (%0.1f %%)\n", total_read_video_ms, 100.0 * total_read_video_ms / time_used);
Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video_ms, 100.0 * total_decode_video_ms / time_used);
Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video_ms, 100.0 * total_show_video_ms / time_used);

#define CHART_MARGIN 64
#define LEGEND_A_COLOR 0x1BB6
#define LEGEND_B_COLOR 0xFBE1
#define LEGEND_C_COLOR 0x2D05
#define LEGEND_D_COLOR 0xD125
#define LEGEND_E_COLOR 0x9337
#define LEGEND_F_COLOR 0x8AA9
#define LEGEND_G_COLOR 0xE3B8
#define LEGEND_H_COLOR 0x7BEF
#define LEGEND_I_COLOR 0xBDE4
#define LEGEND_J_COLOR 0x15F9
// gfx->setCursor(0, 0);
gfx->setTextColor(0xFFFF);
gfx->printf("Played frames: %d\n", played_frames);
gfx->printf("Skipped frames: %d (%0.1f %%)\n", skipped_frames, 100.0 * skipped_frames / total_frames);
gfx->printf("Time used: %d ms\n", time_used);
gfx->printf("Expected FPS: %d\n", FPS);
gfx->printf("Actual FPS: %0.1f\n\n", fps);

int16_t r1 = ((gfx->height() - CHART_MARGIN - CHART_MARGIN) / 2);
int16_t r2 = r1 / 2;
int16_t cx = gfx->width() - r1 - 10;
int16_t cy = r1 + CHART_MARGIN;

float arc_start1 = 0;
float arc_end1 = arc_start1 + max(2.0, 360.0 * total_read_audio_ms / time_used);
for (int i = arc_start1 + 1; i < arc_end1; i += 2)
{
gfx->fillArc(cx, cy, r1, r2, arc_start1 - 90.0, i - 90.0, LEGEND_A_COLOR);
}
gfx->fillArc(cx, cy, r1, r2, arc_start1 - 90.0, arc_end1 - 90.0, LEGEND_A_COLOR);
gfx->setTextColor(LEGEND_A_COLOR);
gfx->printf("Read audio: %lu ms (%0.1f %%)\n", total_read_audio_ms, 100.0 * total_read_audio_ms / time_used);

float arc_start2 = arc_end1;
float arc_end2 = arc_start2 + max(2.0, 360.0 * total_decode_audio_ms / time_used);
for (int i = arc_start2 + 1; i < arc_end2; i += 2)
{
gfx->fillArc(cx, cy, r1, r2, arc_start2 - 90.0, i - 90.0, LEGEND_B_COLOR);
}
gfx->fillArc(cx, cy, r1, r2, arc_start2 - 90.0, arc_end2 - 90.0, LEGEND_B_COLOR);
gfx->setTextColor(LEGEND_B_COLOR);
gfx->printf("Decode audio: %lu ms (%0.1f %%)\n", total_decode_audio_ms, 100.0 * total_decode_audio_ms / time_used);
gfx->setTextColor(LEGEND_J_COLOR);
gfx->printf("Play audio: %lu ms (%0.1f %%)\n", total_play_audio_ms, 100.0 * total_play_audio_ms / time_used);

float arc_start3 = arc_end2;
float arc_end3 = arc_start3 + max(2.0, 360.0 * total_read_video_ms / time_used);
for (int i = arc_start3 + 1; i < arc_end3; i += 2)
{
gfx->fillArc(cx, cy, r1, r2, arc_start3 - 90.0, i - 90.0, LEGEND_C_COLOR);
}
gfx->fillArc(cx, cy, r1, r2, arc_start3 - 90.0, arc_end3 - 90.0, LEGEND_C_COLOR);
gfx->setTextColor(LEGEND_C_COLOR);
gfx->printf("Read video: %lu ms (%0.1f %%)\n", total_read_video_ms, 100.0 * total_read_video_ms / time_used);

float arc_start4 = arc_end3;
float arc_end4 = arc_start4 + max(2.0, 360.0 * total_show_video_ms / time_used);
for (int i = arc_start4 + 1; i < arc_end4; i += 2)
{
gfx->fillArc(cx, cy, r1, r2, arc_start4 - 90.0, i - 90.0, LEGEND_D_COLOR);
}
gfx->fillArc(cx, cy, r1, r2, arc_start4 - 90.0, arc_end4 - 90.0, LEGEND_D_COLOR);
gfx->setTextColor(LEGEND_D_COLOR);
gfx->printf("Show video: %lu ms (%0.1f %%)\n", total_show_video_ms, 100.0 * total_show_video_ms / time_used);

float arc_start5 = 0;
float arc_end5 = arc_start5 + max(2.0, 360.0 * total_decode_video_ms / time_used);
for (int i = arc_start5 + 1; i < arc_end5; i += 2)
{
gfx->fillArc(cx, cy, r2, 0, arc_start5 - 90.0, i - 90.0, LEGEND_E_COLOR);
}
gfx->fillArc(cx, cy, r2, 0, arc_start5 - 90.0, arc_end5 - 90.0, LEGEND_E_COLOR);
gfx->setTextColor(LEGEND_E_COLOR);
gfx->printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video_ms, 100.0 * total_decode_video_ms / time_used);
}
// delay(60000);
#ifdef GFX_BL
// digitalWrite(GFX_BL, LOW);
#endif
// gfx->displayOff();
// esp_deep_sleep_start();
}
  }
}

void loop()
{
}

esp32_audio_task.h

#include "driver/i2s.h"

#include "AACDecoderHelix.h"
#include "MP3DecoderHelix.h"

static unsigned long total_read_audio_ms = 0;
static unsigned long total_decode_audio_ms = 0;
static unsigned long total_play_audio_ms = 0;

static i2s_port_t _i2s_num;
static esp_err_t i2s_init(i2s_port_t i2s_num, uint32_t sample_rate,
int mck_io_num,   /*!< MCK in out pin. Note that ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only*/
int bck_io_num,   /*!< BCK in out pin*/
int ws_io_num,    /*!< WS in out pin*/
int data_out_num, /*!< DATA out pin*/
int data_in_num   /*!< DATA in pin*/
)
{
_i2s_num = i2s_num;

esp_err_t ret_val = ESP_OK;

i2s_config_t i2s_config;
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
i2s_config.sample_rate = sample_rate;
i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
i2s_config.dma_buf_count = 8;
i2s_config.dma_buf_len = 160;
i2s_config.use_apll = false;
i2s_config.tx_desc_auto_clear = true;
i2s_config.fixed_mclk = 0;
i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_768;
i2s_config.bits_per_chan = I2S_BITS_PER_CHAN_16BIT;

i2s_pin_config_t pin_config;
pin_config.mck_io_num = mck_io_num;
pin_config.bck_io_num = bck_io_num;
pin_config.ws_io_num = ws_io_num;
pin_config.data_out_num = data_out_num;
pin_config.data_in_num = data_in_num;

ret_val |= i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
ret_val |= i2s_set_pin(i2s_num, &pin_config);

return ret_val;
}

static int _samprate = 0;
static void aacAudioDataCallback(AACFrameInfo &info, int16_t *pwm_buffer, size_t len)
{
unsigned long s = millis();
if (_samprate != info.sampRateOut)
{
// log_i("bitRate: %d, nChans: %d, sampRateCore: %d, sampRateOut: %d, bitsPerSample: %d, outputSamps: %d, profile: %d, tnsUsed: %d, pnsUsed: %d",
//       info.bitRate, info.nChans, info.sampRateCore, info.sampRateOut, info.bitsPerSample, info.outputSamps, info.profile, info.tnsUsed, info.pnsUsed);
i2s_set_clk(_i2s_num, info.sampRateOut /* sample_rate */, info.bitsPerSample /* bits_cfg */, (info.nChans == 2) ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO /* channel */);
_samprate = info.sampRateOut;
}
size_t i2s_bytes_written = 0;
i2s_write(_i2s_num, pwm_buffer, len * 2, &i2s_bytes_written, portMAX_DELAY);
// log_i("len: %d, i2s_bytes_written: %d", len, i2s_bytes_written);
total_play_audio_ms += millis() - s;
}
static void mp3AudioDataCallback(MP3FrameInfo &info, int16_t *pwm_buffer, size_t len)
{
unsigned long s = millis();
if (_samprate != info.samprate)
{
log_i("bitrate: %d, nChans: %d, samprate: %d, bitsPerSample: %d, outputSamps: %d, layer: %d, version: %d",
info.bitrate, info.nChans, info.samprate, info.bitsPerSample, info.outputSamps, info.layer, info.version);
i2s_set_clk(_i2s_num, info.samprate /* sample_rate */, info.bitsPerSample /* bits_cfg */, (info.nChans == 2) ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO /* channel */);
_samprate = info.samprate;
}
size_t i2s_bytes_written = 0;
i2s_write(_i2s_num, pwm_buffer, len * 2, &i2s_bytes_written, portMAX_DELAY);
// log_i("len: %d, i2s_bytes_written: %d", len, i2s_bytes_written);
total_play_audio_ms += millis() - s;
}

static uint8_t _frame[MP3_MAX_FRAME_SIZE]; // MP3_MAX_FRAME_SIZE is smaller, so always use MP3_MAX_FRAME_SIZE

static libhelix::AACDecoderHelix _aac(aacAudioDataCallback);
static void aac_player_task(void *pvParam)
{
Stream *input = (Stream *)pvParam;

int r, w;
unsigned long ms = millis();
while (r = input->readBytes(_frame, MP3_MAX_FRAME_SIZE))
{
total_read_audio_ms += millis() - ms;
ms = millis();

while (r > 0)
{
w = _aac.write(_frame, r);
// log_i("r: %d, w: %d\n", r, w);
r -= w;
}
total_decode_audio_ms += millis() - ms;
ms = millis();
}
log_i("AAC stop.");

vTaskDelete(NULL);
}

static libhelix::MP3DecoderHelix _mp3(mp3AudioDataCallback);
static void mp3_player_task(void *pvParam)
{
Stream *input = (Stream *)pvParam;

int r, w;
unsigned long ms = millis();
while (r = input->readBytes(_frame, MP3_MAX_FRAME_SIZE))
{
total_read_audio_ms += millis() - ms;
ms = millis();

while (r > 0)
{
w = _mp3.write(_frame, r);
// log_i("r: %d, w: %d\n", r, w);
r -= w;
}
total_decode_audio_ms += millis() - ms;
ms = millis();
}
log_i("MP3 stop.");

vTaskDelete(NULL);
}

static BaseType_t aac_player_task_start(Stream *input, BaseType_t audioAssignCore)
{
_aac.begin();

return xTaskCreatePinnedToCore(
(TaskFunction_t)aac_player_task,
(const char *const)"AAC Player Task",
(const uint32_t)2000,
(void *const)input,
(UBaseType_t)configMAX_PRIORITIES - 1,
(TaskHandle_t *const)NULL,
(const BaseType_t)audioAssignCore);
}

static BaseType_t mp3_player_task_start(Stream *input, BaseType_t audioAssignCore)
{
_mp3.begin();

return xTaskCreatePinnedToCore(
(TaskFunction_t)mp3_player_task,
(const char *const)"MP3 Player Task",
(const uint32_t)2000,
(void *const)input,
(UBaseType_t)configMAX_PRIORITIES - 1,
(TaskHandle_t *const)NULL,
(const BaseType_t)audioAssignCore);
}

mjpeg_decode_draw_task.h

#define READ_BUFFER_SIZE 1024
// #define MAXOUTPUTSIZE (MAX_BUFFERED_PIXELS / 16 / 16)
#define MAXOUTPUTSIZE (288 / 3 / 16)
#define NUMBER_OF_DECODE_BUFFER 3
#define NUMBER_OF_DRAW_BUFFER 9

#include <FS.h>
#include <JPEGDEC.h>

typedef struct
{
  int32_t size;
  uint8_t *buf;
} mjpegBuf;

typedef struct
{
  xQueueHandle xqh;
  JPEG_DRAW_CALLBACK *drawFunc;
} paramDrawTask;

typedef struct
{
  xQueueHandle xqh;
  mjpegBuf *mBuf;
  JPEG_DRAW_CALLBACK *drawFunc;
} paramDecodeTask;

static JPEGDRAW jpegdraws[NUMBER_OF_DRAW_BUFFER];
static int _draw_queue_cnt = 0;
static JPEGDEC _jpegDec;
static xQueueHandle _xqh;
static bool _useBigEndian;

static unsigned long total_read_video_ms = 0;
static unsigned long total_decode_video_ms = 0;
static unsigned long total_show_video_ms = 0;

Stream *_input;

int32_t _mjpegBufSize;

uint8_t *_read_buf;
int32_t _mjpeg_buf_offset = 0;

TaskHandle_t _decodeTask;
TaskHandle_t _draw_task;
paramDecodeTask _pDecodeTask;
paramDrawTask _pDrawTask;
uint8_t *_mjpeg_buf;
uint8_t _mBufIdx = 0;

int32_t _inputindex = 0;
int32_t _buf_read;
int32_t _remain = 0;
mjpegBuf _mjpegBufs[NUMBER_OF_DECODE_BUFFER];

static int queueDrawMCU(JPEGDRAW *pDraw)
{
  int len = pDraw->iWidth * pDraw->iHeight * 2;
  JPEGDRAW *j = &jpegdraws[_draw_queue_cnt % NUMBER_OF_DRAW_BUFFER];
  j->x = pDraw->x;
  j->y = pDraw->y;
  j->iWidth = pDraw->iWidth;
  j->iHeight = pDraw->iHeight;
  memcpy(j->pPixels, pDraw->pPixels, len);

  // log_i("queueDrawMCU start.");
  ++_draw_queue_cnt;
  xQueueSend(_xqh, &j, portMAX_DELAY);
  // log_i("queueDrawMCU end.");

  return 1;
}

static void decode_task(void *arg)
{
  paramDecodeTask *p = (paramDecodeTask *)arg;
  mjpegBuf *mBuf;
  log_i("decode_task start.");
  while (xQueueReceive(p->xqh, &mBuf, portMAX_DELAY))
  {
// log_i("mBuf->size: %d", mBuf->size);
// log_i("mBuf->buf start: %X %X, end: %X, %X.", mBuf->buf[0], mBuf->buf[1], mBuf->buf[mBuf->size - 2], mBuf->buf[mBuf->size - 1]);
unsigned long s = millis();

_jpegDec.openRAM(mBuf->buf, mBuf->size, p->drawFunc);

// _jpegDec.setMaxOutputSize(MAXOUTPUTSIZE);
if (_useBigEndian)
{
_jpegDec.setPixelType(RGB565_BIG_ENDIAN);
}
_jpegDec.setMaxOutputSize(MAXOUTPUTSIZE);
_jpegDec.decode(0, 0, 0);
_jpegDec.close();

total_decode_video_ms += millis() - s;
  }
  vQueueDelete(p->xqh);
  log_i("decode_task end.");
  vTaskDelete(NULL);
}

static void draw_task(void *arg)
{
  paramDrawTask *p = (paramDrawTask *)arg;
  JPEGDRAW *pDraw;
  log_i("draw_task start.");
  while (xQueueReceive(p->xqh, &pDraw, portMAX_DELAY))
  {
// log_i("draw_task work start: x: %d, y: %d, iWidth: %d, iHeight: %d.", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
p->drawFunc(pDraw);
// log_i("draw_task work end.");
  }
  vQueueDelete(p->xqh);
  log_i("draw_task end.");
  vTaskDelete(NULL);
}

bool mjpeg_setup(Stream *input, int32_t mjpegBufSize, JPEG_DRAW_CALLBACK *pfnDraw,
bool useBigEndian, BaseType_t decodeAssignCore, BaseType_t drawAssignCore)
{
  _input = input;
  _mjpegBufSize = mjpegBufSize;
  _useBigEndian = useBigEndian;

  for (int i = 0; i < NUMBER_OF_DECODE_BUFFER; ++i)
  {
_mjpegBufs[i].buf = (uint8_t *)malloc(mjpegBufSize);
if (_mjpegBufs[i].buf)
{
log_i("#%d decode buffer allocated.", i);
}
else
{
log_e("#%d decode buffer allocat failed.", i);
}
  }
  _mjpeg_buf = _mjpegBufs[_mBufIdx].buf;

  if (!_read_buf)
  {
_read_buf = (uint8_t *)malloc(READ_BUFFER_SIZE);
  }
  if (_read_buf)
  {
log_i("Read buffer allocated.");
  }

  _xqh = xQueueCreate(NUMBER_OF_DRAW_BUFFER, sizeof(JPEGDRAW));
  _pDrawTask.xqh = _xqh;
  _pDrawTask.drawFunc = pfnDraw;
  _pDecodeTask.xqh = xQueueCreate(NUMBER_OF_DECODE_BUFFER, sizeof(mjpegBuf));
  _pDecodeTask.drawFunc = queueDrawMCU;

  xTaskCreatePinnedToCore(
(TaskFunction_t)decode_task,
(const char *const)"MJPEG decode Task",
(const uint32_t)2000,
(void *const)&_pDecodeTask,
(UBaseType_t)configMAX_PRIORITIES - 1,
(TaskHandle_t *const)&_decodeTask,
(const BaseType_t)decodeAssignCore);
  xTaskCreatePinnedToCore(
(TaskFunction_t)draw_task,
(const char *const)"MJPEG Draw Task",
(const uint32_t)2000,
(void *const)&_pDrawTask,
(UBaseType_t)configMAX_PRIORITIES - 1,
(TaskHandle_t *const)&_draw_task,
(const BaseType_t)drawAssignCore);

  for (int i = 0; i < NUMBER_OF_DRAW_BUFFER; i++)
  {
if (!jpegdraws[i].pPixels)
{
jpegdraws[i].pPixels = (uint16_t *)heap_caps_malloc(MAXOUTPUTSIZE * 16 * 16 * 2, MALLOC_CAP_DMA);
}
if (jpegdraws[i].pPixels)
{
log_i("#%d draw buffer allocated.", i);
}
else
{
log_e("#%d draw buffer allocat failed.", i);
}
  }

  return true;
}

bool mjpeg_read_frame()
{
  if (_inputindex == 0)
  {
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
_inputindex += _buf_read;
  }
  _mjpeg_buf_offset = 0;
  int i = 0;
  bool found_FFD8 = false;
  while ((_buf_read > 0) && (!found_FFD8))
  {
i = 0;
while ((i < _buf_read) && (!found_FFD8))
{
if ((_read_buf[i] == 0xFF) && (_read_buf[i + 1] == 0xD8)) // JPEG header
{
// log_i("Found FFD8 at: %d.", i);
found_FFD8 = true;
}
++i;
}
if (found_FFD8)
{
--i;
}
else
{
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
}
  }
  uint8_t *_p = _read_buf + i;
  _buf_read -= i;
  bool found_FFD9 = false;
  if (_buf_read > 0)
  {
i = 3;
while ((_buf_read > 0) && (!found_FFD9))
{
if ((_mjpeg_buf_offset > 0) && (_mjpeg_buf[_mjpeg_buf_offset - 1] == 0xFF) && (_p[0] == 0xD9)) // JPEG trailer
{
found_FFD9 = true;
}
else
{
while ((i < _buf_read) && (!found_FFD9))
{
if ((_p[i] == 0xFF) && (_p[i + 1] == 0xD9)) // JPEG trailer
{
found_FFD9 = true;
++i;
}
++i;
}
}

// log_i("i: %d", i);
memcpy(_mjpeg_buf + _mjpeg_buf_offset, _p, i);
_mjpeg_buf_offset += i;
int32_t o = _buf_read - i;
if (o > 0)
{
// log_i("o: %d", o);
memcpy(_read_buf, _p + i, o);
_buf_read = _input->readBytes(_read_buf + o, READ_BUFFER_SIZE - o);
_p = _read_buf;
_inputindex += _buf_read;
_buf_read += o;
// log_i("_buf_read: %d", _buf_read);
}
else
{
_buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
_p = _read_buf;
_inputindex += _buf_read;
}
i = 0;
}
if (found_FFD9)
{
// log_i("Found FFD9 at: %d.", _mjpeg_buf_offset);
if (_mjpeg_buf_offset > _mjpegBufSize) {
log_e("_mjpeg_buf_offset(%d) > _mjpegBufSize (%d)", _mjpeg_buf_offset, _mjpegBufSize);
}
return true;
}
  }

  return false;
}

bool mjpeg_draw_frame()
{
  mjpegBuf *mBuf = &_mjpegBufs[_mBufIdx];
  mBuf->size = _mjpeg_buf_offset;
  // log_i("_mjpegBufs[%d].size: %d.", _mBufIdx, _mjpegBufs[_mBufIdx].size);
  // log_i("_mjpegBufs[%d].buf start: %X %X, end: %X, %X.", _mjpegBufs, _mjpegBufs[_mBufId].buf[0], _mjpegBufs[_mBufIdx].buf[1], _mjpegBufs[_mBufIdx].buf[_mjpeg_buf_offset - 2], _mjpegBufs[_mBufIdx].buf[_mjpeg_buf_offset - 1]);
  xQueueSend(_pDecodeTask.xqh, &mBuf, portMAX_DELAY);
  ++_mBufIdx;
  if (_mBufIdx >= NUMBER_OF_DECODE_BUFFER)
  {
_mBufIdx = 0;
  }
  _mjpeg_buf = _mjpegBufs[_mBufIdx].buf;
  // log_i("queue decode_task end");

  return true;
}

reddit.com
u/RangerMach1 — 3 hours ago
🔥 Hot ▲ 624 r/arduino

What if Guitar Hero was real? I built a one-hand guitar mode with ESP32

I’ve been working on a guitar robot project that can physically play a real guitar.

This is a test of a new “one-hand mode”.

Instead of fully automated playing:

- The left hand (fretting) is handled by servos (ESP32 controlled)

- The right hand is played by a human, following visual cues on a phone

So it becomes something like a real-world rhythm game —

but you're actually playing a real guitar.

No MIDI, no speakers.

All sound comes from real strings.

The goal is not playback, but physical performance.

Still working on:

- timing precision

- dynamics (strong/weak picking)

- servo noise & damping

Curious what you think —

Does this make guitar more accessible, or does it feel too “robotic”?

u/MegCell — 1 day ago
▲ 3 r/arduino+1 crossposts

BNO085 IMU Sensor Heading Readings

While testing the BNO085 sensor with the example code to find heading from Adafruit. I noticed that the readings for heading between "Rotation Vector" and "Geomagnetic Rotation Vector" drift from each other (up to 25° differences between the two at certain point). This is expected as the "Rotation Vector" reading is supposed to be more accurate with a fusion of magnetometer + gyro + accelerometer while the "Geomagnetic Rotation Vector" reading is only magnetometer + accelerometer (based on the provided documentation for the sensor).

However, what is strange is after relaunching the code for to find heading without moving the sensor at all, the "Rotation Vector" increases/decreases to the same reading as "Geomagnetic Rotation Vector" while the "Geomagnetic Rotation Vector" reading stays about the same after rerunning the code.

Ex:

1st run: Run the example code, rotate the sensor a bit, final reading: Rotation Vector: 100°, Geomagnetic Rotation Vector: 120°.

2nd run, without changing position or anything else, final reading: Rotation Vector: 119°, Geomagnetic Rotation Vector: 121°.

Why is this the case? Isn't the "Rotation Vector" supposed to be more accurate than "Geomagnetic Rotation Vector"? It can't be caused by surrounding magnetic field either cause both readings use the magnetometer, the only difference is the inclusion of the gyro.

u/SirAbsolute0 — 9 hours ago

D.C.P. or digital cassette player

I know I have to give it a better name,but is the project I've been working on for a little bit know,and it's an MP.3 player, but whit the aesthetic,sounds,and looks (I'm considering 3D printing some fake cassettes) like a cassette player,it works whit an Arduino UNO R3 (ELEGOO KIT) a membrane keypad (4x4) a TFT RGB 1.77" disaply (from AZ DELIVERY) a DFPLAYER mini, whit a 16GB microSD and two speakers (again everything from AZ DELIVERY)

ONLY problem so fare is the stupid TFT display, which gents foggy cuz of a light leak from the bad backlight isolation.

u/AHH_Servers — 3 hours ago
▲ 31 r/arduino

i just started arduino projects and i am confused

just started expetimenting with transistors as switches and i am confused ,why doesent my ground work like it should?

is it because of my resistors ? or how does it even work witout ground and why do i need 2 resistors grounded on base and on collector? help

u/player11123 — 1 day ago

ESP32 as a USB to UART

I have a PCB from an old ereader (Kobo Touch) that has some serial connections on the PCB and I want to see them. I found someone that says they have done it, so I know it's possible. I managed to put some pins on the RX and GND role on the PCB and plug then into the ESP32, but I just get garbage on the serial console. See photos of the setup.

I realy don't want to buy an adpter board just for this.

https://preview.redd.it/gimn26i7sdwg1.jpg?width=3472&format=pjpg&auto=webp&s=ec16480516d123a93e565556d67429ca8858959b

https://preview.redd.it/12bp8ci7sdwg1.jpg?width=3472&format=pjpg&auto=webp&s=cc5125b059d222fb64441d60c91357486e422f1c

The code:

#define RXD2 18
#define TXD2 17


void setup() {
  Serial.begin(115200);      // USB (PC)
  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2); // PCB UART
  Serial.println("ESP32 STARTED");
}


void loop() {
  // From PCB → PC
  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }


  From PC → PCB
  while (Serial.available()) {
    Serial2.write(Serial.read());
  }
}#define RXD2 18
#define TXD2 17


void setup() {
  Serial.begin(115200);      // USB (PC)
  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2); // PCB UART
  Serial.println("ESP32 STARTED");
}


void loop() {
  // From PCB → PC
  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }


  From PC → PCB
  while (Serial.available()) {
    Serial2.write(Serial.read());
  }
}

https://preview.redd.it/ighi9c0hsdwg1.png?width=1653&format=png&auto=webp&s=1b6ec7afe60390475f90bec76afabffd178737b1

https://petit-noise.net/blog/kobo-touch-%e3%81%aeuart/

https://petit-noise.net/blog/kobo-touch-%E3%81%ABusb%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%92%E7%B9%8B%E3%81%92%E3%82%8B/

reddit.com
u/mateus2k2 — 1 day ago

Solderable breadboards

Im making a playstation type controller for my school project and have now reached the point at which the project should be soldered. I dont know much about this and have asked AI to assist but was wondering if there were any recommendations from people who have more experience.

Im not sure what the english term is for this but what im looking for is a Lochrasterplatine (in german).

Any help would be appreciated!

reddit.com
u/RandomEmKayFan — 1 day ago

Unable to upload code to Uno R3

I’m trying to get started on a project with a new board. I want to upload a program to just blink different LEDs. However, every time I try to upload the code, it get error messages.

The errors are as follows;

Error: programmer is not responding

Error: programmer is not responding

*** failed

Error: unable to write flash (rc = -1)

Error: programmer is not responding

Failed Uploading: uploading error: exit status 1

I’ve tried looking up these error messages but can’t find much helpful information (particularly in regard to the write flash line, haven’t found anything on it).

I’ve done a few tests though. The board does pass the feedback test. My port is right, same with the board setting in the IDE. Using the reset button does make the L light blink but doesn’t fix anything. I’ve also tried the different pins, I have power going through the board, just can’t program.

Thank you for yalls help. I appreciate it.

reddit.com
u/FitConfection9516 — 1 day ago
▲ 22 r/arduino

How to eliminate inverter noise on (long) servo control wire?

Setup; an Arduino controls a PCA9685 that controls a digital servo that needs to operate the throttle wire of a motor dyno test bench. Everything works as expected, until the frequency inverter driving the room exhaust AC motor is switched on.

The servo has its own 20A 5V power supply, which has a shared ground with the PCA9685 sending a 333Hz servo signal. The distance between control room (where the signal is generated) and servo is ~5 meters. Currently the signal wire together with a ground wire is routed to the servo.

This signal gets very messy, see video of scope...

Servo, understandably goes vibrating and does react properly.

What are the best options to filter this noise out?

  1. Shielded COAX cable with the shielding tied to ground?

  2. Ditch the idea and let a microcontroller closer to the servo control it, getting the right position through CAN / RS485?

  3. Some RC filter?

  4. Ferrite beads everywhere? 😅

  5. ???

Thanks in advance!

u/kossen_zophi — 1 day ago

Code question

I am trying to code a cover to automatically open or close based on the temperature outside. So I have the code for that however, what function would I use if I want to add a piece of code that stops the motors once the cover is fully deployed or retracted? With the way my code is right now the DC motors just keep running and don’t stop

I am coding on TinkerCAD

An example is a garage door opening and closing how the motor stops once the garage door is fully open or closed.

Thank you!!!

reddit.com
u/BlueCrayfish275 — 2 days ago

I want to use a rotary encoder in a Teensy 2.0++ environment, but the recognition is strange.

https://reddit.com/link/1srlwz4/video/kssbozefbjwg1/player

I want to use a rotary encoder in a Teensy 2.0++ environment, but the recognition is strange.

In the video, the encoder was rotated in only one direction.

This is my first time working on a project with Teensy, so there might be some parts I don't know well yet.
The joystick input input on the right is the rotary encoder input input.

What I want is for the input to be entered correctly in the direction I rotate the encoder.

I soldered all the wires to the Teensy and am using a direct connection.

I'm not good at English, so I'm using a translator. If there are any parts you don't understand, please leave a comment.

I am using the GHS38-06G50BSCP526 rotary encoder.
https://caltsensor.com/product/shaft-rotary-encoder-ghs38-series/

#include &lt;Encoder.h&gt;


// ===== Pins =====
const int BT_a = 1;
const int BT_b = 2;
const int BT_c = 3;
const int BT_d = 4;


const int start = 9;
const int start_led = 10;


const int FX_a = 13;
const int FX_b = 14;


const int FX_a_led = 15;
const int FX_b_led = 16;


const int vol_a_a = 44;
const int vol_a_b = 45;


const int vol_b_a = 42;
const int vol_b_b = 43;


Encoder nobl(vol_a_a, vol_a_b);
Encoder nobr(vol_b_a, vol_b_b);


// ===== State =====
uint8_t prevStateL = 0;
uint8_t prevStateR = 0;


bool keyborded = false;
bool prevToggle = false;


long rawR = 0;
float filteredR = 0;


long rawL = 0;
float filteredL = 0;


const int maxStep = 20; // Maximum allowed change per step (tuning point)


long prevRawR = 0;
long prevRawL = 0;


const float alpha = 0.1;   // 0~1 (lower = smoother)
const int deadzone = 5;    // Ignore changes below this value


void setup() {
  Serial.begin(115200);


  pinMode(BT_a, INPUT_PULLUP);
  pinMode(BT_b, INPUT_PULLUP);
  pinMode(BT_c, INPUT_PULLUP);
  pinMode(BT_d, INPUT_PULLUP);
  pinMode(start, INPUT_PULLUP);
  pinMode(FX_a, INPUT_PULLUP);
  pinMode(FX_b, INPUT_PULLUP);


  pinMode(start_led, OUTPUT);
  pinMode(FX_a_led, OUTPUT);
  pinMode(FX_b_led, OUTPUT);


  pinMode(vol_a_a, INPUT_PULLUP);
  pinMode(vol_a_b, INPUT_PULLUP);
  pinMode(vol_b_a, INPUT_PULLUP);
  pinMode(vol_b_b, INPUT_PULLUP);


  Joystick.X(512);
  Joystick.Y(512);
}


void loop() {


  // Debug encoder signals
  // Serial.print("A:");
  // Serial.print(digitalRead(vol_a_a));
  // Serial.print("      ");
  // Serial.print("B:");
  // Serial.println(digitalRead(vol_a_b));


  // =========================
  // Mode toggle (edge detection)
  // =========================
  bool nowToggle = (digitalRead(start) == LOW &amp;&amp;
                    digitalRead(FX_a) == LOW &amp;&amp;
                    digitalRead(FX_b) == LOW &amp;&amp;
                    digitalRead(BT_a) == HIGH);


  if (nowToggle &amp;&amp; !prevToggle) {
    keyborded = !keyborded;


    // Reset inputs (important)
    Keyboard.releaseAll();
    for (int i = 1; i &lt;= 7; i++) {
      Joystick.button(i, 0);
    }


    Serial.println(keyborded ? "Keyboard Mode" : "Joystick Mode");
    delay(200); // debounce
  }
  prevToggle = nowToggle;


  // =========================
  // Button handling
  // =========================
  if (!keyborded) {
    // Joystick mode
    Joystick.button(1, digitalRead(BT_a) == HIGH);
    Joystick.button(2, digitalRead(BT_b) == HIGH);
    Joystick.button(3, digitalRead(BT_c) == HIGH);
    Joystick.button(4, digitalRead(BT_d) == HIGH);
    Joystick.button(5, digitalRead(FX_a) == LOW);
    Joystick.button(6, digitalRead(FX_b) == LOW);
    Joystick.button(7, digitalRead(start) == LOW);
  } else {
    // Keyboard mode
    if (digitalRead(BT_a) == HIGH) Keyboard.press(KEY_S);
    else Keyboard.release(KEY_S);


    if (digitalRead(BT_b) == HIGH) Keyboard.press(KEY_D);
    else Keyboard.release(KEY_D);


    if (digitalRead(BT_c) == HIGH) Keyboard.press(KEY_L);
    else Keyboard.release(KEY_L);


    if (digitalRead(BT_d)==HIGH) Keyboard.press(';');
    else Keyboard.release(';');


    if (digitalRead(FX_a) == LOW) Keyboard.press(KEY_LEFT_SHIFT);
    else Keyboard.release(KEY_LEFT_SHIFT);


    if (digitalRead(FX_b) == LOW) Keyboard.press(KEY_RIGHT_SHIFT);
    else Keyboard.release(KEY_RIGHT_SHIFT);


    if (digitalRead(start) == LOW) Keyboard.press(KEY_ENTER);
    else Keyboard.release(KEY_ENTER);
  }


  int hat = -1;


  // Right encoder
  rawR = nobr.read();
  long deltaR = rawR - prevRawR;


  // Left encoder
  rawL = nobl.read();
  long deltaL = rawL - prevRawL;


  if (deltaL &gt; 0 &amp;&amp; deltaR == 0) hat = 0;
  else if (deltaL &lt; 0 &amp;&amp; deltaR == 0) hat = 180;
  else if (deltaR &gt; 0 &amp;&amp; deltaL == 0) hat = 90;
  else if (deltaR &lt; 0 &amp;&amp; deltaL == 0) hat = 270;
  else if (deltaL &gt; 0 &amp;&amp; deltaR &gt; 0) hat = 45;
  else if (deltaL &gt; 0 &amp;&amp; deltaR &lt; 0) hat = 315;
  else if (deltaL &lt; 0 &amp;&amp; deltaR &gt; 0) hat = 135;
  else if (deltaL &lt; 0 &amp;&amp; deltaR &lt; 0) hat = 225;


  Joystick.hat(hat);


  prevRawR = rawR;
  prevRawL = rawL;
}
reddit.com
u/aksndjkhui — 13 hours ago
🔥 Hot ▲ 184 r/arduino+1 crossposts

I cannot buy a cheap microcontroller

AliExpress is a total scam.

I tried to buy a Raspberry Pi Zero 2W and found a few listings around $30. That’s already above what it should cost, but still acceptable given the shortages. All of them claimed to be in stock.

So I placed an order.

Then I get a WhatsApp message from the seller saying it’s “not in stock.” I ask when it’ll be available again and suddenly the price has doubled (How original!!!!!). Of course. I cancel and request a refund.

Here’s the part that makes this even worse: in my country, banks take around 18% commission both when you pay and when you receive money back. So every refund basically burns approx. 30% of your money for nothing.

Fine. I try again.

I find another listing, slightly more expensive, around $34, but still reasonable. It says 99+ units available. Great. I order it.

Now the seller just doesn’t ship. Then they tell me, again , “no stock.” Meanwhile, I’m pretty sure they do have stock, they just don’t want to sell it at the listed price anymore and are waiting to push it up to $47.

And AliExpress support? Completely useless. No enforcement, no accountability, nothing.

If I cancel again, I lose another 30% to bank fees. For absolutely nothing. Not to mention the time wasted.

This is a broken system that actively scam buyers.

u/samaxidervish — 3 days ago