diff --git a/firmware/.gitignore b/firmware/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/firmware/src/credit.hpp b/firmware/src/credit.hpp index 921843f5..a7eaa452 100644 --- a/firmware/src/credit.hpp +++ b/firmware/src/credit.hpp @@ -1,5 +1,4 @@ #pragma once - // defines // Arduino base libraries diff --git a/firmware/src/helpers.hpp b/firmware/src/helpers.hpp index f49a9e1c..4251033e 100644 --- a/firmware/src/helpers.hpp +++ b/firmware/src/helpers.hpp @@ -95,3 +95,48 @@ void urgeent() { } } } + +uint32_t convertArrayToUint32(int arrayToConvert[9]) { + int i = 0; + uint32_t uint32 = 0; + for (i = 0; i < 9; i++) { + uint32 = 10 * uint32 + arrayToConvert[i]; + } + return (uint32); +} + +uint32_t convertByteArrayToUint32(uint8_t arrayToConvert[4]) { + uint32_t low1 = arrayToConvert[0]; + uint32_t low2 = arrayToConvert[1]; + low2 = low2 << 8; + uint32_t high1 = arrayToConvert[2]; + high1 = high1 << 16; + uint32_t high2 = arrayToConvert[3]; + high2 = high2 << 24; + uint32_t result = low1 + low2 + high1 + high2; + return (result); +} + +void convertUint32ToUint8Array(uint32_t uint32ToConvert, + uint8_t arrayBytes[4]) { + byte low1 = uint32ToConvert; + byte low2 = uint32ToConvert >> 8; + byte high1 = uint32ToConvert >> 16; + byte high2 = uint32ToConvert >> 24; + arrayBytes[0] = low1; + arrayBytes[1] = low2; + arrayBytes[2] = high1; + arrayBytes[3] = high2; +} + +void storeUint32InNvram(uint32_t toStore, int address) { + uint8_t arrayBytes[4]; + convertUint32ToUint8Array(toStore, arrayBytes); + rtc.writenvram(address, arrayBytes, 4); +} + +uint32_t readUint32FromNvram(int address) { + uint8_t readData[4] = {0}; + rtc.readnvram(readData, 4, address); + return (convertByteArrayToUint32(readData)); +} diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 78b15e8f..2e74e247 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,11 +1,11 @@ // defines // Arduino base libraries -#include -#include "Arduino.h" +// #include // third party libraries -#include +// #include +// #include // OpenSmartMeter libraries #include "credit.hpp" @@ -14,6 +14,7 @@ #include "lcd_display.hpp" #include "lcd_init.hpp" #include "mem_init.hpp" +#include "opaygo_functions.hpp" #include "power.hpp" #include "relay.hpp" #include "remote.hpp" @@ -22,6 +23,17 @@ #include "time_management.hpp" #include "token_management.hpp" +extern "C" { +#include "opaygo_decoder.h" +} + +uint64_t InputToken; +TokenData Output; +uint32_t StartingCode = 123456789; +unsigned char SECRET_KEY[16] = {0xa2, 0x9a, 0xb8, 0x2e, 0xdc, 0x5f, 0xbb, 0xc4, + 0x1e, 0xc9, 0x53, 0xf, 0x6d, 0xac, 0x86, 0xb1}; +// char SECRET_KEY[16] = {...}; + HardwareSerial Serial2(PA3, PA2); byte fe1[8] = {0b00011, 0b00011, 0b00011, 0b00011, @@ -110,7 +122,7 @@ void setup() { lcd.print("RTC is NOT running!"); delay(2000); } - + initializeTime(); lcd.setCursor(0, 0); lcd.print("CSOne : "); lcd.setCursor(8, 0); @@ -134,23 +146,44 @@ void setup() { } delay(10); relay_on(); - if (is_STSmode) { #if defined(TIM1) - TIM_TypeDef* Instance = TIM1; + TIM_TypeDef* Instance = TIM1; #else - TIM_TypeDef* Instance = TIM2; + TIM_TypeDef* Instance = TIM2; #endif - HardwareTimer* MyTim = new HardwareTimer(Instance); - MyTim->setOverflow(20, HERTZ_FORMAT); - MyTim->attachInterrupt(urgeent); - MyTim->resume(); - } else { - printf("OpenPAYGO code written here"); + HardwareTimer* MyTim = new HardwareTimer(Instance); + MyTim->setOverflow(20, HERTZ_FORMAT); + switch (Mode_select) { + case 0: + lcd.clear(); + lcd.setCursor(0, 0); + lcd.println("No Configuration! "); + while (Mode_select == 0) // wait for mode configuration + { + STS_keypad(); + delay(20); + } + break; + + case 1: + MyTim->attachInterrupt(urgeent); + MyTim->resume(); + break; + + case 2: + /*OpenPayGo Token initializing code; */ + printf("Welcome to the OPAYGO Device\n"); + printf( + "We're waiting for the * character to start recording the key " + "presses.\n(Press the '#' key to see the device activation " + "status)\n\n"); + LoadActivationVariables(); // We load the activation variableS + break; } } void loop() { - if (is_STSmode) { + if (Mode_select == 1) { mesure(); if ((mains_input_value > 50)) { credit_reminder(); @@ -163,7 +196,24 @@ void loop() { if ((sts_mode == 0) && (mains_input_value > 50)) { gsm_func(); } - } else { - printf("OpenPAYGO code written here"); + } + + if (Mode_select == 2) { + // We wait for a token to be entered + InputToken = WaitForTokenEntry(); + // We get the activation value from the token + + Output = GetDataFromToken( + InputToken, &TokenCount, &UsedTokens, StartingCode, + SECRET_KEY); // We get the activation value from the token + + printf("\n(Token entered: %llu)", InputToken); + printf("\n(Activation Value from Token: %d)", + Output.Value); // Activation Value found in the token + printf("\n(Count: %d)", Output.Count); // Count found in the token + printf("\n(Max Count: %d)", TokenCount); // Count found in the token + printf("\n(Used Tokens: %d)\n", UsedTokens); // Count found in the token + + UpdateDeviceStatusFromTokenValue(Output.Value, Output.Count); } } diff --git a/firmware/src/opaygo_functions.hpp b/firmware/src/opaygo_functions.hpp new file mode 100644 index 00000000..92caf57f --- /dev/null +++ b/firmware/src/opaygo_functions.hpp @@ -0,0 +1,313 @@ +#pragma once + +// OpenSmartMeter libraries +#include "global_defines.hpp" +#include "helpers.hpp" +#include "mem_init.hpp" +#include "time_management.hpp" + +// Arduino base libraries +#include +#include "Arduino.h" +// #include + +extern "C" { +#include "opaygo_decoder.h" +} + +#define BLINK_PERIOD 250 +#define DEBUG + +#define STAR_KEY -1 +#define HASH_KEY -2 +#define NON_ACCEPTED_KEY -3 + +#define TIME_DIVIDER 1 +// #define RESTRICTED_DIGIT_SET_MODE + +#ifdef RESTRICTED_DIGIT_SET_MODE +# define TOKEN_LENGTH 15 +#else +# define TOKEN_LENGTH 9 +#endif + +// Device parameters location in Flash/EEPROM +unsigned int TokenCount_eeprom_location = 6; +unsigned int UsedTokens_eeprom_location = 8; +unsigned int PAYGEnabled_eeprom_location = 10; +unsigned int ActiveUntil_eeprom_location = 11; +unsigned int TokenEntryLockedUntil_eeprom_location = 15; +unsigned int LAST_TIME_STAMP_ADDRESS = 27; +unsigned int NB_DISCONNECTIONS_ADDRESS = 32; + +// Device parameters (to be stored in Flash/EEPROM) +uint16_t TokenCount = 1; +uint16_t UsedTokens = 0; +bool PAYGEnabled = true; +uint32_t ActiveUntil = 0; +uint32_t TokenEntryLockedUntil = 0; +uint8_t nbDisconnections = 0; + +int InvalidTokenCount = 0; + +void LoadActivationVariables() { + TokenCount = + mem.readInt(TokenCount_eeprom_location); // We load TokenCount (& + // UsedTokens if needed) + UsedTokens = + mem.readInt(UsedTokens_eeprom_location); // We load UsedTokens if needed + PAYGEnabled = mem.read( + PAYGEnabled_eeprom_location); // We load PAYGEnabled //Verify this syntax + ActiveUntil = + mem.readLong(ActiveUntil_eeprom_location); // We load ActiveUntil + TokenEntryLockedUntil = mem.readLong( + TokenEntryLockedUntil_eeprom_location); // We load TokenEntryLockedUntil +} + +void StoreActivationVariables() { + mem.writeInt(TokenCount_eeprom_location, + TokenCount); // We store TokenCount (& UsedTokens if needed) + mem.writeInt(UsedTokens_eeprom_location, + UsedTokens); // We store UsedTokens if needed + mem.write(PAYGEnabled_eeprom_location, + PAYGEnabled); // We store PAYGEnabled //Verify this syntax + mem.writeLong(ActiveUntil_eeprom_location, + ActiveUntil); // We store ActiveUntil + mem.writeLong(TokenEntryLockedUntil_eeprom_location, + TokenEntryLockedUntil); // We store TokenEntryLockedUntil +} + +void storeTimeStampEEPROM(uint32_t timeStampInSeconds) { + mem.writeInt(LAST_TIME_STAMP_ADDRESS, timeStampInSeconds); +} + +void storeNbDisconnectionsEEPROM() { + mem.writeInt(NB_DISCONNECTIONS_ADDRESS, nbDisconnections); +} + +void incrementNbDisconnectionsEeprom() { + Serial.println("Disconnection spotted!!"); // will be displayed if DEBUG_MODE + // is uncommented + nbDisconnections = + mem.readInt(NB_DISCONNECTIONS_ADDRESS); // just to be sure we have the + // proper nb of disconnections + nbDisconnections++; + storeNbDisconnectionsEEPROM(); +} + +void ChangeLedState(int ledPin) { + digitalRead(ledPin) == LOW ? digitalWrite(ledPin, HIGH) + : digitalWrite(ledPin, LOW); +} + +void BlinkLED(int LedPin, int NumberOfBlinks, int BlinkPeriode) { + int i; + for (i = 0; i < NumberOfBlinks; i++) { + ChangeLedState(LedPin); + delay(BlinkPeriode); + ChangeLedState(LedPin); + delay(BlinkPeriode); + } +} + +void BlinkRedLED(int NumberOfBlinks, int BlinkPeriode) { + BlinkLED(red_led, NumberOfBlinks, BlinkPeriode); +} + +void BlinkGreenLED(int NumberOfBlinks, int BlinkPeriode) { + BlinkLED(green_led, NumberOfBlinks, BlinkPeriode); +} + +uint32_t GetTimeInSeconds() { + /* + *Returns the unixtime in seconds of Now + */ + DateTime now = rtc.now(); + uint32_t nowInSeconds = + (now.unixtime() - + timeInitializationRtc); // we substract the init time so that it's + // easier to read when debugging, and works the + // same as Arduino Time mgt + return (nowInSeconds); +} + +bool TokenEntryAllowed() { + if (TokenEntryLockedUntil > GetTimeInSeconds()) { + return false; + } else { + return true; + } +} + +int GetKeyPressed() +/* + * returns the key pressed on the Keypad remote as a int (this function forces + * to put the key in a char, good practice) + */ +{ + char incomingByte; + incomingByte = customKeypad.getKey(); + Serial.print("incoming byte = "); + Serial.println(incomingByte); + switch (incomingByte) { + case '*': + return STAR_KEY; + break; + + case '#': + return HASH_KEY; + break; + + default: + return (int)(incomingByte - '0'); // this_char is now an int + break; + } +} + +void UpdateInvalidTokenWaitingPeriod() { + uint32_t Now = GetTimeInSeconds(); + + // We check that it does not become unbearably long + if (InvalidTokenCount > 11) { + InvalidTokenCount = 11; + } + + // We add some forgiveness for the first 2 errors + if (InvalidTokenCount > 2) { + TokenEntryLockedUntil = Now + pow(2, InvalidTokenCount - 2) * 60; + } +} + +void AddTime(int ActivationValue) { + uint32_t Now = GetTimeInSeconds(); + int NumberOfSecondsToActivate = (ActivationValue * 3600 * 24) / TIME_DIVIDER; + + if (ActiveUntil < Now) { + ActiveUntil = Now; + } + + ActiveUntil += + NumberOfSecondsToActivate; // We add the number of days (converted in + // seconds for to compare to our RTC time) +} + +void SetTime(int ActivationValue) { + uint32_t Now = GetTimeInSeconds(); + int NumberOfSecondsToActivate = (ActivationValue * 3600 * 24) / TIME_DIVIDER; + + ActiveUntil = + Now + + NumberOfSecondsToActivate; // We set the number of days (converted in + // seconds for to compare to our RTC time) +} + +void UpdateDeviceStatusFromTokenValue(int TokenValue, int ActivationCount) { + if (TokenValue == -1) { + InvalidTokenCount++; + UpdateInvalidTokenWaitingPeriod(); + BlinkRedLED(10, BLINK_PERIOD); + } else if (TokenValue == -2) { + BlinkGreenLED(1, BLINK_PERIOD); // We blink the green LED once to show that + // the token was valid but isnt anymore + } else { + InvalidTokenCount = 0; + if (TokenValue == COUNTER_SYNC_VALUE) { + BlinkGreenLED( + 3, + BLINK_PERIOD); // We blink green twice to show that the token is good + } else if (TokenValue == PAYG_DISABLE_VALUE) { + PAYGEnabled = false; + BlinkGreenLED(5, BLINK_PERIOD); // We blink green twice to show that the + // device is active forever + } else { + if (ActivationCount % 2) { + PAYGEnabled = true; + SetTime(TokenValue); + } else { + AddTime(TokenValue); + } + BlinkGreenLED( + 2, + BLINK_PERIOD); // We blink green twice to show that the token is good + } + StoreActivationVariables(); // We store in every case + } +} + +bool IsActive() { + if (PAYGEnabled) { + if (ActiveUntil > GetTimeInSeconds()) { + return true; + } else { + return false; + } + } else { + return true; + } +} + +uint64_t WaitForTokenEntry() { + uint64_t TempToken = 0; + bool NoToken = true; + int LastKey; + + while (NoToken) { + LastKey = GetKeyPressed(); + if (LastKey == STAR_KEY) { + if (TokenEntryAllowed()) { + NoToken = false; + } else { + BlinkRedLED(1, BLINK_PERIOD); +#ifdef DEBUG + printf("\nToken entry locked for %" PRIu32 "seconds", + TokenEntryLockedUntil - GetTimeInSeconds()); +#endif + } + } else if (LastKey == HASH_KEY) { + if (IsActive()) { + BlinkGreenLED(1, BLINK_PERIOD); +#ifdef DEBUG + printf("\nTime Left: %" PRIu32 "seconds", + ActiveUntil - GetTimeInSeconds()); +#endif + } else { + BlinkRedLED(1, BLINK_PERIOD); + } + } + } + for (int i = 0; i < TOKEN_LENGTH; i++) { + // We add the last key pressed to the token (as integer) if needed + TempToken += GetKeyPressed() * pow(10, (TOKEN_LENGTH - 1) - i); + } + return TempToken; +} + +void initializeTime() { + if (!rtc.begin()) { + Serial.println("Couldn't find RTC"); + while (1) + ; + } + + if (!rtc.isrunning()) { + Serial.println("RTC is NOT running!"); + } + + // We check that it is not a disconnection power - Arduino + uint32_t nvramCheck = readUint32FromNvram(TIME_INITIALIZATION_NVRAM_ADDRESS); + if (nvramCheck == 0) { // it is the first setup of the Arduino + rtc.adjust( + DateTime(F(__DATE__), F(__TIME__))); // sets the RTC to the date & time + // this sketch was compiled + DateTime now = rtc.now(); + timeInitializationRtc = now.unixtime(); + storeUint32InNvram(timeInitializationRtc, + TIME_INITIALIZATION_NVRAM_ADDRESS); + } else { // a disconnection happened + timeInitializationRtc = nvramCheck; + incrementNbDisconnectionsEeprom(); + storeTimeStampEEPROM( + GetTimeInSeconds()); // to avoid counting twice the disconnection if + // the previous TimeStamp was too long ago + } +} diff --git a/firmware/src/sts_token.hpp b/firmware/src/sts_token.hpp index f4f9a9ec..5dc00c98 100644 --- a/firmware/src/sts_token.hpp +++ b/firmware/src/sts_token.hpp @@ -18,7 +18,7 @@ int private_stskey = 109; byte sts_accept = 0; byte c_chek = 0; -int convertedsts_data = 0; +unsigned int convertedsts_data = 0; unsigned long convertedsts_day = 0; unsigned long eeprom_sts_data = 0; @@ -48,7 +48,7 @@ void STStoken_decode() { String sts_meter_no_count = sts_data.substring(4, 5); convertedsts_data = sts_data.toInt(); - int stsnew_meter_no_count = sts_meter_no_count.toInt(); + unsigned int stsnew_meter_no_count = sts_meter_no_count.toInt(); // int stsnew_meter_no_count = sts_meter_no_count; String sts_day = sts_data.substring(6, 8); convertedsts_day = sts_day.toInt(); @@ -58,8 +58,8 @@ void STStoken_decode() { String identifier = sts_third.substring(6, 7); String sts_encode = sts_data.substring(15, 20); long tariff_MT_NO = sts_encode.toInt(); - long tariff_gotten = ((tariff_MT_NO) - (meter_no * multiplier)); - long trueMT_NO = tariff_MT_NO - tariff; + // long tariff_gotten = ((tariff_MT_NO) - (meter_no * multiplier)); + unsigned long trueMT_NO = tariff_MT_NO - tariff; trueMT_NO = trueMT_NO / multiplier; if ((confirmkey == private_stskey) && diff --git a/firmware/src/time_management.hpp b/firmware/src/time_management.hpp index 75225814..638398ba 100644 --- a/firmware/src/time_management.hpp +++ b/firmware/src/time_management.hpp @@ -12,6 +12,10 @@ #include "thingsboard.hpp" RTC_DS1307 rtc; +#define ACTIVE_UNTIL_NVRAM_ADDRESS \ + 0 // 56 bytes address from 0 to 55 in the NVRAM +#define TIME_INITIALIZATION_NVRAM_ADDRESS 4 +uint32_t timeInitializationRtc = 0; unsigned int hours, minutes, seconds, rtcday = 0; long nw_month_cnt, rtcmonth, rtcnewmonth, billing_date = 0; diff --git a/firmware/src/token_management.hpp b/firmware/src/token_management.hpp index 8d0cade2..0d26a33b 100644 --- a/firmware/src/token_management.hpp +++ b/firmware/src/token_management.hpp @@ -34,6 +34,7 @@ byte data_count = 0; byte dt = 0; byte parameters = 0; byte token_used = 0; +String password = "1234"; // for meter unsigned long sts_eeprom_fetched = 0; @@ -42,6 +43,7 @@ unsigned long eeprom_location_cnt = 40; unsigned long sts_value = 0; unsigned long sts_mode = 0; +unsigned long Mode_select = 0; void buss() { digitalWrite(buzzer, HIGH); @@ -90,11 +92,13 @@ void STS_keypad() { sts_value = 0; delay(20); lcd.clear(); - buss(); + buss(); // emit a sound to inform that it is ready to receive the token lcd.setCursor(0, 0); sts_mode = 1; if (customKeypad.getState() == HOLD) { - lcd.println("Password:"); + lcd.println("Password: "); + } else { + lcd.print("TOKEN: "); } } @@ -135,27 +139,31 @@ void STS_keypad() { parameters = parameters + 1; } - if (customKey == '#' && sts_mode == 1 && data_count > 19) { + if (customKey == '#' && sts_mode == 1) { sts_data = Data; - check_tokenused(); - if (token_used == 0) { - STStoken_decode(); - } - } - if (data_count < 19 && data_count > 3) { - if (sts_data == "1234") { - lcd.clear(); - lcd.setCursor(0, 0); - lcd.print("MODE: "); - data_count = 0; - lcd_count = 8; - dt = 0; - } - if (sts_data == "112") { - is_STSmode = true; + if (data_count > 19) { + check_tokenused(); + if (token_used == 0) { + STStoken_decode(); + } } - if (sts_data == "122") { - is_STSmode = false; + + if (data_count < 19 && data_count > 3) { + if (sts_data == password) { + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("MODE: "); + data_count = 0; + lcd_count = 8; + dt = 0; + } + if (sts_data == "112") { + Mode_select = 1; + } + + if (sts_data == "122") { + Mode_select = 2; + } } } }