I2C Lichtsensor soll über SPI digit. Potentiometer steuern

Hallo an die Community!

Habe da ein "kleines" Problem in der Schule mit einer Aufgabe.
Wir sollen den Widerstand eines digitales Potentiometer vom Typ MCP41010 über SPI-Bus mit den Daten eines I2C Lichtsensor vom Typ GY-30 BHI750 FVI stellen. D.h. je höher die Werte am Lichtsensor desto höher soll der Widerstand des Potentiometers sein.

Einzeln bekomm ich die Bauteile zum laufen, d.h. der Lichtsensor liest Helligkeit ein und gibt diese dann via I2C verständlich am Serial Monitor aus.
Auch am Potentiometer habe ich einen Code der die einzelnen "Wiper"-Stellungen durchgeht und Spannungen ausgibt. An der Kombination happert es leider.

Anbei die Codes, vielleicht könnt ihr mir etwas weiterhelfen, da ich im Moment absolut am Ende meiner Fantasie angekommen bin.

Achja um eines gleich vorab klarzustellen: Ich Frage nicht aus faulheit nach oder weil ich keine Lust zu überlegen habe, ich finde einfach keine Lösung für dieses Problem.

Hier der Code den ich für den Lichtsensor verwendet habe:

#include <Wire.h>

byte ICAdress = 0x23;
byte buffer [2];

void setup() 
{
  Wire.begin();
  Serial.begin(9600);
  initSensor();
}

void loop() 
{
  int SensorWert = 0;
  if (readSensor() == 2) 
  {
  SensorWert = ((buffer[0] << 8) | buffer [1]);
  Serial.print(" Beleuchtungsstaerke: ");
  Serial.print(SensorWert);
  Serial.println(" lux ");
  }
  delay (500);
}

void initSensor()
{
Wire.beginTransmission(ICAdress);
Wire.write(0x10); 
Wire.endTransmission();
}

int readSensor()
{
byte SensorWert = 0;
Wire.beginTransmission(ICAdress);
Wire.requestFrom(ICAdress, 2);
while (Wire.available())
{
buffer[SensorWert]= Wire.read();
SensorWert++;
}
Wire.endTransmission();
return SensorWert;
}

und hier der Code den ich für das Poti verwendet habe

#include <SPI.h>  
int CS = 10;
int PotWiperVoltage = 1;
int RawVoltage = 0;
float Voltage = 0;
 
void setup() {
  pinMode (CS, OUTPUT);   
  Serial.begin(9600);
  SPI.begin();    
}
void loop() 
  {
  for (int level = 0; level < 255; level++) 
  {
    PotiWrite(level);
    delay(1000);
    RawVoltage = analogRead(PotWiperVoltage);
    Voltage = (RawVoltage * 5.0 )/ 1024.0;
    Serial.print("Level = " );                      
    Serial.print(level);      
    Serial.print("\t Voltage = ");
    Serial.println(Voltage,3);  
  }
  delay(2000);  

  for (int level = 255; level > 0; level--) 
  {
    PotiWrite(level);
    delay(1000);
    RawVoltage = analogRead(PotWiperVoltage);
    Voltage = (RawVoltage * 5.0 )/ 1024.0;
    Serial.print("Level = " );                      
    Serial.print(level);      
    Serial.print("\t Voltage = ");
    Serial.println(Voltage,3); 
  }
   delay(2000);
}
void PotiWrite(byte value) 
{
  digitalWrite(CS,LOW);
  SPI.transfer(B00010001);
  SPI.transfer(value);
  digitalWrite(CS,HIGH); 
}

Vielen Dank im Voraus für eure Hilfe :slight_smile:

Hi

Hast Du Das auch schon Mal laufen lassen?
Bei 2x 255 Sekunden Wartezeit wird wohl die Pausenklingel schlimmeres verhindern, bevor ansatzweise eine Lösung zustande kommt.

Davon ab - ich sehe zwei wunderbare Einzel-Sketche, Die ich aber so bestimmt nicht antesten werde - ich bin schon alt und könnte in der Zwischenzeit versterben!!

Was ich nicht sehe, ist die Kombination der Beiden und eine Erklärung, Was nun nicht klappt.

MfG

Vom Lichtsensor liest Du einen Wert und in das digitale Potentiometer schreibst Du einen anderen Wert.
Dazwischen mußt Du den Wert umformen.
Das geht am einfachsten mit map().

Grüße Uwe

Achja um eines gleich vorab klarzustellen: Ich Frage nicht aus faulheit nach oder weil ich keine Lust zu überlegen habe, ich finde einfach keine Lösung für dieses Problem.

Ok, das glauben wir dir gern.
Ein fertiges Beispiel aus den Libraries zu kopieren, ist ja auch nicht schwierig.
Wenn du die beiden Sketche tatsächlich selbst geschrieben hast, warum sind denn da keine Kommentare drin, die es uns einfacher machen, was da passieren soll.

Und dann vermissen wir deinen Versuch, diese beiden zu verbinden. Auch mit entsprechenden Kommentaren.
Dann könnten wir wenigsten feststellen, was falsch läuft.

So ist das nicht einfach, zu sehen, was du falsch machst.

Uwe in anderen Worten:

Du brauchst ein ganz klassiches E V A

E ingabe --> Vom Lichtsensor liest Du einen Wert
V erarbeitung --> Dazwischen mußt Du den Wert umformen. Das geht am einfachsten mit map().
A usgabe --> und in das digitale Potentiometer schreibst Du einen anderen Wert.

P.S.:
dein int readSensor() im I2C ist schon eigenartig.
Du returns die gelesenen Byte um dann im Loop den (globalen) Buffer zu lesen.
Kannst so machen, aber dann würde ich das nicht in eine Variable einlesen, die "SensorWert" heißt, sondern "empfangeneBytes" oder ähnlich.

Hallo,
Na ja , du must aus den beiden Sketch´s einen machen. Wenn Du halbwegs verstanden hast was da passiert sollte das doch kein Problem sein.

Im ersten liest Du einen Wert in die Variable "Sensorwert" ein.

SensorWert = ((buffer[0] << 8) | buffer [1]);

mit der Variablen kannst Du dann machen was Du willst. in dem Beispiel wird er halt mit Serial.print() ausgegeben. Du kannst Ihn aber auch weiter verarbeiten. Eigendlich willst Du ihn ja auf das Poti geben. Da wird derzeit im zweiten Sketch in der Funktion void PotiWrite(byte value) ein Byte ausgegeben. Das value musst du durch den Sensorwert ersetzten.

So jetzt kommt ein kleines Problemchen. Der Sensorwert ist ein Integer und kann damit einen Zahlenwert annehmen der nicht in ein Byte passt. Byte geht bis max 255. Jetzt musst Du also zunächst mal herausfinden wie groß der Wert von Sensorwert maximal werden kann bei maximaler Helligkeit. Dann musst Du das so skalieren und begrenzen das der Wert den Du ausgeben willst nicht > 255 wird. (Dreisatz). Du kannst es auch mit map() machen dann macht der Controler für Dich die Umrechnung.

Der Rest ist geschicktes c&p.

Heinz

So jetzt kommt ein kleines Problemchen. Der Sensorwert ist ein Integer und kann damit einen Zahlenwert annehmen der nicht in ein Byte passt.

Schon ( buffer[0] << 8 ) schiebt ja gerade das erste Byte in die höherwertige Hälfte deines int16_t. Allerdings wird da sicher nicht der volle Bereich ausgenutzt, denn vorausgesetzt dein sketch ist in Ordnung und der Sensor gibt tatsächlich einen Wert in "lux" aus, wären Werte über 30000 lux doch ziemlich hell. Obwohl Sommers im Freien durchaus realistisch. Deine Software liefert da erstmal negative Werte. Danach ist die Frage, was der Sensor macht, wenn er Helligkeiten über 65000 lux melden will.
Nach Klärung der Frage, welche Werte in puffer[0] tatsächlich vom Sensor erwartbar sind kannst du dir wünschen, ab welchem Wert du via SPI 100% = 255 ausgeben willst. Wenn der minimale Ausgabe-Wert 0 auch erst bei erfassten 0 "lux" kommen soll, wird aus dem Dreisatz eine einfache Multiplikation oder gar nur eine Bit-Schieberei, sonst kommt tatsächlich die volle Power der map - Funktion zum Tragen. (s. aber auch constrain, das macht map nicht selbständig)

Hallo und vorab gleich entschuldigung für die späte Antwort. Da ich voll berufstätiger Abendschüler bin und es die letzten Wochen etwas verrückt in der Arbeit zuging hatte ich für meine Arduino - Aufgabe wenig zeit.

Habe jetzt meinen Sketch umgebaut (auch etwas "entrümpelt) und beide Kommunizieren miteinander. Das Kommandobyte kommt auch am Arduino an allerdings kommen die Werte vom Lichtsensor nicht an. Ich weiß das, weil ich mithilfe eines Logic Analyzer mir die Signale hab anzeigen lassen. Meine Vermutung ist, dass es einfach an der falsch verwendeten Map-funktion liegt. Hoffe ihr könnt mir einen kurzen Hinweis geben.

#include <Wire.h>
#include <SPI.h>

//I2C
byte ICAdress = 0x23;
byte buffer[2];

//SPI
int CS = 10;


void setup() 
{
  Wire.begin();
  Serial.begin(9600);

  pinMode(CS, OUTPUT);
  SPI.begin();

}

void loop() 
{
  int SensorWert = 0;
  if (readSensor() == 2) 
    {
    SensorWert = ((buffer[0] << 8) | buffer [1]); //durch links shiften wird aus 2 Byte-Werten ein 16Bit Int-wert
    Serial.println(SensorWert);
    map(SensorWert, 0, 65535, 0, 255);  // Wert mappen (Wert der gemappt werden soll, unter Grenze, obere Grenze, untere Grenze Zielbereich, obere Grenze Zielbereich)
    }
  PotiWrite(SensorWert);

  
}

void initSensor()
  {
  Wire.beginTransmission(ICAdress); //Welche I2C Adresse wird angesprochen
  Wire.write(0x10); // Messgenauigkeit (Continously H-Resolution Mode) 0x10 für 1 lux, 0x11 für 0,5lux, 0x13 für 4lux 
  Wire.endTransmission();
  }
  
  int readSensor() //beginn der Messung
  {
  byte SensorWert = 0;
  Wire.beginTransmission(ICAdress);
  Wire.requestFrom(ICAdress, 2); //holt sich 2 Byte von ICAdress
  while (Wire.available())
    {
    buffer[SensorWert]= Wire.read();
    SensorWert++;
    }
  Wire.endTransmission();
  return SensorWert;
  }

void PotiWrite(byte SensorWert) 
  {
    digitalWrite(CS,LOW);
    SPI.transfer(B00010001);        // kommando byte (Dezimal 17)
    SPI.transfer(SensorWert);     //  Position des Poti setzen
    digitalWrite(CS,HIGH); 
  }

danke :slight_smile:

Ach stimmt ja. Der weiß dann ja gar nicht was er mit dem gemappten Wert anfangen soll bzw. wohin er den Wert schreiben soll

Wie du in dem erwähnten Link ja auch nachlesen kannst, weisst man Variablen oft Werte mit dem =-Zeichen zu:

SensorWert = map(SensorWert, 0, 65535, 0, 255);

Wobei hier immer noch fehlt, die Eckwerte von map auf empirisch ermittelte Werte zu ändern.

Hi

Wollte schon einwerfen, daß 0-65535 --> 0-255 auch einfacher zu erledigen ist, wobei Das vll. sogar der Kompiler in eine einfache Shift-Operation umbaut, oder einfach nur stur das obere Register (bei 16 Bit sind's zwei 8 Bit Register) zum Weiterspielen nimmt.

MfG

Wollte schon einwerfen, daß 0-65535 --> 0-255 auch einfacher zu erledigen ist

Genau das ist nicht so einfach zu erledigen, ist aber wohl auch nicht das, was eigentlich gemeint ist.

Wenn man
byte skaliert = sensorWert / 256; schreibt, erlaubt man dem Compiler, das einfach als
skaliert = sensorWert >> 8; zu übersetzen. (Noch einfacher wäre, gleich nur das HIGH-Byte auszuwerten.

skaliert = map(sensorWert, 0, 65535, 0, 255); // sehr zweifelhaft

sorgt dafür , dass Werte bis 65534 einschließlich skaliert 254 ergeben. Das ist vermutlich so nicht gemeint und erfordert tatsächlich komplizierte Multiplikationen und noch kompliziertere Divisions-Operationen.

... immer noch fehlt, die Eckwerte von map auf empirisch ermittelte Werte zu ändern.

Sehr wahr.