Vastaanottimen lähdekoodi V2: Difference between revisions

From Pessin randon wiki
Created page with "=== Versiohistoria === ==== V2.0 ==== Muutettu täysin näytön näyttötapaa. Parannettu vastaanotetun tiedon varmistamista käyttäen CRC-16 funktiota. Tiedetyt ongelmat. Rekisteröi uuden vastaanotetun koodin vasta kun näyttö on piirtänyt KAIKKI ID:t.<syntaxhighlight lang="c#"> #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <RH_ASK.h> #include <CRC16.h> #define OLED_RESET -1 #define SCREEN_WIDTH 128 // OLED d..."
 
 
(One intermediate revision by the same user not shown)
Line 2: Line 2:


==== V2.0 ====
==== V2.0 ====
Muutettu täysin näytön näyttötapaa.


Parannettu vastaanotetun tiedon varmistamista käyttäen CRC-16 funktiota.
* Muutettu täysin näytön näyttötapaa.
* Parannettu vastaanotetun tiedon varmistamista käyttäen CRC-16 funktiota.
* Tiedetyt ongelmat. Rekisteröi uuden vastaanotetun koodin vasta kun näyttö on piirtänyt KAIKKI ID:t.


Tiedetyt ongelmat. Rekisteröi uuden vastaanotetun koodin vasta kun näyttö on piirtänyt KAIKKI ID:t.<syntaxhighlight lang="c#">
==== V2.1 ====
 
* Lisätty näytön tummennus, kun ei näytettäviä ID:.
* Poistettu ylimääräiset muuttujat
* Yhdistetty IDs ja ID last seen samaan muuttujataulukkoon vähentääksemme muistin käyttöä
* Muita muistinoptimointeja, kuten checksummin tallentaminen unit8_t muuttujaan unit16_t sijaan.
<syntaxhighlight lang="c#">
#include <SPI.h>
#include <SPI.h>
#include <Wire.h>
#include <Wire.h>
Line 21: Line 28:
RH_ASK driver(1000, 12, 11, 10); // RX = D12
RH_ASK driver(1000, 12, 11, 10); // RX = D12


//ID:n tallennusmuuttujat
// ID storage structure
const int MAX_IDS = 16;
const int MAX_IDS = 16;
uint8_t ids[MAX_IDS];
struct IdData {
unsigned long idTimes[MAX_IDS]; //Aikaleima
  uint8_t id;
  unsigned long time;
};
IdData idData[MAX_IDS];
int numIds = 0;
int numIds = 0;


//Screen indicator
// Screen indicator
const unsigned long BLINK_INTERVAL = 500;
const unsigned long BLINK_INTERVAL = 500;
bool indicatorOn = false;
bool indicatorOn = false;
Line 33: Line 43:


// ID activity & deactivity
// ID activity & deactivity
//const unsigned long ACTIVE_TIME = 300000;    // time threshold for active IDs (5 minutes)
const unsigned long INACTIVE_TIME = 3600000; // time threshold for inactive IDs (1 hour)
const unsigned long INACTIVE_TIME = 3600000; // time threshold for inactive IDs (1 hour)


Line 40: Line 49:
int selectedId = 0;
int selectedId = 0;
unsigned long lastSelectedTime = 0;
unsigned long lastSelectedTime = 0;
static uint8_t receivedChecksum[2];


void setup() {
void setup() {
  Serial.begin(9600);
   // initialize the OLED screen
   // initialize the OLED screen
   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
Line 54: Line 63:
   display.setTextColor(SSD1306_WHITE);
   display.setTextColor(SSD1306_WHITE);
   display.setCursor(0, 0);
   display.setCursor(0, 0);
   display.println(F("Odotetaan..."));
   display.println(F("Initializing..."));
   display.display();
   display.display();
    
    
Line 60: Line 69:
     display.clearDisplay();
     display.clearDisplay();
     display.setCursor(0, 0);
     display.setCursor(0, 0);
     display.println(F("Radio ei vastaa"));
     display.println(F("Radio module error!"));
     display.display();
     display.display();
     while (1); //  
     while (1); //  
   }
   }
}
}


void loop() {
void loop() {
  handleRadioReceiver(); // receive packages


  handleRadioReceiver(); //receive packages
   // update the selected ID every X seconds to show different ID's timestamp and draw the screen
 
   // update the selected ID every 3 seconds to show different ID's timestamp
   if (millis() - lastSelectedTime >= DISPLAY_DURATION) {
   if (millis() - lastSelectedTime >= DISPLAY_DURATION) {
     selectedId = (selectedId + 1) % numIds;
     selectedId = (selectedId + 1) % numIds;
Line 77: Line 84:
     displayData();
     displayData();
   }
   }
 
   void removeInactiveIds();
  //displayData();  // display all of the received IDs and their last seen times, or "Waiting for message..." if there are no IDs recorded
   removeInactiveIds(); // remove any inactive IDs older than INACTIVE_TIME
}
}


//Receive the radio message
// Receive the radio message
void handleRadioReceiver() {
void handleRadioReceiver() {
   uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
   uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
   uint8_t buflen = sizeof(buf);
   uint8_t buflen = sizeof(buf);
   static bool checksumReceived = false;
   static bool checksumReceived = false;
  static uint16_t receivedChecksum;
   uint16_t checksumValue;
   uint16_t checksumValue;
   uint8_t data[4];
   uint8_t data[4];


   if (driver.recv(buf, &buflen)) { // Non-blocking
   if (driver.recv(buf, &buflen)) { // Non-blocking
    int16_t rssi = driver.lastRssi();
     if (buflen == 2 && !checksumReceived) {
     if (buflen == 2 && !checksumReceived) {
       // Received checksum packet
       // Received checksum packet
       receivedChecksum = *(uint16_t *)buf;
       memcpy(receivedChecksum, buf, sizeof(receivedChecksum));
       checksumReceived = true;
       checksumReceived = true;
     } else if (buflen == 4 && checksumReceived) {
     } else if (buflen == 4 && checksumReceived) {
       // Received data packet, with preceding valid checksum packet
       // Received data packet, with preceding valid checksum packet
     
       memcpy(data, buf, sizeof(data));
       memcpy(data, buf, sizeof(data));


Line 108: Line 108:
       checksumValue = crc.calc();
       checksumValue = crc.calc();


       if(checksumValue == receivedChecksum) {
       if (memcmp(&checksumValue, receivedChecksum, sizeof(receivedChecksum)) == 0) {
         uint8_t id = processPacket(data, sizeof(data) / sizeof(data[0]));
         uint8_t id = processPacket(data, sizeof(data) / sizeof(data[0]));


Line 115: Line 115:
           bool found = false;
           bool found = false;
           for (int i = 0; i < numIds; i++) {
           for (int i = 0; i < numIds; i++) {
             if (ids[i] == id) {
             if (idData[i].id == id) {
               found = true;
               found = true;
               idTimes[i] = millis(); // Update the last time this ID was received
               idData[i].time = millis(); // Update the last time this ID was received
               break;
               break;
             }
             }
Line 123: Line 123:


           if (!found && numIds < MAX_IDS) {
           if (!found && numIds < MAX_IDS) {
             ids[numIds] = id;
             idData[numIds].id = id;
             idTimes[numIds] = millis();
             idData[numIds].time = millis();
             numIds++;
             numIds++;
           }
           }
Line 137: Line 137:
     }
     }
   }
   }
}
bool checkChecksum(uint8_t *data, uint8_t *receivedCrc) {
  CRC16 crc;
  crc.setPolynome(0x1021); // CCITT polynomial (0x1021)
  crc.add(data, sizeof(data));
  uint16_t calcCrc = crc.calc();
  return (calcCrc == *(uint16_t *)receivedCrc);
}
}


Line 163: Line 154:
     display.println("Waiting for");
     display.println("Waiting for");
     display.print("message...");
     display.print("message...");
    setContrast(1);
   } else {
   } else {
     // Valitaan fonttikoko ID-määrän perusteella
     // Dynamically adjust font size based on the number of IDs received
     int textSize;
     int textSize;
     int idsPerRow = 4;
     int idsPerRow = 4;
Line 175: Line 167:
       idsPerRow = 6;
       idsPerRow = 6;
     }
     }
    setContrast(255);
     display.setTextSize(textSize);
     display.setTextSize(textSize);


    // Perustiedot
    //const int charsPerId = 2;      // max 2 merkkiä ID:ssä
    //const int charWidth = 6;        // per merkki (fontti size=1)
    //const int idBoxWidth = charsPerId * charWidth * textSize + 2;  // pieni väli
     const int idBoxWidth = 15 * textSize;
     const int idBoxWidth = 15 * textSize;
    //int idsPerRow = (textSize == 1) ? 8 : 4;  // size=1 → 8 per rivi, muuten 4
     int rowHeight = 8 * textSize;
     int rowHeight = 8 * textSize;


     // Piirretään ID:t riveittäin
     // Display IDs in rows and columns
     for (int i = 0; i < numIds; i++) {
     for (int i = 0; i < numIds; i++) {
       int row = i / idsPerRow;
       int row = i / idsPerRow;
Line 194: Line 182:


       if (i == selectedId) {
       if (i == selectedId) {
         display.setTextColor(BLACK, WHITE); // invertoitu
         display.setTextColor(BLACK, WHITE); // inverted
       } else {
       } else {
         display.setTextColor(WHITE, BLACK);
         display.setTextColor(WHITE, BLACK);
Line 200: Line 188:


       display.setCursor(x, y);
       display.setCursor(x, y);
       display.print(ids[i]);
       display.print(idData[i].id);
     }
     }


     // Last seen aika
     // Display last seen time for the selected ID
     display.setTextSize(1);
     display.setTextSize(1);
     display.setTextColor(WHITE);
     display.setTextColor(WHITE);
     int lastSeenMins = (millis() - idTimes[selectedId]) / 60000;
     int lastSeenMins = (millis() - idData[selectedId].time) / 60000;
     display.setCursor(SCREEN_WIDTH - 24, SCREEN_HEIGHT - 8);
     display.setCursor(SCREEN_WIDTH - 24, SCREEN_HEIGHT - 8);
     display.print(lastSeenMins);
     display.print(lastSeenMins);
Line 212: Line 200:
   }
   }


   // Vilkkuva indikaattori
   // Blinking indicator
   //if (millis() - indicatorTime >= BLINK_INTERVAL) {
   indicatorOn = !indicatorOn;
    indicatorOn = !indicatorOn;
  indicatorTime = millis();
    indicatorTime = millis();


    display.setCursor(0, SCREEN_HEIGHT - 8);
  display.setCursor(0, SCREEN_HEIGHT - 8);
    if (indicatorOn) {
  if (indicatorOn) {
      display.print(".");
    display.print(".");
    } else {
  } else {
      display.print(" ");
    display.print(" ");
    }
   }
   //}


   display.display();
   display.display();
}
}


void removeInactiveIds() {
void removeInactiveIds() {
  if (numIds == 0) {
    return;
  }
   for (int i = numIds - 1; i >= 0; i--) {
   for (int i = numIds - 1; i >= 0; i--) {
     if (millis() - idTimes[i] > INACTIVE_TIME) {
     if (millis() - idData[i].time > INACTIVE_TIME) {
       // shift the remaining IDs and last seen times down by one position to overwrite the inactive ID
       // Shift the remaining IDs and last seen times down by one position to overwrite the inactive ID
       for (int j = i + 1; j < numIds; j++) {
       for (int j = i + 1; j < numIds; j++) {
         ids[j - 1] = ids[j];
         idData[j - 1] = idData[j];
        idTimes[j - 1] = idTimes[j];
       }
       }
       numIds--;
       numIds--;
Line 241: Line 228:
   }
   }
}
}
void setContrast(uint8_t newContrast) {
  display.ssd1306_command(SSD1306_SETCONTRAST);
  display.ssd1306_command(newContrast);
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 18:43, 4 October 2025

Versiohistoria

V2.0

  • Muutettu täysin näytön näyttötapaa.
  • Parannettu vastaanotetun tiedon varmistamista käyttäen CRC-16 funktiota.
  • Tiedetyt ongelmat. Rekisteröi uuden vastaanotetun koodin vasta kun näyttö on piirtänyt KAIKKI ID:t.

V2.1

  • Lisätty näytön tummennus, kun ei näytettäviä ID:tä.
  • Poistettu ylimääräiset muuttujat
  • Yhdistetty IDs ja ID last seen samaan muuttujataulukkoon vähentääksemme muistin käyttöä
  • Muita muistinoptimointeja, kuten checksummin tallentaminen unit8_t muuttujaan unit16_t sijaan.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RH_ASK.h>
#include <CRC16.h>

#define OLED_RESET -1
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

RH_ASK driver(1000, 12, 11, 10); // RX = D12

// ID storage structure
const int MAX_IDS = 16;
struct IdData {
  uint8_t id;
  unsigned long time;
};
IdData idData[MAX_IDS];
int numIds = 0;

// Screen indicator
const unsigned long BLINK_INTERVAL = 500;
bool indicatorOn = false;
unsigned long indicatorTime = 0;

// ID activity & deactivity
const unsigned long INACTIVE_TIME = 3600000; // time threshold for inactive IDs (1 hour)

// Screen rotation time
const unsigned long DISPLAY_DURATION = 2500; // 2.5 s
int selectedId = 0;
unsigned long lastSelectedTime = 0;

static uint8_t receivedChecksum[2];

void setup() {
  // initialize the OLED screen
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(F("Initializing..."));
  display.display();
  
  if (!driver.init()) {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(F("Radio module error!"));
    display.display();
    while (1); // 
  }
}

void loop() {
  handleRadioReceiver(); // receive packages

  // update the selected ID every X seconds to show different ID's timestamp and draw the screen
  if (millis() - lastSelectedTime >= DISPLAY_DURATION) {
    selectedId = (selectedId + 1) % numIds;
    lastSelectedTime = millis();
    displayData();
  }
  void removeInactiveIds();
}

// Receive the radio message
void handleRadioReceiver() {
  uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
  uint8_t buflen = sizeof(buf);
  static bool checksumReceived = false;
  uint16_t checksumValue;
  uint8_t data[4];

  if (driver.recv(buf, &buflen)) { // Non-blocking
    if (buflen == 2 && !checksumReceived) {
      // Received checksum packet
      memcpy(receivedChecksum, buf, sizeof(receivedChecksum));
      checksumReceived = true;
    } else if (buflen == 4 && checksumReceived) {
      // Received data packet, with preceding valid checksum packet
      memcpy(data, buf, sizeof(data));

      CRC16 crc;
      crc.add(data, sizeof(data));    // add the DIP switch values to the CRC calculation
      checksumValue = crc.calc();

      if (memcmp(&checksumValue, receivedChecksum, sizeof(receivedChecksum)) == 0) {
        uint8_t id = processPacket(data, sizeof(data) / sizeof(data[0]));

        // Check if ID value is valid
        if (id >= 0 && id < MAX_IDS) {
          bool found = false;
          for (int i = 0; i < numIds; i++) {
            if (idData[i].id == id) {
              found = true;
              idData[i].time = millis(); // Update the last time this ID was received
              break;
            }
          }

          if (!found && numIds < MAX_IDS) {
            idData[numIds].id = id;
            idData[numIds].time = millis();
            numIds++;
          }
        }
        checksumReceived = false;
      } else {
        checksumReceived = false;
      }
    } else {
      // Invalid packet length or out-of-order transmission
      checksumReceived = false;
    }
  }
}

uint8_t processPacket(uint8_t *data, uint8_t dataLength) {
  uint8_t id = 0;
  for (uint8_t i = 0; i < dataLength; i++) {
    id |= data[i] << i; // convert DIP code to ID
  }
  return id;
}

void displayData() {
  display.clearDisplay();

  if (numIds == 0) {
    display.setCursor(0, 10);
    display.println("Waiting for");
    display.print("message...");
    setContrast(1);
  } else {
    // Dynamically adjust font size based on the number of IDs received
    int textSize;
    int idsPerRow = 4;
    if (numIds < 4) {
      textSize = 3;
    } else if (numIds < 8) {
      textSize = 2;
    } else {
      textSize = 1;
      idsPerRow = 6;
    }
    setContrast(255);
    display.setTextSize(textSize);

    const int idBoxWidth = 15 * textSize;
    int rowHeight = 8 * textSize;

    // Display IDs in rows and columns
    for (int i = 0; i < numIds; i++) {
      int row = i / idsPerRow;
      int col = i % idsPerRow;

      int x = col * idBoxWidth;
      int y = row * rowHeight;

      if (i == selectedId) {
        display.setTextColor(BLACK, WHITE); // inverted
      } else {
        display.setTextColor(WHITE, BLACK);
      }

      display.setCursor(x, y);
      display.print(idData[i].id);
    }

    // Display last seen time for the selected ID
    display.setTextSize(1);
    display.setTextColor(WHITE);
    int lastSeenMins = (millis() - idData[selectedId].time) / 60000;
    display.setCursor(SCREEN_WIDTH - 24, SCREEN_HEIGHT - 8);
    display.print(lastSeenMins);
    display.print("m");
  }

  // Blinking indicator
  indicatorOn = !indicatorOn;
  indicatorTime = millis();

  display.setCursor(0, SCREEN_HEIGHT - 8);
  if (indicatorOn) {
    display.print(".");
  } else {
    display.print(" ");
  }

  display.display();
}

void removeInactiveIds() {
  if (numIds == 0) {
    return;
  }
  for (int i = numIds - 1; i >= 0; i--) {
    if (millis() - idData[i].time > INACTIVE_TIME) {
      // Shift the remaining IDs and last seen times down by one position to overwrite the inactive ID
      for (int j = i + 1; j < numIds; j++) {
        idData[j - 1] = idData[j];
      }
      numIds--;
    }
  }
}

void setContrast(uint8_t newContrast) {
  display.ssd1306_command(SSD1306_SETCONTRAST);
  display.ssd1306_command(newContrast);
}