This shows you the differences between two versions of the page.
pm:prj2023:alucaci:priza-smart [2023/05/18 22:32] laurentiu.ursu [Hardware Design] |
pm:prj2023:alucaci:priza-smart [2023/05/28 23:13] (current) laurentiu.ursu |
||
---|---|---|---|
Line 12: | Line 12: | ||
Priza smart este un prelungitor cu o singura priza care inregistreaza informatii despre consumul de curent pe care dispozitivul bagat in priza il foloseste. Aceasta priza stocheaza datele inregistrate care pot fi folosite mai tarziu pentru a trasa grafice de consum. Priza foloseste de asemenea un display pentru a afisa atat consumul instantaneu, cat si consumul total de cand a inceput masuratoarea. Exista si un buton de reset pentru a reincepe masuratoarea | Priza smart este un prelungitor cu o singura priza care inregistreaza informatii despre consumul de curent pe care dispozitivul bagat in priza il foloseste. Aceasta priza stocheaza datele inregistrate care pot fi folosite mai tarziu pentru a trasa grafice de consum. Priza foloseste de asemenea un display pentru a afisa atat consumul instantaneu, cat si consumul total de cand a inceput masuratoarea. Exista si un buton de reset pentru a reincepe masuratoarea | ||
- | {{:pm:prj2023:alucaci:priza_extern.png?800|}} | + | {{:pm:prj2023:alucaci:screenshot_1.png?800|}} |
===== Hardware Design ===== | ===== Hardware Design ===== | ||
Line 26: | Line 26: | ||
* Suport priza tata si mama | * Suport priza tata si mama | ||
- | {{:pm:prj2023:alucaci:schema_pm.png?400|}} | + | {{:pm:prj2023:alucaci:screenshot_2.png?400|}} |
===== Software Design ===== | ===== Software Design ===== | ||
+ | **Mediu De Dezvoltare** | ||
- | <note tip> | + | Arduino IDE |
- | Descrierea codului aplicaţiei (firmware): | + | |
- | * mediu de dezvoltare (if any) (e.g. AVR Studio, CodeVisionAVR) | + | |
- | * librării şi surse 3rd-party (e.g. Procyon AVRlib) | + | |
- | * algoritmi şi structuri pe care plănuiţi să le implementaţi | + | |
- | * (etapa 3) surse şi funcţii implementate | + | |
- | </note> | + | |
- | ===== Rezultate Obţinute ===== | + | **Biblioteci folosite** |
- | <note tip> | + | * Waveshare OLED_0_96_Display |
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | * SdFat by Bill Greiman |
- | </note> | + | |
- | ===== Concluzii ===== | + | <note tip>Pentru a reduce consumul de memorie, a trebuit sa fac cateva schimbari in biblioteca celor de la Waveshare</note> |
- | ===== Download ===== | + | ** Cod Sursa ** |
- | <note warning> | + | ==== Functie de Setup ==== |
- | O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-). | + | |
- | Fişierele se încarcă pe wiki folosind facilitatea **Add Images or other files**. Namespace-ul în care se încarcă fişierele este de tipul **:pm:prj20??:c?** sau **:pm:prj20??:c?:nume_student** (dacă este cazul). **Exemplu:** Dumitru Alin, 331CC -> **:pm:prj2009:cc:dumitru_alin**. | + | Functia de setup are ca rol initializarea diferitilor pini si difertelor componenta care creeaza proiectul |
- | </note> | + | |
+ | <code cpp> | ||
+ | void setup() { | ||
+ | sei(); // Permite intreruperi | ||
+ | |||
+ | configure2msTimer(); // Configurare | ||
+ | |||
+ | /* Initializare pin pentru card SD */ | ||
+ | pinMode(sdCardPin, OUTPUT); | ||
+ | digitalWrite(sdCardPin, LOW); | ||
+ | |||
+ | /* Initializare biblioteca si imagine al display-ului */ | ||
+ | System_Init(); | ||
+ | OLED_0in96_Init(); | ||
+ | Driver_Delay_ms(500); | ||
+ | OLED_0in96_clear(); | ||
+ | UWORD Imagesize = ((OLED_0in96_WIDTH%8==0)? (OLED_0in96_WIDTH/8): (OLED_0in96_WIDTH/8+1)) * OLED_0in96_HEIGHT; | ||
+ | if((BlackImage = (UBYTE *)malloc(Imagesize)) == NULL) { | ||
+ | return -1; | ||
+ | } | ||
+ | Paint_NewImage(BlackImage, 64, 128, 90, BLACK); | ||
+ | Paint_SelectImage(BlackImage); | ||
+ | Paint_Clear(BLACK); | ||
+ | |||
+ | pinMode(A0, INPUT); // Initializare pin pentru citire curent | ||
+ | |||
+ | /* Initializare pin pentru Reset*/ | ||
+ | pinMode(resetPin, INPUT_PULLUP); | ||
+ | attachInterrupt(digitalPinToInterrupt(resetPin), resetTotal, FALLING); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Functie de Resetare ==== | ||
+ | |||
+ | Functia de resetare are ca scop resetarea variabilei care tine energia totala consumata de priza | ||
+ | |||
+ | <code cpp> | ||
+ | void resetTotal() { | ||
+ | totalPower = 0.0; | ||
+ | displayPower(oldAvgPower, totalPower); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Functia de citire curent ==== | ||
+ | |||
+ | Functia de citire a curentului este folosita pentru a converti valoarea citita analogic in curentul real citit de senzor. Pentru a avea niste valori mai apropiate de realitate, se va face media a 500 de valori citite la fiecare secunda | ||
+ | |||
+ | <code cpp> | ||
+ | void readCurrent() { | ||
+ | int currentSensorValue = analogRead(A0); | ||
+ | |||
+ | float sensedVoltage = (Vcc / analogMax) * currentSensorValue; | ||
+ | |||
+ | float sensedCurrent = (sensedVoltage - ACS712_Ref) / ACS712_Sensitivity; | ||
+ | |||
+ | if (sensedCurrent < 0) { | ||
+ | sensedCurrent *= -1; | ||
+ | } | ||
+ | |||
+ | avgSensedCurrentTotal += sensedCurrent; | ||
+ | |||
+ | measures++; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Functia de afisare pe display ==== | ||
+ | |||
+ | Functia de afisare pe display este folosita pentru a crea si afisa pe ecran string-urile care redau informatia | ||
+ | |||
+ | <code cpp> | ||
+ | void displayPower(float avgPower, float totalPower) { | ||
+ | char avgPowerStr[24] = "Consmpt.: "; | ||
+ | char totalPowerStr[24] = "Total: "; | ||
+ | |||
+ | char avgPowerF[10]; | ||
+ | char totalPowerF[10]; | ||
+ | |||
+ | floatToStr(avgPowerF, avgPower); | ||
+ | floatToStr(totalPowerF, totalPower); | ||
+ | |||
+ | strcat(avgPowerStr, avgPowerF); | ||
+ | strcat(totalPowerStr, totalPowerF); | ||
+ | strcat(avgPowerStr, "W"); | ||
+ | strcat(totalPowerStr, "Wh"); | ||
+ | |||
+ | Paint_Clear(BLACK); | ||
+ | Paint_DrawString_EN(10, 0, avgPowerStr, &Font8, WHITE, WHITE); | ||
+ | Paint_DrawString_EN(10, 17, totalPowerStr, &Font8, WHITE, WHITE); | ||
+ | // Show image | ||
+ | OLED_0in96_display(BlackImage); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Functie pentru scriere pe card SD ==== | ||
+ | |||
+ | Aceasta functie este folosita pentru a scrie intr-un fisier de pe cardul SD informatiile necesare pentru trasarii graficului, adica o pereche (x,y) unde x este secunda la care a facut efectuata masura si y energia totala consumata pana la acel punct. | ||
+ | |||
+ | <code cpp> | ||
+ | void writeToSDCard(float x, float y) { | ||
+ | digitalWrite(displayPin, LOW); | ||
+ | digitalWrite(sdCardPin, HIGH); | ||
+ | |||
+ | if (sd.begin(sdCardPin, SPI_HALF_SPEED)) { | ||
+ | bool res = graph.open("graph.txt", O_WRONLY | O_APPEND | O_CREAT); | ||
+ | |||
+ | char xStr[10], yStr[0]; | ||
+ | |||
+ | floatToStr(xStr, x); | ||
+ | floatToStr(yStr, y); | ||
+ | |||
+ | char result[16] = "("; | ||
+ | strcat(result, xStr); | ||
+ | strcat(result, ", "); | ||
+ | strcat(result, yStr); | ||
+ | strcat(result, ")\n"); | ||
+ | |||
+ | Serial.println(result); | ||
+ | |||
+ | graph.print(result); | ||
+ | graph.close(); | ||
+ | } else { | ||
+ | Serial.println("Could not initialize SD card"); | ||
+ | } | ||
+ | |||
+ | digitalWrite(sdCardPin, LOW); | ||
+ | digitalWrite(displayPin, HIGH); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Intreruperea ==== | ||
+ | |||
+ | Intreruperea timer-ului are loc la fiecare 2ms. Este folosita pentru a citi atat pentru a citi senzorul (la fiecare 2ms) si pentru a afisa pe ecran valorile updatate (la fiecare secunda). Valoarea puterii este calculata folosind media ultimelor 500 de valori citite | ||
+ | |||
+ | <code cpp> | ||
+ | ISR(TIMER1_COMPA_vect) { | ||
+ | readCurrent(); | ||
+ | entries++; | ||
+ | |||
+ | if (entries == 500) { | ||
+ | entries = 0; | ||
+ | float avgPower = (avgSensedCurrentTotal / measures) * 230; | ||
+ | |||
+ | avgSensedCurrentTotal = 0.0; | ||
+ | |||
+ | measures = 0; | ||
+ | |||
+ | totalPower += oldAvgPower / 3600; | ||
+ | |||
+ | if (avgPower < 0) { | ||
+ | avgPower = 0.0; | ||
+ | } | ||
+ | |||
+ | oldAvgPower = avgPower; | ||
+ | |||
+ | displayPower(avgPower, totalPower); | ||
+ | writeToSDCard(counter, avgPower); | ||
+ | counter++; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Functii ajutatoare ==== | ||
+ | |||
+ | Functia `floatToStr` este folosita pentru a transforma un float intr-un char * | ||
+ | |||
+ | <code cpp> | ||
+ | void floatToStr(char *result, float num) { | ||
+ | long a = (long) (num * 100); | ||
+ | |||
+ | long copy; | ||
+ | copy = a; | ||
+ | short int len = 0; | ||
+ | |||
+ | while (copy > 0) { | ||
+ | copy /= 10; | ||
+ | len++; | ||
+ | } | ||
+ | |||
+ | if (len <= 2) { | ||
+ | len = 3; | ||
+ | } | ||
+ | |||
+ | result[len + 1] = 0; | ||
+ | |||
+ | result[len--] = (a % 10) + 48; | ||
+ | a /= 10; | ||
+ | result[len--] = (a % 10) + 48; | ||
+ | a /= 10; | ||
+ | result[len--] = '.'; | ||
+ | |||
+ | while (len >= 0) { | ||
+ | result[len--] = (a % 10) + 48; | ||
+ | a /= 10; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <note important>Din motive de memorie, modulul de card SD si cel de display nu pot functiona simultan pe un Arduino Uno care are doar 2kb de memorie SRAM. Am adaugat si verificat modulele individual, dar pot functiona impreuna doar cu un microcontroller cu mai multa memorie</note> | ||
+ | |||
+ | ===== Rezultate Obţinute ===== | ||
+ | |||
+ | ===== Concluzii ===== | ||
+ | |||
+ | ===== Download ===== | ||
+ | {{:pm:prj2023:alucaci:priza_smart.zip|}} | ||
===== Jurnal ===== | ===== Jurnal ===== | ||