Skip to content

Commit

Permalink
Merge branch 'master' into support-triphase-historical-tic
Browse files Browse the repository at this point in the history
  • Loading branch information
lains committed Apr 20, 2024
2 parents 9cad098 + 051b6c6 commit 40b1ddd
Show file tree
Hide file tree
Showing 13 changed files with 856 additions and 200 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ On the STM32F769I-DISCO., I use USART6, the above pins are all available on the
* USART6 RX is on CN13, pin 1 (maps to PC7)

The software is currently configured to decode Linky data in standard TIC mode (9600 bauds), which is not the default built-in mode for Linky meters.
In order to switch to this more verbose mode (that also provides more data), you need to make a request to your energy vendor. As an alternative, the code can be slightly tweaked to switch to historical mode and to work at 1200 bauds (this is actually the default mode for Linky meters).
In order to switch to this more verbose mode (that also provides more data), you need to make a request to your energy vendor.
As an alternative, the code can be slightly tweaked to switch to historical mode and to work at 1200 bauds (this is actually the default mode for Linky meters).
In that case, replace 9600 by 1200 in source file [Stm32SerialDriver.cpp](https://github.com/lains/stm32-linky-display/blob/master/src/hal/Stm32SerialDriver.cpp) inside the function called `MX_USART_TIC_UART_Init()`.

In order to compile the code, this project uses:
* GNU Make (Build System)
Expand Down Expand Up @@ -162,4 +164,4 @@ The content of function `HAL_UART_MspInit()` should be moved from `stm32f7xx_hal

Also remember to redirect USART interruptions to the proper handler. (in `stm32fxxx_it.c`, `USARTx_IRQHandler(void)` should grab an external reference to the uart pointer via `get_huartx()` and call `HAL_UART_IRQHandler()`.
File `stm32xxx_it.c` should however be used almost as is (it has a `USARTx_IRQHandler()` defined).
You should try to replace arguments `&huartx` in the above function by `get_huartx()`` provided by my code.
You should try to replace arguments `&huartx` in the above function by `get_huartx()` provided by my code.
29 changes: 15 additions & 14 deletions inc/domain/PowerHistory.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#pragma once

#include "FixedSizeRingBuffer.h"
#include "TIC/DatasetView.h" // For TIC::Horodate
#include "TicProcessingContext.h"
#include "TicFrameParser.h" // For TicEvaluatedPower
#include "Timestamp.h"

struct PowerHistoryEntry {
PowerHistoryEntry();
PowerHistoryEntry(const TicEvaluatedPower& power, const TIC::Horodate& horodate);
PowerHistoryEntry(const TicEvaluatedPower& power, const Timestamp& timestamp);

#ifndef __UNIT_TEST__
private:
Expand All @@ -25,12 +25,13 @@ struct PowerHistoryEntry {
* @brief Update this object with an average between our current value and a new power measurement sample
*
* @param power A new power measurement sample to take into account
* @param timestamp The timestamp for the new @p power
*/
void averageWithPowerSample(const TicEvaluatedPower& power, const TIC::Horodate& horodate);
void averageWithPowerSample(const TicEvaluatedPower& power, const Timestamp& timestamp);

/* Attributes */
TicEvaluatedPower power; /*!< A power (in multiples or fractions of W... see scale below) */
TIC::Horodate horodate; /*!< The horodate for the @p power entry */
Timestamp timestamp; /*!< The timestamp for the @p power entry */
unsigned int nbSamples; /*!< The number of samples that have been averaged to produce the value in @p power */
unsigned int scale; /*!< A divider for @p power. If scale=1000, then power is represented in mW */
};
Expand Down Expand Up @@ -67,19 +68,19 @@ struct PowerHistory {
* @brief Method to invoke when new power data is retrieved
*
* @param power The power measurement
* @param horodate The horotate associated with the @p power
* @param timestamp The timestamp associated with the @p power
* @param frameSequenceNb The TIC frame sequence number
*/
void onNewPowerData(const TicEvaluatedPower& power, const TIC::Horodate& horodate, unsigned int frameSequenceNb);
void onNewPowerData(const TicEvaluatedPower& power, const Timestamp& timestamp, unsigned int frameSequenceNb);

/**
* @brief Check if two horodates are part of the same internal time resolution (and will thus be averaged to be stored in the same period history entry)
* @brief Check if two timestamps are part of the same internal time resolution (and will thus be averaged to be stored in the same period history entry)
*
* @param first The first horodate
* @param second The second horodate
* @param first The first timestamp
* @param second The second timestamp
* @return true If @p first and @p second end up in the same averaging period, and should thus be averaged to create one signel history entry
*/
bool horodatesAreInSamePeriodSample(const TIC::Horodate& first, const TIC::Horodate& second);
bool timestampsAreInSamePeriodSample(const Timestamp& first, const Timestamp& second);

/**
* @brief Get the averaging period value (in seconds)
Expand All @@ -100,25 +101,25 @@ struct PowerHistory {
* It is used as a callback provided to TIC::DatasetExtractor
*
* @param power The power measurement
* @param horodate The horotate associated with the @p power
* @param timestamp The timestamp associated with the @p power
* @param frameSequenceNb The TIC frame sequence number
* @param context A context as provided by TIC::DatasetExtractor, used to retrieve the wrapped TicFrameParser instance
*/
static void unWrapOnNewPowerData(const TicEvaluatedPower& power, const TIC::Horodate& horodate, unsigned int frameSequenceNb, void* context);
static void unWrapOnNewPowerData(const TicEvaluatedPower& power, const Timestamp& timestamp, unsigned int frameSequenceNb, void* context);

/**
* @brief Get the Last Values object
*
* @param[in,out] nb The max number of measurements requested, modified at return to represent the number of measurements actually retrieved
* @param[out] result A C-array of results, the first one being the most recent
*
* @note The horodate of the most recent entry can be retrieved in the first element of the result array (if nb!=0 at return)
* @note The timestamp of the most recent entry can be retrieved in the first element of the result array (if nb!=0 at return)
*/
void getLastPower(unsigned int& nb, PowerHistoryEntry* result) const;

/* Attributes */
FixedSizeRingBuffer<PowerHistoryEntry, 1024> data; /*!< The last n instantaneous power measurements */
AveragingMode averagingPeriod; /*!< Which sampling period do we record (we will perform an average on all samples within the period) */
TicProcessingContext* ticContext; /*!< An optional context structure instance that we should refresh on new power data reception */
TIC::Horodate lastPowerHorodate; /*!< The horodate of the last received power measurement */
Timestamp lastPowerTimestamp; /*!< The timestamp of the last received power measurement */
};
5 changes: 3 additions & 2 deletions inc/domain/TicFrameParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "TIC/DatasetExtractor.h"
#include "TIC/DatasetView.h"
#include "Timestamp.h"
#include "FixedSizeRingBuffer.h"

/* Forward declarations */
Expand Down Expand Up @@ -56,7 +57,7 @@ class TicMeasurements {

/* Attributes */
unsigned int fromFrameNb; /*!< The TIC frame ID from which the enclosed data has been extracted */
TIC::Horodate horodate; /*!< The optional horodate for the TIC frame */
Timestamp timestamp; /*!< An optional timestamp for the TIC frame */
unsigned int instVoltage; /*!< The instantaneous (ie within the last TIC frame) RMS voltage, in Volts */
unsigned int instAbsCurrent; /*!< The instantaneous (ie within the last TIC frame) absolute current, in Amps */
unsigned int maxSubscribedPower; /*!< The maximum allowed withdrawned power (subscribed), in Watts */
Expand All @@ -66,7 +67,7 @@ class TicMeasurements {
class TicFrameParser {
public:
/* Types */
typedef void(*FOnNewPowerData)(const TicEvaluatedPower& power, const TIC::Horodate& horodate, unsigned int frameId, void* context); /*!< The prototype of callbacks invoked on new power data */
typedef void(*FOnNewPowerData)(const TicEvaluatedPower& power, const Timestamp& timestamp, unsigned int frameId, void* context); /*!< The prototype of callbacks invoked on new power data */

/* Methods */
/**
Expand Down
75 changes: 75 additions & 0 deletions inc/domain/Timestamp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma once

#include "TIC/DatasetView.h" // For TIC::Horodate

class Timestamp {
private:
static constexpr unsigned int lastDayPerMonth[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public:
/* Methods */
Timestamp();

/**
* @brief Construct from hour, minute, seconds and optionally milliseconds
*/
Timestamp(unsigned int hour, unsigned int minute, unsigned int second, unsigned int millisecond = static_cast<unsigned int>(-1));

/**
* @brief Construct from month, day, hour, minute, seconds and optionally milliseconds
*/
Timestamp(unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int millisecond = static_cast<unsigned int>(-1));

/**
* @brief Construct from a TIC::Horodate
*/
Timestamp(const TIC::Horodate& from);

/**
* @brief Make the current horodate go forward a given seconds in time
*
* @param seconds The seconds to add
* @return 0 if the result is in the same day as the original value, otherwise, the number of days forward (the current instance will be updated up to hours, but days will remain unchanged in any case)
*/
unsigned int addSecondsWrapDay(unsigned int seconds);

private:
/**
* @brief Comparison of two timestamps
*
* @param other The other Timestamp to compare with
* @return int -1 is we are earlier than @other, 1 if we are later than @other and 0 if both are equal
*
* @note If one timestamp is invalid, it is considered as the origin of time, thus earlier (-1) than any valid timestamps
* If both timestamps are invalid, they are considered equal (0)
* @note We perform date comparison (day+month) if knownDate is true on timestamps
* If one date is unknown, it is considered as the origin of time, thus earlier (-1) than any valid timestamps
* @note We only compare milliseconds if available on both objects
*/
int timeStampCmp(const Timestamp& other) const;

public:
bool operator==(const Timestamp& other) const;
bool operator!=(const Timestamp& other) const;
bool operator<(const Timestamp& other) const;
bool operator>(const Timestamp& other) const;
bool operator<=(const Timestamp& other) const;
bool operator>=(const Timestamp& other) const;

#ifdef __TIC_LIB_USE_STD_STRING__
std::string toString() const;
#endif

public:
/* Attributes */
bool isValid; /*!< Does this instance contain a valid timestamp? */
bool estimatedTime; /*!< The timestamp was generated by a precise clock */
unsigned int month; /*!< The month */
unsigned int day; /*!< The day */
bool knownDate; /*!< Are the month+day valid? If false, the year will be invalid as well */
unsigned int hour; /*!< The hour */
unsigned int minute; /*!< The minute */
unsigned int second; /*!< The second */
unsigned int millisecond; /*!< The milliseconds */
bool knownMilliseconds; /*!< Is the millisecond attribute valid? */
bool absolute; /*!< Is the timestamp absolute, relative to the power up time or to the start of the current day? */
};
4 changes: 2 additions & 2 deletions src/domain/HistoryDraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void drawHistory(Stm32LcdDriver& lcd, uint16_t x, uint16_t y, uint16_t width, ui
}

if (thisSamplePower.isValid) {
/* Review the code below */
/* TODO: Review the code below */
if (thisSamplePower.maxValue > 0) { /* Positive value, even if range, display the highest value of the range (worst case) */
uint16_t thisSampleTopAbsoluteY = 0;
uint16_t thisSampleBottomAbsoluteY = 0;
Expand Down Expand Up @@ -252,7 +252,7 @@ void drawHistory(Stm32LcdDriver& lcd, uint16_t x, uint16_t y, uint16_t width, ui
if (gridX > x)
lcd.drawVerticalLine(gridX-1, y, height, Stm32LcdDriver::Black); /* Draw each hour, with a double line */
}
else if (fiveMinStep % 4 == 0) /* gridX is pointing to the edge of a quarter of an hour */
else if (fiveMinStep % 3 == 0) /* gridX is pointing to the edge of a quarter of an hour */
lcd.drawVerticalLine(gridX, y, height, Stm32LcdDriver::Black); /* Draw each other quarter */
else
lcd.drawVerticalLine(gridX, y, height, Stm32LcdDriver::LightGrey); /* Draw each other step of 5 mins */
Expand Down
Loading

0 comments on commit 40b1ddd

Please sign in to comment.