# Output Devices - Week 10 ---- ## Assignments > **Input Devices** > > **group assignment** > > - measure the power consumption of an output device > > **individual assignment** > > - add an output device to a microcontroller board you've designed, and program it to do something ## Group Assignment I wanted to work with the addressable LEDs ('NeoPixel') for my final project, so I looked a bit more into them. ### General about 'NeoPixel' [WS2812B Datasheet](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) Those LEDs are special because they have an Integrated Circuit (IC) for adresssing the LED, and all in a 5050 package (5mmx5mm). Addressable means in this case that the LEDs are controllable via an address via a specific [protocol](#how-they-work). #### Inner Structure ```{figure} ../media/Week10/leds_neo-closeup.jpg [Source](https://learn.adafruit.com/assets/10668) ``` They have 4 connectors: - VCC (in this case 3-5V) - Ground (VSS) - Data Input (DIN) - Data Out (DOUT) In the above case are the top and bottom ones VCC or Ground and connected horizontally. Then are the DIN and DOUT pins the middle ones. #### How they work To control/use them, the need a voltage and they also need to be programmed/need to have a value.\ The DIN and DOUT Pins are SPI pins of the IC and receive output from another IC (e.g. the one controlling it OR another NeoPixel). And the usual way they are used is to daisy chain the LEDs (DIN to DOUT) and only connect one DIN pin to a MCU.\ That means, there is only one pin of the MCU needed to control many of those LEDs. And the amount of LED is actually only limited by your power supply and your needed frequency to change the LEDs. This is because of how they are addressed, they use the data-in pin to receive 24 bits (3 bytes, one byte for each color). The rest is just forwarded to DOUT and the next LEDs. This way you can easily control every pixel in a stripe with only needing one wire! :D ### Measuring Power Consumption [Group Page](https://fabacademy.org/2025/labs/ilmenau/assignments/week10/) For the Power Consumption I measured the current of one WS2812B LED to calculate, how much Ampere would run on my board below. There I limited the current to 100mA and set the Voltage to 3.3V which I wanted to operate them with in my board below. In this case, the led was used previously and pre-programmed. ![IMAGE_WS_current](../media/Week10/measure_led.jpg) There I measured: **0.02A @ 3.3V** for one LED with color white. I wanted to build 12 of those LEDs in my board and I wanted to use 2 of the [Super Capacitors](https://www.digikey.com/en/products/detail/tecate-group/TPLC-3R8-10MR8X14/12417602) to power the board, here the energy and time calculation: ``` 2x Super Conductors each: - 10F - 3.8V 12x WS2812B each: - 0.02A - 3.3V Q = C * V = 10F * 3.8V => 38C == 38 As For 1 LED: 2 * 38 As / 0.02A = 3800s == ca. 63min (-> a small pomodoro cycle :D) For 12 LEDs: 2 * 38As / (12 * 0.02A) = 317s == ca. 5min ``` Also note that this is with full brightness and white color, meaning if I change to another color and decrease the brightness, I should get more time :]. ## Individual Assignment ### AT Tiny412 Blink First I wanted to try out my debugger and programmer for the ATTiny412 I build in [Electronic Production](../assignments/electronics_production.md) and just use [this small and cute little board](https://leomcelroy.com/svg-pcb/?file=hello.t412.3.blink.js) to test a simple LED Blink code without the Arduino library. ![IMAGE_SMALL_ATTINY](../media/Week10/attiny_small.jpg) I used the [FTDI](https://gitlab.fabcloud.org/pub/programmers/programmer-serial-d11c) Programmer and first the [SPDI](https://gitlab.fabcloud.org/pub/programmers/programmer-adapter-serial-to-updi) adapter but it didn't work with the board because the pins are wrongly connected I guess, ![IMAGE_FTDI_UPDI_SETUP](../media/Week10/ftdi_wrong.jpg) After that, I used [this one](https://leomcelroy.com/svg-pcb/?file=hello.serial-UPDI.js) which then worked with following setup. ![IMAGE_FTDI_UPDI_SETUP](../media/Week10/ftdi_right.jpg) #### AVR-GCC Then I compiled this small program `blink.c`: ```c #define F_CPU 20000000ULL #include #include #define PIN PIN4_bp #define PERIOD 1000 int main(void) { PORTA.DIR = PIN; // Choose pin 4 data direction while(1){ PORTA.OUTSET = PIN; // set pin 4 as output pin _delay_ms(500); PORTA.OUTTGL = PIN; // Toggle state of pin 4 _delay_ms(200); } } ``` with following commands just with [avr-gcc](https://gcc.gnu.org/wiki/avr-gcc) and [avr-libc](https://github.com/avrdudes/avr-libc): ```bash GCC_ARGS=(-mmcu=attiny412 -O2); avr-gcc "${GCC_ARGS[@]}" -c blink.c -o blink.o; avr-gcc "${GCC_ARGS[@]}" blink.o -o blink.elf; avr-objcopy -O ihex blink.elf blink.hex ``` and then with my flasher-setup, I installed [pymcuprog](https://github.com/microchip-pic-avr-tools/pymcuprog) with pip and then just used: ```bash pymcuprog -d attiny412 -t uart -u /dev/ttyACM0 ping # just for checking, if the connection works pymcuprog -d attiny412 -t uart -u /dev/ttyACM0 write -f blink.hex # for flashing the hex file ``` but this didn't work :[, I need to look into that a bit more... #### Arduino IDE Then just as a Sanity check, I used the Arduino IDE, installed the [tinyMegaCore](https://github.com/SpenceKonde/megaTinyCore) boards and set the ATTiny412 as a target and the `/dev/ttyACM0` as programmer. Then I just loaded the blink program, and flashed it to the board ![IMAGE_ARDUINO_IDE](../media/Week10/arduino.png) which worked :D. ![IMAGE_FTDI_TINY_BLINK](../media/Week10/attiny_blink.jpg) ### Final Project POC Now I used my proof of concept device from the [last weeks](./electronics_production.md#own-pcb) to control the LED-Matrix with my touchpads, therefore I used the [example-code](https://github.com/espressif/esp-idf/tree/release/v5.4/examples/peripherals/touch_sensor/touch_sensor_v2/touch_pad_read) for the Qtouch pins of the esp-idf. Unfortunately I did not choose the [Charlieplexing](https://academy.cba.mit.edu/classes/output_devices/array/Charlieplexing.pdf) for my matrix control because when I designed it, I didn't know about such a thing. ![IMAGE_BOARD](../media/Week10/poc.jpg) I only changed the callback for the threading-abstraction to check for the pad pressed and then blink one LED in the Matrix and some pragma-stuff. ```c /* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include "driver/gpio.h" #include "driver/touch_pad.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include #include #define TOUCH_BUTTON_NUM 3 #define TOUCH_CHANGE_CONFIG 0 #define LED_V0 1 #define LED_V1 2 #define LED_V2 3 #define LED_G0 6 #define LED_G1 5 #define LED_G2 4 static const char *TAG = "touch read"; static const touch_pad_t button[TOUCH_BUTTON_NUM] = { /*TOUCH_PAD_NUM1,*/ /*TOUCH_PAD_NUM2,*/ /*TOUCH_PAD_NUM3,*/ /*TOUCH_PAD_NUM4,*/ /*TOUCH_PAD_NUM5,*/ /*TOUCH_PAD_NUM6,*/ TOUCH_PAD_NUM7, TOUCH_PAD_NUM8, TOUCH_PAD_NUM9, /*TOUCH_PAD_NUM10,*/ /*TOUCH_PAD_NUM11,*/ /*TOUCH_PAD_NUM12,*/ /*TOUCH_PAD_NUM13,*/ /*TOUCH_PAD_NUM14*/ }; /* Read values sensed at all available touch pads. Print out values in a loop on a serial monitor. */ // // MY CHANGES // static void tp_example_read_task(void *pvParameter) { uint32_t touch_value; /* Wait touch sensor init done */ vTaskDelay(100 / portTICK_PERIOD_MS); printf( "Touch Sensor read, the output format is: \nTouchpad num:[raw data]\n\n"); while (1) { for (int i = 0; i < TOUCH_BUTTON_NUM; i++) { touch_pad_read_raw_data(button[i], &touch_value); // read raw data. printf("T%d: [%4" PRIu32 "] ", button[i], touch_value); if (i == 0) { if (touch_value > 30000) { gpio_set_level(LED_V1, 1); } else { gpio_set_level(LED_V1, 0); } } if (i == 1) { if (touch_value > 30000) { gpio_set_level(LED_V2, 1); } else { gpio_set_level(LED_V2, 0); } } if (i == 2) { if (touch_value > 30000) { gpio_set_level(LED_V0, 1); } else { gpio_set_level(LED_V0, 0); } } } printf("\n"); vTaskDelay(200 / portTICK_PERIOD_MS); } } // // END MY CHANGES // void app_main(void) { /* Initialize touch pad peripheral. */ touch_pad_init(); for (int i = 0; i < TOUCH_BUTTON_NUM; i++) { touch_pad_config(button[i]); } gpio_reset_pin(LED_V0); gpio_reset_pin(LED_V1); gpio_reset_pin(LED_V2); gpio_reset_pin(LED_G0); gpio_reset_pin(LED_G1); gpio_reset_pin(LED_G2); /* Set the GPIO as a push/pull output */ gpio_set_direction(LED_V0, GPIO_MODE_OUTPUT); gpio_set_direction(LED_V1, GPIO_MODE_OUTPUT); gpio_set_direction(LED_V2, GPIO_MODE_OUTPUT); gpio_set_direction(LED_G0, GPIO_MODE_OUTPUT); gpio_set_direction(LED_G1, GPIO_MODE_OUTPUT); gpio_set_direction(LED_G2, GPIO_MODE_OUTPUT); gpio_set_level(LED_G0, 0); gpio_set_level(LED_G1, 1); gpio_set_level(LED_G2, 1); #if TOUCH_CHANGE_CONFIG /* If you want change the touch sensor default setting, please write * here(after initialize). There are examples: */ touch_pad_set_measurement_interval(TOUCH_PAD_SLEEP_CYCLE_DEFAULT); touch_pad_set_charge_discharge_times(TOUCH_PAD_MEASURE_CYCLE_DEFAULT); touch_pad_set_voltage(TOUCH_PAD_HIGH_VOLTAGE_THRESHOLD, TOUCH_PAD_LOW_VOLTAGE_THRESHOLD, TOUCH_PAD_ATTEN_VOLTAGE_THRESHOLD); touch_pad_set_idle_channel_connect(TOUCH_PAD_IDLE_CH_CONNECT_DEFAULT); for (int i = 0; i < TOUCH_BUTTON_NUM; i++) { touch_pad_set_cnt_mode(button[i], TOUCH_PAD_SLOPE_DEFAULT, TOUCH_PAD_TIE_OPT_DEFAULT); } #endif /* Denoise setting at TouchSensor 0. */ touch_pad_denoise_t denoise = { /* The bits to be cancelled are determined according to the noise level. */ .grade = TOUCH_PAD_DENOISE_BIT4, .cap_level = TOUCH_PAD_DENOISE_CAP_L4, }; touch_pad_denoise_set_config(&denoise); touch_pad_denoise_enable(); ESP_LOGI(TAG, "Denoise function init"); /* Enable touch sensor clock. Work mode is "timer trigger". */ touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); touch_pad_fsm_start(); /* Start task to read values by pads. */ xTaskCreate(&tp_example_read_task, "touch_pad_read_task", 4096, NULL, 5, NULL); } ``` There I first mixed the pin-direction up in my schematics, where I then tested the LEDs manual: ![IMAGE_TEST_LEDs](../media/Week10/touch_measure.jpg) But then I corrected the code and it worked :D ```{video} ../media/Week10/touch.mp4 :width: 100% ``` Also I tested out the NeoPixel LEDs in the [next week](./networking_and_communications.md#designing-the-pcb). ### Pomodoro Timer ![IMAGE](../media/Week10/pomodoro_schematics.png) ![IMAGE](../media/Week10/pomodoro_traces.png) ![IMAGE](../media/Week10/pomodoro_nice.jpg) ![IMAGE](../media/Week10/pomodoro.jpg) ## Notes - [Best Practices from Adafruit](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices) - https://gcc.gnu.org/wiki/avr-gcc