ublo
bogdan's (micro)blog

bogdan » muzică și lumină

08:51 pm on Jan 26, 2020 | read the article | tags:

nostalgic încă după sărbătorile de iarnă – și aici mă refer la timpul liber, nu la mâncare și obiceiuri – după robotul wi-fi, de fapt, puțin înaintea lui, inspirat de un instastory, m-am gândit că n-ar strica să mai pierd ceva timp lipind niște circuite de roboți cu bucăți de software să am și eu luminițe în casă, că p-alea de pe Magheru nu le-am văzut. dar cu o ș’pârlă: mi-ar plăcea să fie sincronizate pe muzică.

e greu? pare greu

depinde. dacă mi-ai fi pus întrebarea asta acum douăzeci de ani ți-aș fi spus că da. de fapt nici nu m-aș fi încumetat. dar acum? neah. am nevoie doar de ceva care să transforme sunetele în semnale electrice, pe care apoi le procesez cu un fel de Arduino pentru a controla o bandă de leduri adresabile cu neopixel. simplu ca bună ziua!

pe rând, în fraza de dinainte, sunet în semnal electric: orice microfon face asta. cum mă zgârcesc, să fie microfon condensator – ieftin, bun și de fapt tipul de microfon care se găsea în aproape orice acum ceva timp. e cilindric, în capsulă de metal cu o bucată de pânză pe una dintre margini și doi electrozi pe cealaltă. ce e rău e că semnalul lui e prea mic să pot să-l procesez direct cu Arduino. așa că, fie aleg un modul care are amplificator – dar asta e pentru începători, fie îmi construiesc propriul amplificator de microfon.

lăsând modestia deoparte, prefer varianta asta mai hardcore pentru că pot să controlez exact ce se întâmplă cu semnalul electric de la microfon. am ales pentru amplificare un amplificator operațional. pentru că e la același preț, m-am aruncat direct la unul dual – LM358B. ți-l recomand. de obicei aș fi venit cu alternative, dar circuitul ăsta de la Texas Instruments e printre puținele care funcționează bine cu o singură sursă de alimentare și tensiuni mici – 3-5V. prima jumătate amplifică și cum mai aveam una disponibilă, pe ce-a de-a doua am configurat-o ca superdiodă să pot să integrez semnalul microfonului – dada, e integrarea aia de la matematică, Riemann-Stieltjes.

ai aici o diagramă electronică a părții analogice din proiect, la rezoluție ceva mai bună și gata de print, ca să o ai în față în timp ce-l construiești.

trecând la Arduino, am folosit de fapt un ESP8266 Witty. are wi-fi să pot să configurez luminile din browser – deși, de lene, în practică am folosit Postman, e destul de mic și mult mai rapid decât un Arduino la procesat date – iar, nu că aș avea mare chestie de făcut, dar mai multă viteză nu strică. pentru că ieșirile lui ating maximum 3,3V, nu e direct compatibil cu ledurile neopixel. așa că la mijloc apare un tranzistor care schimbă nivelul discuției digitale.

nu uita, ți-am pregătit aici diagrama electronică a conexiunilor digitale în format pdf, gata pentru print.

leduri neopixel. îmi plac la nebunie. cu un singur fir poți să controlezi o mulțime de leduri înseriate. multicolore! și arată bine și se controlează ușor și poți să faci tot felul de chestii cu ele – așa că obișnuiește-te, vor mai apărea pe aici. problema cu ele e că fiecare led consumă 60mA. banda pe care am cumpărat-o eu acum ceva timp are 150 de leduri = 5m × 30 leduri/m care adunate dau 9A. nouă amperi! am repetat ca să nu crezi că am tastat greșit. așa că ai nevoie de o sursă de alimentare bună, pentru că, deși pentru teste poți să folosești portul USB3 al calculatorului sau un încărcător pentru telefon, în varianta finală vei avea nevoie de ea.

lista de cumpărături

totul pornește de la o bandă cu leduri neopixel. adică cu leduri WS2812 sau variante ale acestora. ledurile de acest fel sunt ceva mai scumpe decât cele obișnuite, chiar și RGB. asta deoarece fiecare led include propriul circuit de control, făcând posibilă aprinderea individuală a fiecărui led din bandă. îți recomand modelul WS2812, deși WS2811 e și el o opțiune, pentru simplul fapt că sunt mai rapide. ce mi se pare interesant e că odată stabilită culoare și intensitatea pentru un led, procesorul poate să se ocupe de orice altceva, lucru important pentru dispozitive fără prea multă putere de calcul.

microfonul și circuitul integrat LM358B vin la pachet cu un buchet de rezistori și o mână de capacitoare. strecurate mai sunt două diode – superdioda aia nu se face singură, da?, un led – te ajută să vezi că merge ce-ai făcut – și un tranzistor – pentru led, săracul, să nu fie singur.

ESP8266 Witty vine singur. mă rog, cu tranzistorul translator, dar ăla nu se pune, chiar dacă e însoțit de două rezistoare. am ales modulul ăsta cu ESP8266 că e ieftin. merge orice altă variantă. de fapt, dacă știam eu că la același preț găsesc ESP8266 WeMos D1 Mini, n-aș fi mers pe Witty. dar, asta e.

chiar dacă am folosit un breadboard pentru definitivarea circuitului audio – versiunea din articolul ăsta e a treia, asta după ce am început măreț cu o versiune care includea nu unul, ci două LM358B și care n-a mers deloc – în final, am ales o variantă mai permanentă care m-a făcut să șterg de praf abilitățile de a folosi Autodesk Eagle.

alte lucruri pe care le-am avut la îndemână și pe care ți le recomand, dar care pot fi refolosite după asta sunt: un clește pentru tăiat fire – al meu e de la hornbach, 86 de lei, dar au și la conexelectronic unul decent, 15 lei, o stație de lipit, conexelectronic, 300 de lei sau un ciocan de lipit, conexelectronic, 50 de lei – ți-l recomand pentru că găsești ușor vârfuri de schimb, pentru că vârfurile sunt consumabile – și aliaj de lipit, un breadboard cu puțin peste 400 de puncte, fire de conexiune – un set de 10 fire tată-tată e perfect, o ramă de fotografie, niște hârtie de calc, o lampă uv pentru unghii, emag, 45 de lei, mr. proper – granule sau lichid, cloură ferică 1l, conexelectronic, 16 lei și diluant de unghii. să nu uiți de multimetru – am testat cu studenții două modele ieftine, DVM832, conexelectronic, 50 de lei și AX100, conexelectronic, 60 de lei – versiunea europeană a multimetrului descris aici.

sunet electric

da. microfonul condensator transformă vibrațiile – nu alea eterice, cele muzicale, da? – în variații ale capacității unui capacitor și pentru că producătorii s-au gândit la noi, au inclus un mic convertor în interior care transformă variația capacității în variații de curent. atenție! microfonul e polarizat. terminalul negativ e cel conectat la carcasa metalică și poți să-l găsești ușor cu un multimetru testând continuitatea. cel de-al doilea terminal e mixt, folosit pentru alimentare, dar și pentru semnal. alimentarea se face printr-un rezistor cu valoarea între 1 și 10KΩ. aici am ales să fac o șmecherie: am împărțit rezistorul în două bucăți, una de 4,7KΩ conectată între polul pozitiv al sursei de alimentare, înseriată cu una de 1KΩ conectată la microfon. punctul median l-am conectat la un capacitor polarizat de 100μF/10V.

de ce m-am chinuit? îți spuneam mai sus că ledurile consumă foarte mult curent. asta înseamnă că tensiunea de alimentare va fi afectată destul de mult – mă rog, e relativ termenul, dar crede-mă pe cuvânt – atunci când ledurile se aprind. păi, ledurile se aprind când primesc semnal audio. dar dacă variația asta de tensiune de alimentare se suprapune peste semnalul audio? îți amintești țiuitul ăla enervant de la karaoke când, de la curajul lichid consumat anterior, ajungi mai aproape de boxe decât ți-ai dori? același lucru se întâmplă și aici: ledurile modulează intrarea de microfon care la rândul ei comandă ledurile care modulează care comandă care … și în loc să facă ce trebuie, circuitul va oscila, cel mai probabil neplăcut.

capacitorul ăla de 100μF netezește extrem de mult aceste variații. ordinea rezistoarelor e irelevantă din ce-am văzut practic. prefer varianta cu rezistorul mai mare către sursa de alimentare și cel mai mic către microfon. semnalul audio – și doar el, că nu mă interesează potențialul ăla continuu obținut prin rezistor – îl extrag printr-un capacitor de 2,2μF conectat la terminalul de alimentare / semnal al microfonului.

tensiunea pe terminalul liber al capacitorului are vârfuri de 40mV în cazul în care sunetul e puternic și cam 12mV pentru sunete normale. ESP8266 are o sensibilitate la intrare de 3mV, cam mică pentru semnalul microfonului.

totuși sunt departe de probleme, am un LM358B la dispoziție. îl pun pe breadboard și îl alimentez, cu plus la pinul 8 și minus la pinul 4.

conectez jumătate din LM358B în configurație de amplificator inversor: leg capacitorul printr-un rezistor de 1KΩ la intrarea inversoare a amplificatorului operațional (pin 2) pe care la rândul ei o conectez la ieșirea amplificatorului operațional (pin 1) printr-un rezistor de 220KΩ. mai am nevoie de o rețea de polarizare a intrării neinversoare, foarte simplă, realizată din doi rezistori de 100KΩ înseriați și un capacitor de 220nF în paralel cu unul dintre ei. amplificarea obținută e în teorie de 220 = 220KΩ/1KΩ.

în practică obțin cam 2V, normal că nu 8,8V, că n-aș avea de unde. mai mult decât suficient. totuși, nu sunt încă mulțumit. dacă mă uit la semnalul pe care l-am obținut conectând osciloscopul la ieșire, nu arată prea «curat». adică nu văd bassul și nici toba mare în el.

trec la cealaltă jumătate din LM358B. nu uit să înseriez un capacitor de 2,2μF nepolarizat la ieșirea primului amplificator, să fiu sigur că nu trece niciun fel de potențial continuu.

cu două diode 1N4148 – de fapt, aici poți să folosești orice diode, dar să fie la fel – și două rezistoare, construiesc o superdiodă folosind amplificatorul operațional disponibil prin pinii 5,6 și 7 al circuitului integrat. construcția e asemănătoare cu a amplificatorului inversor, doar că inserez niște diode la ieșire. pentru intrarea neinversoare nu mă mai interesează să o polarizez la jumătatea tensiunii de alimentare. semnalul nu mai am chef să fie simetric. pentru că fix asta face superdioda: ce e peste zero, trece, ce e sub zero, nu trece. de fapt, pe mine mă interesează alt comportament al diodei: să nu conducă în sens invers, adică de la ieșire spre intrare. e superdiodă pentru că se comportă ca o diodă ideală, fără vrăjeli – ăăă, căderi vreau să zic – de tensiune.

pentru că la ieșirea superdiodei, am conectat un rezistor de 47KΩ în serie cu un capacitor de 10nF către polul pozitiv al sursei de alimentare. circuitul ăsta, diodă în serie cu rezistor în serie cu capacitor se cheamă integrator. e cu rimă Barbiliană. pentru că integrarea aia înseamnă de fapt integrarea semnalului la intrarea superdiodei în raport cu timpul. adică face un fel de medie pe unitatea de timp – încă puțin și îți explic și care e unitatea asta de timp. deci, când un semnal apare, trece prin diodă și prin rezistor și încarcă capacitorul. nu-l și descarcă, chiar dacă scade către 0V, pentru că dioda nu mai conduce.

am pus osciloscopul să-ți arăt cum arată semnalul pe capcitorul din circuitul integrator: frumos nu? se vede clar acum când bate toba. să nu uit, cea mai bună melodie pentru teste e Shed a lui Shebbe. semnalul are acum peste 3V – mai mult nu merge, că și superdioda amplifică de 100 de ori = 1MΩ/10KΩ. 3V pentru că exită căderi de tensiune în etajul de ieșire al amplificatorului operațional. dar deja nu mă mai interesează.

ca să văd dacă sunetul e convertit bine, folosesc un tranzistor 2N3904 în configurația cu colector-comun, numită și repetor-pe-emitor. în link e o simulare aproximativă a modului în care se comportă – apropo, îți recomand Multisim de la National Instruments, te ajută să vezi foarte rapid ce se întâmplă într-un circuit. configurația e de fapt un convertor de impedanță. adică, mai simplu, ce vede capacitorul în paralel e rezistorul de 100Ω conectat în serie cu ledul legat la emitorul tranzistorului, înmulțit ca valoare cu factorul de amplificare în curent al tranzistorului – se cheamă β sau hFE și se găsește în foaia de catalog a tranzistorului. în cazul ăsta, cam 200 în medie. adică 20KΩ. adică intervalul ăla de timp pentru integrare 20ms = 20KΩ × 10nF. vezi? n-am uitat.

semnalul pe led este cam mare pentru limita aia de 3,3V pentru intrarea analogică a ESP8266, așa că în paralel cu ledul și rezistorul – nu doar cu ledul, pentru că dacă-l conectezi acolo, tensiunea va fi constantă, egală cu căderea de tensiune pe led – conectez un divizor rezistiv care împarte la potențialul. doi rezistori de 4,7KΩ fac treaba asta. și gata prima parte. muzica aia ar trebui să facă ledul să lumineze pe ritm. dacă nu, verifică toate conexiunile. ai poze.

esp8266 witty, serios?

dacă mă gândesc acum, nu sunt chiar sigur că am făcut alegerea bună. de fapt, cred că a fost chiar proastă. pentru că prima chestie pe care trebuie să o faci cu Witty e să dezlipești de pe spatele plăcuței superioare un rezistor – vezi imaginea, e suficient să încălzești unul dintre terminale și să miști puțin cu cleștele de el – și să extragi și fotorezistorul de pe față – poți să-l și tai, dar dacă-l dezlipești poți să-l folosești în alt proiect.

înainte de a monta ESP8266 pe breadboard va trebui să pregătești conexiunile pentru VCC și GND prin jumperi și către ADC (2) și GPIO14/D5 (5), deoarece va fi puțin mai greu – ca să nu spun imposibil, că nu-mi place cuvântul – să conectezi terminalele respective acolo unde trebuie după ce modulul e plasat pe breadboard. mare atenție când conectezi jumperii, deoarece eu am avut neinspirația să inversez terminalele VCC și GND și să mă trezesc cu un miros suspect, indicator al faptului că pot liniștit să arunc modulul la gunoi.

pentru programarea în circuit a modului ESP8266 a trebuit să conectez terminalele RST (1), VCC (8), GND (9), D3 (12), RXD (15) și TXD (16) la plăcuța de bază, care conține convertorul USB/serial și circuitul care resetează modulul pentru programare.

la terminalul GPIO14/D5 (5) nu uita să conectezi printr-un rezistor de 4,7KΩ tranzistorul 2N3906. cred că ai observat că e o diferență între cele două tranzistoare. cel cu șase la final, adică ăsta, ultimul, este PNP. configurația în care sunt conectate cele două este aceeași, de «repetor-pe-emitor», în care potențialul electric pe emitor urmărește potențialul bazei, dar spre deosebire de configurația cu tranzistor NPN, potențialul crește cu o valoare constantă, suficientă cât să transforme nivelul HIGH specific 3,3V în nivel HIGH specific 5V. ai aici link cu simularea configurației cu colector-comun PNP.

zi de soft

bibliotecile software pe care le-am utilizat sunt cele clasice, pentru ESP8266 – pentru conexiunea la wi-fi și pentru pornirea unui server web, la care se adaugă biblioteca NeoPixel de la Adafruit. sunt mai multe care fac același lucru, dar cea de la Adafruit mi se pare ușor de folosit. așa că sketch-ul începe simplu cu:

#include <ESP8266WiFi.h> /** biblioteca prin care ESP8266 acceseaza conexiunea wi-fi */
#include <WiFiClient.h> /** biblioteca prin care ESP8266 folosește wi-fi și își ia adresa de IP */
#include <ESP8266WebServer.h> /** biblioteca prin care ESP8266 pornește serverul web, nimic nou */
#include <Adafruit_NeoPixel.h> /** dar asta da, se ocupă de ledurile NeoPixel */

urmează să definesc constantele. pe lângă numele rețelei și parola de wireless, mai am nevoie de o constantă care să-mi spună câte leduri se găsesc în banda mea cu leduri. a mea are 150. pentru teste am folosit banda cu 8 leduri, așa că am modificat constanta asta corespunzător. mai jos o las 150, dar nu uita să o modifici. sincer, n-am testat să văd ce se întâmplă dacă inițializarea se face cu un număr diferit de leduri decât cele disponibile.

#ifndef LOCAL_SSID /** protecție să nu definesc rețeaua de două ori */
#define LOCAL_SSID "nume-retea-wireless" /** numele rețelei, așa cum apare el în telefon sau pe calculator */
#define LOCAL_PASS  "parola-de-la-wireless" /** parola de la wireless */
#endif

const char* ssid     = LOCAL_SSID; /** pun în constante de tip char* valorile definite mai sus */
const char* password = LOCAL_PASS;
const uint16_t all_pixels = 150; /** aici e numărul de leduri din banda pe care o am conectată */

poate ți-ai dori ca numărul de leduri să fie configurabil, însă fără restartarea esp8266, nu cred că e posibil, mai ales că inițializarea ledurilor se face imediat după alimentare, prin funcția setup. probabil are merge o variantă cu salvarea setărilor în memoria EEPROM și restartarea circuitului, dar o las pentru un articol viitor. deci, variabile:

ESP8266WebServer server(80); /** aici definesc serverul web, folosind portul 80, adică normal */
Adafruit_NeoPixel pixels(all_pixels, D5, NEO_GRB + NEO_KHZ800); /** aici definesc banda cu leduri */
/**
 * parametrii sunt:
 * all_pixels, constantă de tip unsigned int, numărul de leduri din bandă
 * D5, este terminalul esp8266 witty la care e conectată banda cu leduri
 * NEO_GRB + NEO_KHZ800, sunt două constante care activează tipul dispunerii ledurilor în neopixel,
 *     și poate fi NEO_GRB sau NEO_RGB, în timp ce a doua constantă îmi spune viteza de comunicare
 *     NEO_KHZ800 pentru WS2812 sau NEO_KHZ400 pentru WS2811
 */

uint16_t _min = 1023; /** minimum înregistrat de convertorul ADC, 1023 petru că va scădea cu fiecare sunet */
uint16_t _max = 0; /** maximum înregistrată de convertorul ADC, 0 pentru că va crește cu fiecare sunet */
uint16_t _length = 32; /** pentru că sursa nu are suficient curent, voi aprinde doar 32 de leduri deodată */
uint16_t _hue = 40000; /** _hue poate fi între 0 și 65535 și reprezintă culoarea în modul de funcționare 1 */
uint8_t _mode = 0; /** reprezintă modul curent de funcționare */
uint16_t pixel = 0; /** o variabilă care indică pixelul curent */
uint16_t last_pixel = 0; /** o variabilă care indică precedentul pixel */

las pentru început doar două dintre funcțiile care se ocupă de clienții serverului web. adică cele implicite, care nu fac nimic important, ci doar îmi permit să testez conexiunea.

void handle_root() { /** functia asta spune ce se intampla cand accesez adresa direct in browser */
  /** adică nimic, doar îi spun clientului că nu sunt erori */
  server.send(200, "text/html", "");
}

void handle_404() { /** functia asta spune ce se intampla cand accesez o pagină care nu există */
  /** doar trimit către client un mesaj de eroare */
  server.send(404, "application/json", "{\"error\":1,\"message\":\"not found\"}");
}

și ajung la setup. inițializarea serverului web e standard. mă conectez la rețeaua wi-fi, aștept să fiu conectat, trimit adresa de IP către portul serial, după care inițializez serverul web. tot aici, inițializez și pixelii și îi stabilesc pe toți ca fiind stinși inițial. vei vedea că mare parte din cod seamănă cu cel de la robotul wi-fi. am totuși grijă ca ledul RGB instalat pe placa ESP8266 Witty să fie stins, făcând terminalele D5, D6 și D7 să aibă potențialul LOW (GND).

void setup() { /** functia de setup, specifica Arduino, ruleaza după reset sau la pornire */
  Serial.begin(115200); /** initializez conexiunea serială, să văd ce se întâmplă, viteza 115200 bauds */
  WiFi.begin(ssid, password); /** încerc să mă conectez la wireless */
  Serial.println(""); /** trimit o linie nouă către terminalul serial */
  
  while (WiFi.status() != WL_CONNECTED) { /** atât timp cât încă nu m-am conectat la wireless */
    delay(500); /** aștept jumătate de secundă */
    Serial.print("#"); /** și trimit un # către terminalul serial, după care mai încerc o dată */
  }
  
  Serial.println(""); /** aici înseamnă că m-am conectat, așa că trec la linia următoare în terminal */
  Serial.println(WiFi.localIP()); /** și trimit adresa de IP ca să știu unde mă conectez cu calculatorul */
  server.on("/", handle_root); /** conectez funcția pentru index la serverul web */
  server.onNotFound(handle_404); /** conectez funcția pentru pagini care nu există la serverul web */
  server.begin(); /** pornesc serverul web */
  Serial.println("S"); /** trimit S către terminal ca să știu că a pornit și serverul web */

  /** până aici, codul e foarte asemănător cu cel de la robotul wi-fi */  

  pixels.begin(); /** inițializez banda cu leduri */
  Serial.println("P"); /** trimit P către termina să știu că banda e inițializată */
  pixels.clear(); /** sting toți pixelii din bandă */
  Serial.println("C"); /** și trimit C prin serial să știu că am făcut asta */

  pinMode (D6, OUTPUT); /** stabilesc terminalele D6, D7, D8 ca ieșiri */
  pinMode (D7, OUTPUT); /** la terminalele astea sunt conectate leduri direct pe placuta witty */
  pinMode (D8, OUTPUT); /** asa ca am grija sa le sting */
  digitalWrite (D6, LOW); /** sting ledul conectat la D6 */
  digitalWrite (D7, LOW); /** sting ledul conectat la D7 */
  digitalWrite (D8, LOW); /** sting ledul conectat la D8 */
}

în funcția loop se întâmplă magia. semnalul analog este procesat de convertorul analog-digital (ADC), i se stabilește intervalul de variație apoi acest interval este transformat într-unul corespunzător fie nuanței reproduse de led – hue, în formatul de reprezentare al culorii HSV – fie intensității culorii – value, din reprezentarea HSV. în fiecare iterație a lui loop, aprind pixelul următor de pe bandă, avâng grijă să sting cel mai vechi pixel – să string un pixel e simplu, pun value = 0 în formatul HSV și gata. nu e chiar cel mai bun algoritm de afișare al sunetului și cu siguranță merge îmbunătățit. proiectul îl găsești și pe github, așa că poți oricând face un fork.

void loop() { /** functia asta se repetă la nesfârșit, după setup */
  /** am nevoie de câteva variabile locale */
  uint8_t
    b_map; /** variabilă pentru intensitatea pixelului când _mode = 1 */
  uint16_t
    a_val, /** variabilă pentru valoarea măsurată de ADC */
    a_map; /** variabilă pentru culoarea pixelului când _mode = 0 */
  uint32_t
    color; /** variabilă pentru culoarea pixelului, în format NeoPixel */
  server.handleClient(); /** aici văd dacă serverul web a primit ceva date */
  
  a_val = analogRead(A0); /** citesc în a_val valoarea din ADC; aici e nivelul sunetului */
  if (_min > a_val) { /** dacă nivelul sunetului e mai mic decât cel minim */
    _min = a_val; /** redefinesc valoarea minimă */
  }
  if (_max < a_val) { /** dacă nivelul sunetului e mai mare decât cel maxim */
    _max = a_val; /** redefinesc valoarea maximă */
  }

  switch (_mode) { /** în funcție de modul de operare */
    case 0: /** dacă modul e 0, atunci: */
      /**
       * găsesc punctul a_map în intervalul [0,65535] care împarte intervalul
       * în aceleași proporții în care a_val împarte intervalul [_min, _max]
       * în felul ăsta, culoarea pixelului e proporțională cu intensitatea sunetului
       */
      a_map = _max > _min ? (uint16_t) floor (65535.0 * (float)(a_val - _min) / (float) (_max - _min)) : 0; 
      color = pixels.gamma32(pixels.ColorHSV(a_map)); /** transform numărul în culoare */
      break;
    case 1:
      /**
       * găsesc punctul b_map în intervalul [0,255] care împarte intervalul
       * în aceleași proporții în care a_val împarte intervalul [_min, _max]
       * în felul ăsta, intensitatea luminoasă a pixelului e proporțională cu sunetul
       */
      b_map = _max > _min ? (uint8_t) floor (255.0 * (float)(a_val - _min) / (float) (_max - _min)) : 0; 
      /** pornind de la culoarea _hue, stabilesc culoarea pixelului având intensitatea b_map */
      color = pixels.gamma32(pixels.ColorHSV(_hue, 255, b_map));
      break;
  }
  
  pixels.setPixelColor(pixel, color); /** fac pixelul curent pixel de culoarea color */
  /**
   * consider că toți pixelii sunt așezați în cerc. pe cerc, calculez pixelul pe care
   * trebuie să-l șterg -> păstez ultimii _length pixel și îl șterg pe _length+1
   */
  last_pixel = (all_pixels + pixel - _length) % all_pixels;
  pixel = (pixel + 1) % all_pixels; /** trec la pixelul următor, în cerc */
  pixels.setPixelColor(last_pixel, pixels.Color(0,0,0)); /** sting ultimul pixel */
  pixels.show(); /** trimit datele către banda cu leduri */
}

versiunea intermediară a sketch-ului pentru IDE-ul Arduino o găsești pe github: 8266-neopixel-wifi-v1.ino.

încărcând programul pe ESP8266, în acest moment ledurile conectate la circuit se vor aprinde în ritmul dictat de sunet. totuși, mă interesează să pot să configurez modul în care ledurile se aprind – adică să aleg între _mode = 0 și _mode = 1, să pot să stabilesc culoarea atunci când _mode = 1, să stabilesc câți pixeli afișez de-o dată sau să resetez valorile _min și _max ale sunetului, pentru a potrivi circuitul cu mediul înconjurător.

comenzile le voi trimite prin HTTP POST către serverul web pornit de ESP8266, cu următorii parametri: mode, care poate fi 0 sau 1 și scrie valoarea în _mode, hue care poate fi orice număr între 0 și 65535 și stabilește _hue, length care poate fi orice număr între 0 și numărul maxim de pixeli minus unu și va modifica _length și reset care atunci când e on va reseta valorile pentru _min și _max.

am nevoie doar de o funcție care să proceseze parametrii și pe care să o apelez când se conectează un client. funcția o voi adăuga fix înainte de setup:

void handle_form () {
  long convert; /** variabilă temporară în care convertesc șiruri în numere */

  if (server.method() != HTTP_POST) { /** dacă cererea nu e HTTP POST */
    server.send(405, "application/json", "{\"error\":1,\"message\":\"method not allowed\"}");
    /** trimit către client codul de eroare asociat cu method not allowed, 405 */
    return;
  }
  for (uint8_t c = 0; c < server.args(); c++) { /** scanez toți parametrii */
    if (server.argName(c) == String("mode")) { /** dacă am găsit mode */
      convert = server.arg(c).toInt(); /** în convert pun valoarea parametrului */
      /** dar dacă cumva valoarea e mai mare de 1, pun în _mode 1 */
      _mode = convert < 0 ? 0 : (convert > 1 ? 1 : convert);
    }
    if (server.argName(c) == String("hue")) { /** dacă am găsit hue */
      convert = server.arg(c).toInt(); /** convertesc valoarea lui în număr */
      /** iar dacă numărul e mai mare de 65535, pun în _hue 65535 */
      _hue = convert < 0 ? 0 : (convert > 65535 ? 65535 : convert);
    }
    if (server.argName(c) == String("length")) { /** dacă am găsit length */
      convert = server.arg(c).toInt(); /** convertesc valoarea lui în număr */
      /** dacă numărul e mai mare decât all_pixels - 1, pun în _length all_pixels - 1 */
      _length = convert <  0 ? 0 : (convert > all_pixels - 1 ? all_pixels - 1 : convert);
    }
    /** dacă găsesc reset cu valoarea on, reset valorile pentru _min și _max */
    if (server.argName(c) == String("reset") && server.arg(c) == String("on")) {
      _min = 1023;
      _max = 0;
    }
  }
  /** trimit ca răspuns valorile nou stabilite */
  server.send (200, "application/json",
    "{\"mode\":" + String(_mode) +
    ",\"length\":" + String(_length) +
    ",\"hue\":" + String(_hue) +
    ",\"min\":" + String(_min) +
    ",\"max\":" + String(_max) +
    "}");
}

mai am doar de asociat funcția care procesează parametrii trimiși către serverul web, modificând setup și inserând legătura imediat după cea pentru procesarea paginii principale – handle_root.

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print("#");
  }
  
  Serial.println("");
  Serial.println(WiFi.localIP());
  server.on("/", handle_root);
  server.on("/rpc/", handle_form); /** aici asociez handle_form cu serverul web */
  server.onNotFound(handle_404);
  server.begin();
  Serial.println("S");
  
  pixels.begin();
  Serial.println("P");
  pixels.clear();
  Serial.println("C");

  pinMode (D6, OUTPUT);
  pinMode (D7, OUTPUT);
  pinMode (D8, OUTPUT);
  digitalWrite (D6, LOW);
  digitalWrite (D7, LOW);
  digitalWrite (D8, LOW);
}

versiunea finală a sketch-ului pentru IDE-ul Arduino – sau dacă îți place mai mult, pentru Visual Studio Code o poți descărca de pe github: 8266-neopixel-wifi-final.ino. am pus-o aici pentru că și mie mi-ar fi lene să o scriu.

lampa pentru unghii

în momentul ăsta, circuitul de pe breadboard funcționează. dacă ai răbdare, vei observa că numărul de leduri influențează negativ semnalul de intrare: ledul indicator de nivel audio va lumina aproape constant, indiferent dacă aude ceva sau nu. asta se întâmplă din cauza contactelor imperfecte și a curenților relativ mari. e momentul pentru o îmbunătățire mai permanentă: cablajul imprimat.

cablajul ăsta e bucata aia de plastic – nu e chiar plastic, dar are aspect – verde – bine, nu doar verde, albastră, neagră, roșie, mov – cu desene, din interiorul oricărui aparat electric. e de fapt o bucată de fibră din sticlă – adică o țesătură din fibră de sticlă acoperită cu un fel de poxypol – peste care se așează o foiță subțire din cupru, care apoi e decupată pentru a desena conexiuni între componente. poate pare complicat de realizat, dar e cât se poate de simplu.

folosind Autodesk Eagle poți să alegi toate componentele de care ai nevoie, să le așezi virtual pe plăcuța ta și apoi să le conectezi între ele cu trasee din cupru. când ești gata, pur și simplu printezi rezultatul pe o hârtie de calc. desenele le-am făcut eu și le găsești aici.

într-un articol viitor îți explic cum se face, cu toate că nivelul la care sunt e cel mult de amator-începător – ai nevoie doar de puțină răbdare și să-ți explic puțin regulile de bază. când deschizi fișierul esp8266-lights-v3.brd descărcat – ai grijă să ai descărcate în același director și fișierul esp8266-lights-v3.sch – apeși prima dată pe butonul Ratsnet, apoi din meniul View, Layer Settings ascunzi toate straturile cu excepția Bottom (albastru), Pads (verde) și Dimension (galben). apeși CTRL și P în același timp și bifezi la Options Solid și Black – doar astea două, da?, Scale Factor ai grijă să fie 1 și ambele Border Left și Border Top le stabilești la 1 inch. nu te descurci, sau n-ai chef de Eagle? nicio problemă. ai desenul traseelor aici, formatat, în format A4. ai nevoie doar de un cititor de PDF-uri care să știe să nu scaleze pagina atunci când o printezi.

decupezi ce-ai printat la dimensiunea ramei foto, în așa fel încât desenul să fie centrat. apoi, din rama foto vei folosi doar geamul și bucata de carton din spate – va trebui să rupi piciorul de suport din carton. stingi lumina – poți să lași doar o lumină slabă, care să nu bată direct către zona ta de lucru. sau dacă chiar ai nevoie de lumină, o lampă roșie e perfectă. așezi plăcuța de circuit imprimat foto cu partea albastră în sus pe cartonul ramei. dezlipești plasticul albastru protector de pe plăcuță. plasezi peste partea metalică desenul cu fața printată în jos. acoperi cu geamul și prinzi totul cu două agrafe de hârtie, simetric. vezi să fie prinse bine și desenul să acopere corect bucata metalică. dimensiunea e fix pentru o plăcuță standard de circuit imprimat de 100×50mm.

introduci rama pregătită în felul ăsta, cu geamul în sus, în lampa de unghii. în funcție de dimensiunea geamului, s-ar putea să fie nevoie să îndepărtezi capacul de jos al lămpii. prima dată când am făcut asta am testat mai multe intervale de timp, dar am văzut că timerul lămpii e perfect. de obicei inserez rama și apăs butonul, iar după ce se termină, întorc rama foto cu capătul celălalt către interiorul lămpii și mai expun o dată, de data asta pentru numai 10-15 secunde. radiația UV polimerizează stratul fotosensibil și imprimă desenul pe plăcuță. să nu arunci foaia de calc, o poți refolosi de câte ori vrei.

în timpul expunerii, găsești o tăviță din plastic – fie iei una dedicată pentru proiecte de tipul ăsta, fie una de unică folosință. eu folosesc o cutie pentru înghețată. neapărat mai ai nevoie de o pereche de mănuși de cauciuc. substanțele pe care ți le indic în continuare sunt toxice, așa că te rog să ai grijă.

pune-ți mănușile! în cutia de plastic măsori fie 50g de mr. proper lichid – eu folosesc un cântar de bucătărie pe care așez direct cutia, fie 5g de mr. proper granule. mr. proper pentru desfundat chiuvete, da? n-ai uitat de mănuși, nu? completezi conținutul cutiei cu 500ml de apă rece de la robinet. nu uita să amesteci continuu. mr. proper e practic făcut din sodă caustică. din aia care se folosește la săpun. atenție la mâini! sper că ai mănuși!

odată expunerea terminată, iei plăcuța și o scufunzi, folosind mănuși!, în soluția de mr. proper. în câteva secunde vei vedea desenul printat prinzând contur. agită plăcuța în tot acest timp și ai grijă să nu atingi suprafața cu desenul. și nici să nu o ții prea mult în soluție. maximum un minut. când e gata, pune plăcuța sub jet de apă de la robinet. dacă nu faci mai multe plăcuțe chiar acum, poți să arunci – folosind mănuși! – soluția în chiuvetă. până la urmă pentru asta a fost făcută, nu?

cu desenul pregătit – folosind mănuși! – pune într-o altă tăviță clorură ferică încât să treacă cu jumătate de centimetru peste plăcuța cu desenul. ai grijă! clorura ferică e foarte corozivă și pătează aproape orice! scoate-ți mănușile și uită-te la un serial timp de o oră. când te întorci, pune-ți din nou mănușile și verifică plăcuța. desenul ar trebui să fie numai din cupru, arămiu, iar între traseele desenată să nu mai fie nimic. dacă mai sunt urme de material între trasee, lasă plăcuța în soluție pentru încă 10-15 minute. ajută mult dacă încălzești soluția în baie de apă. ai mare grijă pentru că lichidul pătează aproape orice, inclusiv metalul! sper că ai mănuși!

pentru a putea arunca lichidul în siguranță, neutralizează-l înainte cu bicarbonat de sodiu. vei știi că e neutralizat, atunci când bicarbonatul nu se mai dizolvă în lichid și nu mai apar bule. durează mult, poate și câteva zile. partea bună e că soluția o poți folosi pentru destul de multe plăcuțe și nu expiră așa repede. probabil o tăviță cu capac e cea mai bună. eu din nou, folosesc o altă cutie pentru înghețată. ai grijă unde o depozitezi! e toxică!

așa. acum plăcuța se numește corodată. desenele sunt vizibile, nu sunt urme de cupru între trasee și ai clătit-o cu apă din abundență. pentru a înlătura stratul rezistent la coroziune, șterge-o cu diluant pentru unghii. când mă gândesc că jumătate din proces a inclus cuvântul «unghii», îmi vin în minte numai saloanele de apartament și condițiile în care-și desfășoară activitatea. dar ce știu eu?

ultimul pas în pregătirea plăcuței este găurirea ei. cu o minibormașină – poate fi de orice fel, eu am una de la Dremmel, cu stand – perforezi locurile special marcate pe plăcuță. majoritatea cu un burghiu de 0.8mm. am observat că deși mai mic e mai bine, unele componente au terminale prea groase. găurile pentru baretele cu pini le poți lărgi cu un burghiu de 1.2mm. perforarea o fac întotdeauna dinspre partea cu traseele de cupru – spațiul pentru găuri te ajută în ghidarea burghiului, iar după ce termin, prefer să spăl plăcuța cu detergent de vase și burete abraziv pentru a înlătura eventualele așchii. o șterg cu un șervețel de bucătărie și gata.

e timpul pentru lipeli

cum plăcuța e pregătită, încep să transfer componentele de pe breadboard pe ea. uite și diagrama de amplasare a componentelor, care să te ghideze, în format PDF, pentru print. încep cu rezistorii. în poze, i-am pus pe toți deodată. nu te sfătuiesc să faci asta. transferă câte unul de-o dată. imediat ce l-ai transferat, îl lipești cu aliaj și ciocanul de lipit și tai cu cleștele sfic terminalele cât mai aproape de plăcuță. dacă vrei, poți să le păstrezi pe post de jumperi, însă eu nu-mi bat capul cu ele.

după rezistori, urmează capacitorii ceramici. nu știu dacă observi, dar ordinea e dictată de înălțimea componentelor. urmează soclul pentru circuitul integrat, tranzistoarele, condensatorul electrolitic – aici, dacă vrei să păstrezi dimensiunea poți folosi unul cu tantal, deși e cam de 10 ori mai scump – baretele cu pini mamă și pinii pentru alimentare.

vei observa că pe diagramă – și pe plăcuță – e spațiu pentru un rezistor de 0Ω. aici e de fapt locul pentru un jumper, pe care-l poți folosi dacă vrei să alimentezi ledurile separat de restul circuitului. în cele mai multe situații, poți să-l înlocuiești cu un simplu fir și scapi de o problemă. eu am ales să pun două terminale pe care să le conectez între ele cu un fir cu conectoare dupont mamă-mamă.

fără a conecta alimentarea, montezi circuitul integrat LM358B în soclu și ESP8266 Witty în baretele lui, cu antena wi-fi îndreptată în partea opusă conectorului pentru leduri. nu uita de leduri. polaritatea e 5V, date și GND, dacă ții plăcuța cu GND către tine.

mai e ceva. microfonul nu l-am lipit pe placă. am preferat să pun alte două terminale pentru a conecta microfonul prin două fire dupont mamă-mamă. nu-ți recomand. semnalul fiind mic și amplificarea mare, fie folosești cablu pentru microfon, fie fire foarte scurte, răsucite, fie lipești microfonul direct pe placă. orice fir mai lung, funcționează ca antenă și ajungi ca ledurile să nu fie controlate de lumină, ci de undele radio din casă.

pentru alimentare, te sfătuiesc să folosești o sursă potentă în comutație. rezultate bune am obținut cu una de 5V 4.3A. dar depinde mult de banda cu leduri pe care o folosești, câte are pe metru și cât de lungă e. pentru cele mai scurte, un încârcător de 5V/2.4A pentru telefon ar trebui să fie suficient și are avantajul că poți alimenta tot circuitul prin conectorul micro USB al modului ESP8266 Witty.

(alte) referințe:

  1. LM358B – foaie de catalog
  2. 2N3904 – foaie de catalog
  3. ESP8266 Witty – diagrama conexiunilor

conflict de interese:

în articole apar des conexelectronic – principala sursă de componente discrete, foarte profesioniști, optimusdigital și robofun – magazine cu module electronice pentru că sunt din bucurești. câteodată – dar mai rar – cumpăr chestii de la clește – galați și ardushop – sibiu. prefer ce e în bucurești pentru că în caz de urgență, când fac un proiect pentru facultate, pot să primesc repede ce am nevoie. componente electronice mai cumpăr uneori de la adelaida – craiova – aproape cel mai bun stoc, doar că serviciile sunt puțin cam lente, mouser – polonia, foarte bine aprovizionați, dar cu timp de livrare de câteva zile, olimex – bulgaria – care au module interesante, unice și watterott – germania – cu prețuri excelente și servicii premium, doar că și-au schimbat site-ul și stocul a rămas în urmă. acest articol nu este susținut de niciunul dintre ele.

aceast sait folosește cookie-uri pentru a îmbunătăți experiența ta, ca vizitator. în același scop, acest sait utilizează modulul Facebook pentru integrarea cu rețeaua lor socială. poți accesa aici politica mea de confidențialitate.