Skocz do zawartości

Projekt z czujnikami do auta


MatJJ

Pomocna odpowiedź

@MatJJ No to Święty Mikołaj już sobie poszedł, czas na prezenty.

Zacznijmy od jednej ważnej sprawy: operowanie floatami w Arduino to nie jest dobry pomysł. Wolne to, niedokładne i odporne na próby ładnego wyświetlania. Ponieważ wszystkie wartości (zarówno wejściowe, jak i wyjściowe) mieszczą się w zakresie int16, będziemy operować właśie tym typem.

No tak, ale przecież dane wejściowe to napięcia podane w postaci float...

Właśnie. Normalnie przeliczylibyśmy wartość otrzymaną z ADC na napięcie, czyli w przypadku 5-voltowego Arduino:

    float u = (5.0 * analogRead(pin)) / 1024;

Tylko po co? Przecież te wartości służą tylko do znalezienia przedziału, a dokładność floata raczej nie będzie większa niż dokładność przetwornika!

Zróbmy więc odwrotnie - przeliczmy napięcia podane w tabelach na 10-bitowe liczby zwracane przez ADC, czyli:

    int n = (int)(u * 1024 / 5.0);

Oczywiście możemy żmudnie przeliczać wszystkie podane w tabelkach wartości, ale ja jeztem zbyt leniwy i napisałem sobie taki krótki kod w pythonie:

tempin=[0.45, 0.48, 0.51, 0.54, 0.56, 0.60, 0.63, 0.66, 0.69, 0.74, 0.78,
        0.82, 0.86, 0.92, 0.97, 1.03, 1.08, 1.15, 1.21, 1.28, 1.34, 1.42,
        1.50, 1.58, 1.66, 1.76, 1.85, 1.94, 2.03, 2.14, 2.24, 2.35, 2.45,
        2.56, 2.66, 2.76, 2.86, 2.97, 3.08, 3.19, 3.29, 3.39, 3.49, 3.59,
        3.69]
print 'const int16_t PROGMEM temp_in[]={' + \
    (', '.join(str(int(x * 1024 / 5.0)) for x in tempin)) + '};'

pressin=[0.26, 1.46, 2.18, 2.76, 3.14, 3.40, 3.51, 3.60, 3.71]
print 'const int16_t PROGMEM press_in[]={' + \
    (', '.join(str(int(x * 1024 / 5.0)) for x in pressin)) + '};'

I tu uwaga: parametr in funkcji multiMap* musi zawierać wartości rosnące, tak więc tabelkę temperatur musimy odwrócić!

W wyniku dostajemy coś, co możemy bezpośrednio wkleić w program:

const int16_t PROGMEM temp_in[]={92, 98, 104, 110, 114, 123, 129, 135, 141, 151, 159, 168, 176, 188, 198, 211, 221, 235, 248, 262, 274, 291, 307, 323, 340, 360, 379, 397, 416, 438, 459, 481, 502, 524, 545, 565, 586, 608, 631, 653, 674, 694, 715, 735, 756};
const int16_t PROGMEM press_in[]={53, 299, 446, 565, 643, 697, 719, 738, 760};

Tak samo postąpimy z wartościami wynikowymi: będą to wartości int, w tabelce press_out wyrażające ciśnienie w 0.01 bara, w temp_out temperaturę w 0.01 °C.

const int16_t PROGMEM temp_out[]={1500, 1475, 1450, 1425, 1400, 1375,
    1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 1075,
    1050, 1025, 1000, 975, 950, 925, 900, 875, 850, 825, 800, 775, 750,
    725, 700, 675, 650, 625, 600, 575, 550, 525, 500, 475, 450, 425, 400};
const int16_t PROGMEM press_out[]={0, 414, 552, 138, 276, 690, 828, 966, 1035};

Dobra, dość teoretyzowania, czas na obiecany prezent.Pozwoliłem sobie lekko podrasować Twój kod:

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
// nie będziemy korzystać z biblioteki MultiMap bo jest spaprana
// natomiast z tabelek we flashu owszem owszem
// nie pamiętam czy ten include jest potrzebny ale symulator
// na tinkercadzie bez tego głupieje
#include <avr/pgmspace.h>

#define BACKLIGHT_PIN 3 //pin podświetlenia wyświetlacza
LiquidCrystal_I2C lcd(0x27, 20 , 4); //adres wyświetlacza

//Map sensor
const int pressureInput = A0; //PIN OD MAP SENSORA
const int oilPressInput = A1; //PIN OD CIŚNIENIA OLEJU
const int oilTempInput  = A2; //PIN OD TEMPERATURY OLEJU

const int pressureZero = 0;
const int pressureMax = 330;
const int pressuretransducermaxBAR = 3;

const int baudRate = 9600;
const int sensorreadDelay = 250; //1/4 s odsw.


float pressureValue = 0;

// a teraz tabelki dla ciśnienia i temperatury

const int16_t PROGMEM press_out[]={0, 414, 552, 138, 276, 690, 828, 966, 1035};

const int16_t PROGMEM press_in[]={53, 299, 446, 565, 643, 697, 719, 738, 760};

const int16_t PROGMEM temp_out[]={1500, 1475, 1450, 1425, 1400, 1375,
    1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 1075,
    1050, 1025, 1000, 975, 950, 925, 900, 875, 850, 825, 800, 775, 750,
    725, 700, 675, 650, 625, 600, 575, 550, 525, 500, 475, 450, 425, 400};

const int16_t PROGMEM temp_in[]={92, 98, 104, 110, 114, 123, 129, 135,
        141, 151, 159, 168, 176, 188, 198, 211, 221, 235, 248, 262, 274,
        291, 307, 323, 340, 360, 379, 397, 416, 438, 459, 481, 502, 524,
        545, 565, 586, 608, 631, 653, 674, 694, 715, 735, 756};


// zdefiniujemy sobie jeszcze pomocnicze makro żeby się nie męczyć
#define TABLESIZE(a) (sizeof(a) / sizeof(a[0]))

// funkcja pgm_read_word zwraca uint16_t; zrobmy coś żeby zwróciła inta
#define pgm_read_int16(a) (int16_t)pgm_read_word(a)

// A teraz najważniejsze: odpowiednik funkcji multiMap, ale działający
// na danych w pamięci flash i odporny na overflow.

int multiMap16_P(int value, const int16_t *in, const int16_t *out, uint8_t size)
{
    if (value <= pgm_read_int16(in)) return pgm_read_int16(out);
    if (value >= pgm_read_int16(in+size-1)) return pgm_read_int16(out+size-1);

    uint8_t pos = 1;
    while(value > pgm_read_int16(in+pos)) pos++;

    long int in2 = pgm_read_int16(in+pos);
    long int out2 = pgm_read_int16(out+pos);
    if (value == in2) return out2; // nie ma co dzielić!

    long int in1 = pgm_read_int16(in+pos-1);
    long int out1 = pgm_read_int16(out+pos-1);
    return ((value - in1) * (out2 - out1)) / (in2-in1) + out1;
}


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

    lcd.begin(20,4);
    lcd.setBacklight(HIGH);

}

void loop()
{
  //map sensor
  pressureValue = analogRead(pressureInput);
  pressureValue = ((pressureValue-pressureZero)*pressuretransducermaxBAR)/(pressureMax-pressureZero);
  Serial.print(pressureValue, 1);
  Serial.println("bar");

  lcd.setCursor(0,0); //k0 l0
  lcd.print("Boost:");
  lcd.print(pressureValue-1, 1);
  lcd.print("b");
  lcd.print("         .");

  // sprbujmy sobie teraz obliczyć ciśnienie oleju
  int oilPress = analogRead(oilPressInput); // surowa wartość
  oilPress =  multiMap16_P(oilPress, press_in, press_put, TABLESIZE(press_in));
  // mamy wartość w jednostach 0.01 bara, spróbujmy to jakoś ładnie zaoktąglić
  oilPress = (oilPress + 4) / 10;
  //a teraz spróbujemy to ładnie wypisać
  //użyjemy funkcji sprintf, która bardzo ładnie pisze.
  //potrzebny jakiś bufor pomocniczy
  char buf[32]; // z zapasem
  sprintf(buf, "OIL PRESSURE: %2d.%1d B", oilPress/10, oilPress % 10);
  lcd.setCursor(0,1);
  lcd.print(buf);

  // zróbmy to samo z temperaturą
  int oilTemp = analogRead(oilTempInput);
  oilTemp = multiMap16_P(oilTemp, temp_in, temp_out, TABLESIZE(temp_in));
  // mamy wartość w 0.01 stopnia, znów zaokrąglamy
  oilTemp = (oilTemp+49) / 100;

  sprintf(buf, "OIL TEMP.:    %3d *C", oilTemp);
  lcd.setCursor(0,2);
  lcd.print(buf);


  lcd.setCursor(0,3);
  // nie wiem co to, na wszelki wypadek nie dotykam bo wybuchnie!
  lcd.print("Intake temp:  XX  ."); //Wyświetlenie tekstu na 4 linii

  delay(sensorreadDelay);
}

I tu uwaga: nie mogłem tego sprawdzić (tinkercad jak dostaje sprintfa to robi zwis, a o wyświetlaczach na i2c chyba nic nie wie), mogłem się gdzieś machnąć, więc jakiś drobny babol jest możliwy.

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.