Files
OpenEPaperLink/tag_fw/main.c
2023-03-11 13:39:22 +01:00

445 lines
13 KiB
C

#define __packed
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asmUtil.h"
#include "comms.h" // for mLastLqi and mLastRSSI
#include "eeprom.h"
#include "powermgt.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
#include "screen.h"
#include "settings.h"
#include "syncedproto.h"
#include "timer.h"
#include "userinterface.h"
#include "wdt.h"
// #define DEBUG_MODE
void eventMode();
void displayLoop() {
while (1) {
wdt120s();
pr("Splash screen\n");
powerUp(INIT_EPD);
showSplashScreen();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt120s();
pr("Event screen1\n");
powerUp(INIT_EPD);
eventUpdateScreen();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt120s();
pr("Event screen2\n");
powerUp(INIT_EPD);
eventScreen();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
}
pr("Splash screen\n");
powerUp(INIT_EPD);
showSplashScreen();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
pr("Update screen\n");
powerUp(INIT_EPD);
showApplyUpdate();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt60s();
pr("Scanning screen - ");
powerUp(INIT_EPD);
showScanningWindow();
timerDelay(TIMER_TICKS_PER_SECOND * 8);
for (uint8_t i = 0; i < 5; i++) {
for (uint8_t c = 0; c < 16; c++) {
addScanResult(11 + c, 2 * i + 60 + c);
}
pr("redraw... ");
draw();
}
pr("\n");
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt30s();
pr("AP Found\n");
powerUp(INIT_EPD);
showAPFound();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt30s();
pr("AP NOT Found\n");
powerUp(INIT_EPD);
showNoAP();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt30s();
pr("Longterm sleep screen\n");
powerUp(INIT_EPD);
showLongTermSleep();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt30s();
pr("NO EEPROM\n");
powerUp(INIT_EPD);
showNoEEPROM();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdt30s();
pr("NO EEPROM\n");
powerUp(INIT_EPD);
showNoMAC();
timerDelay(TIMER_TICKS_PER_SECOND * 4);
wdtDeviceReset();
}
uint8_t showChannelSelect() { // returns 0 if no accesspoints were found
uint8_t __xdata result[16];
memset(result, 0, sizeof(result));
showScanningWindow();
powerUp(INIT_RADIO);
for (uint8_t i = 0; i < 5; i++) {
for (uint8_t c = 11; c < 27; c++) {
if (detectAP(c)) {
if (mLastLqi > result[c - 11]) result[c - 11] = mLastLqi;
pr("Channel: %d - LQI: %d RSSI %d\n", c, mLastLqi, mLastRSSI);
}
if (eventModeActive) {
// if event mode became active, skip the rest of the scan and go straight to event mode
epdWaitRdy();
powerDown(INIT_RADIO);
// skip the rest of the scan if
return c;
}
}
epdWaitRdy();
for (uint8_t c = 0; c < 16; c++) {
addScanResult(11 + c, result[c]);
}
drawNoWait();
}
uint8_t __xdata highestLqi = 0;
uint8_t __xdata highestSlot = 0;
for (uint8_t c = 0; c < sizeof(result); c++) {
if (result[c] > highestLqi) {
highestSlot = c + 11;
highestLqi = result[c];
}
}
powerDown(INIT_RADIO);
epdWaitRdy();
mLastLqi = highestLqi;
return highestSlot;
}
uint8_t channelSelect() { // returns 0 if no accesspoints were found
uint8_t __xdata result[16];
memset(result, 0, sizeof(result));
for (uint8_t i = 0; i < 2; i++) {
for (uint8_t c = 11; c < 27; c++) {
if (detectAP(c)) {
if (mLastLqi > result[c - 11]) result[c - 11] = mLastLqi;
}
}
}
uint8_t __xdata highestLqi = 0;
uint8_t __xdata highestSlot = 0;
for (uint8_t c = 0; c < sizeof(result); c++) {
if (result[c] > highestLqi) {
highestSlot = c + 11;
highestLqi = result[c];
}
}
mLastLqi = highestLqi;
return highestSlot;
}
void main() {
setupPortsInitial();
powerUp(INIT_BASE | INIT_UART);
// displayLoop(); // remove me
if (RESET & 0x01) {
wakeUpReason = WAKEUP_REASON_WDT_RESET;
pr("WDT reset!\n");
} else {
wakeUpReason = WAKEUP_REASON_FIRSTBOOT;
}
wdt10s();
boardGetOwnMac(mSelfMac);
{
bool __xdata macSet = false;
for (uint8_t __xdata c = 0; c < 8; c++) {
if (mSelfMac[c] != 0xFF) {
macSet = true;
break;
}
}
if (!macSet) {
pr("Mac can't be all FF's.\n");
powerUp(INIT_EPD);
showNoMAC();
powerDown(INIT_EPD | INIT_UART | INIT_EEPROM);
doSleep(-1);
wdtDeviceReset();
}
}
pr("BOOTED> %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]);
pr("%02X%02X", mSelfMac[2], mSelfMac[3]);
pr("%02X%02X", mSelfMac[4], mSelfMac[5]);
pr("%02X%02X\n", mSelfMac[6], mSelfMac[7]);
powerUp(INIT_RADIO); // load down the battery using the radio to get a good voltage reading
powerUp(INIT_EPD_VOLTREADING | INIT_TEMPREADING);
powerDown(INIT_RADIO);
powerUp(INIT_EEPROM);
// get the highest slot number, number of slots
initializeProto();
powerDown(INIT_EEPROM);
// show the splashscreen
powerUp(INIT_EPD);
showSplashScreen();
powerUp(INIT_EPD);
wdt30s();
currentChannel = showChannelSelect();
wdt10s();
if (eventModeActive) {
// skip straight to event mode
eventMode();
}
if (currentChannel) {
showAPFound();
initPowerSaving(INTERVAL_BASE);
powerDown(INIT_EPD | INIT_UART);
doSleep(5000UL);
} else {
showNoAP();
initPowerSaving(INTERVAL_AT_MAX_ATTEMPTS);
powerDown(INIT_EPD | INIT_UART);
doSleep(120000UL);
}
while (1) {
powerUp(INIT_UART);
wdt10s();
if (currentChannel) {
// associated
struct AvailDataInfo* __xdata avail;
// Is there any reason why we should do a long (full) get data request (including reason, status)?
if ((longDataReqCounter > LONG_DATAREQ_INTERVAL) || wakeUpReason != WAKEUP_REASON_TIMED) {
// check if we should do a voltage measurement (those are pretty expensive)
if (voltageCheckCounter == VOLTAGE_CHECK_INTERVAL) {
powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading
powerUp(INIT_TEMPREADING | INIT_EPD_VOLTREADING);
powerDown(INIT_RADIO);
voltageCheckCounter = 0;
} else {
powerUp(INIT_TEMPREADING);
}
voltageCheckCounter++;
// check if the battery level is below minimum, and force a redraw of the screen
if ((lowBattery && !lowBatteryShown) || (noAPShown)) {
// Check if we were already displaying an image
if (curImgSlot != 0xFF) {
powerUp(INIT_EEPROM | INIT_EPD);
wdt60s();
drawImageFromEeprom(curImgSlot);
powerDown(INIT_EEPROM | INIT_EPD);
} else {
powerUp(INIT_EPD);
showAPFound();
powerDown(INIT_EPD);
}
}
powerUp(INIT_RADIO);
avail = getAvailDataInfo();
powerDown(INIT_RADIO);
if (avail != NULL) {
// we got some data!
longDataReqCounter = 0;
// since we've had succesful contact, and communicated the wakeup reason succesfully, we can now reset to the 'normal' status
wakeUpReason = WAKEUP_REASON_TIMED;
}
} else {
powerUp(INIT_RADIO);
avail = getShortAvailDataInfo();
powerDown(INIT_RADIO);
}
addAverageValue();
if (avail == NULL) {
// no data :(
nextCheckInFromAP = 0; // let the power-saving algorithm determine the next sleep period
} else {
nextCheckInFromAP = avail->nextCheckIn;
// got some data from the AP!
if (avail->dataType != DATATYPE_NOUPDATE) {
// data transfer
if (processAvailDataInfo(avail)) {
// succesful transfer, next wake time is determined by the NextCheckin;
} else {
// failed transfer, let the algorithm determine next sleep interval (not the AP)
nextCheckInFromAP = 0;
}
} else {
// no data transfer, just sleep.
}
}
uint16_t nextCheckin = getNextSleep();
longDataReqCounter += nextCheckin;
if (nextCheckin == INTERVAL_AT_MAX_ATTEMPTS) {
// disconnected, obviously...
currentChannel = 0;
}
// if the AP told us to sleep for a specific period, do so.
if (nextCheckInFromAP) {
doSleep(nextCheckInFromAP * 60000UL);
} else {
doSleep(getNextSleep() * 1000UL);
}
} else {
// not associated
if (((scanAttempts != 0) && (scanAttempts % VOLTAGEREADING_DURING_SCAN_INTERVAL == 0)) || (scanAttempts > (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))) {
powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading
powerUp(INIT_EPD_VOLTREADING);
powerDown(INIT_RADIO);
}
// try to find a working channel
powerUp(INIT_RADIO);
currentChannel = channelSelect();
powerDown(INIT_RADIO);
if ((!currentChannel && !noAPShown) || (lowBattery && !lowBatteryShown) || (scanAttempts == (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) {
powerUp(INIT_EPD);
wdt60s();
if (curImgSlot != 0xFF) {
powerUp(INIT_EEPROM);
drawImageFromEeprom(curImgSlot);
powerDown(INIT_EEPROM);
} else if ((scanAttempts >= (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) {
showLongTermSleep();
} else {
showNoAP();
}
powerDown(INIT_EPD);
}
// did we find a working channel?
if (currentChannel) {
// now associated!
scanAttempts = 0;
wakeUpReason = WAKEUP_REASON_NETWORK_SCAN;
initPowerSaving(INTERVAL_BASE);
doSleep(getNextSleep() * 1000UL);
} else {
// still not associated
doSleep(getNextScanSleep(true) * 1000UL);
}
}
}
}
#define EVENT_POLL_INTERVAL 5000
uint8_t __xdata eventDataID = 0;
void eventMode() {
powerUp(INIT_EPD);
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
selectLUT(EPD_LUT_FAST_NO_REDS);
clearScreen();
epdPrintBegin(0, 60, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("EventMode");
epdPrintEnd();
drawWithSleep();
powerDown(INIT_EPD);
doSleep(1000);
uint16_t __xdata failedCount = 0;
// display welcome!
while (failedCount < 17280) { // 24 hours at 5 second intervals
wdt10s();
powerUp(INIT_RADIO | INIT_UART);
struct eventData* __xdata ed = getEventData();
powerDown(INIT_RADIO);
if (ed == NULL) {
failedCount++;
} else {
// eventdata is copied to blockXferBuffer, gets picked up from
failedCount = 0;
// check if should display this data, and make it available to other tags
if ((ed->eventDataID > eventDataID) || (ed->eventDataID - eventDataID > 128)) {
eventDataID = ed->eventDataID;
// display event logo while we run the AP (we could just skip straight to showing the data, but where's the fun in that)
powerUp(INIT_EPD);
eventUpdateScreen();
wdt30s();
// enter AP mode
pr("AP enabled\n");
powerUp(INIT_RADIO);
eventAPMode();
powerDown(INIT_RADIO);
pr("AP disabled\n");
// for good measure, check if the EPD was ready with the picture
epdWaitRdy();
wdt10s();
// display new data
eventScreen();
powerDown(INIT_EPD);
} else {
// ignore
}
}
doSleep(EVENT_POLL_INTERVAL);
}
// display thank you blah
while (1) {
// sleep forever
doSleep(-1);
}
}