main/task_dcf.c

Sat, 21 Oct 2023 19:04:52 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 21 Oct 2023 19:04:52 +0200
changeset 6
60ae555eff0b
parent 5
676c38f52d08
permissions
-rw-r--r--

Change colors and intensity.

/**
 * @file task_dcf.c
 * @brief DCF77 task.
 */


#include "dcf77tx.h"


static const char *TAG = "task_dcf";


SemaphoreHandle_t               xSemaphoreDCF = NULL;		///< Semaphore DCF task.
EventGroupHandle_t              xEventGroupDCF;			///< Events DCF task.
DCF_State			*dcf_state = NULL;		///< Public state for other tasks.
esp_timer_handle_t		timerHandle;			///< Timer handler
int				impulseCount = 0;		///< 100 mSec transmit slices.
int8_t				impulseArray[61];		///< Pulses, 0 = no pulse, 1=100ms, 2=200ms
int				actualSecond = 0;		///< Current second to transmit.
time_t				dcf_now;			///< Current time to send.
struct tm			dcf_tm;				///< Local broken down time.


ledc_timer_config_t		ledc_timer = {
				    .speed_mode = LEDC_LOW_SPEED_MODE,		///< Use high speed timer
				    .timer_num = LEDC_TIMER_0,			///< Timer 0
				    .duty_resolution = LEDC_TIMER_10_BIT,	///< 10 bits resolution
				    .freq_hz = 77500,				///< 77.5 KHz
				    .clk_cfg = LEDC_AUTO_CLK			///< Auto select PWM clock
};

ledc_channel_config_t		dcf77_100tx = {					///< 100% Antenna power
				    .channel    = LEDC_CHANNEL_0,
				    .duty       = 0,				///< Default 0%
				    .gpio_num   = CONFIG_ANTENNA_100_PIN,	///< Antenna pin 100%
				    .speed_mode = LEDC_LOW_SPEED_MODE,
				    .hpoint     = 0,
				    .intr_type  = LEDC_INTR_DISABLE,
				    .timer_sel  = LEDC_TIMER_0			///< Timer 0
				};
ledc_channel_config_t		dcf77_15tx = {					///< 15% Antenna power
				    .channel    = LEDC_CHANNEL_1,
				    .duty	= 0,
				    .gpio_num   = CONFIG_ANTENNA_15_PIN,	///< Antenna pin 15%
				    .speed_mode = LEDC_LOW_SPEED_MODE,
				    .hpoint     = 0,
				    .intr_type  = LEDC_INTR_DISABLE,
				    .timer_sel  = LEDC_TIMER_0			///< Timer 0
				};


extern bool			System_TimeOk;


#define LED1			CONFIG_LED1_PIN
#define	LED2			CONFIG_LED2_PIN


const int TASK_DCF_REQUEST_START = BIT0;
const int TASK_DCF_REQUEST_STOP = BIT1;

const int TASK_DCF_RUN = BIT2;


bool ready_DCF(void)
{
    return dcf_state->DCF_running;
}



void request_DCF(bool run)
{
    ESP_LOGI(TAG, "request_DCF(%s)", run ? "start":"stop");

    if (run)
	xEventGroupSetBits(xEventGroupDCF, TASK_DCF_REQUEST_START);
    else
	xEventGroupSetBits(xEventGroupDCF, TASK_DCF_REQUEST_STOP);
}


int bin2bcd(int data)
{
    int	msb, lsb;

    if (data < 10)
	return data;
    msb = (data / 10) << 4;
    lsb = data % 10;
    return msb + lsb;
}


static void DCFout(void* arg);
void DCFout(void* arg)
{
    int		i;
    static int	tmp, tmpin, parity = 0;

    switch (impulseCount++) {
	case 0:		if (actualSecond == 0) {
			    time(&dcf_now);
			    dcf_now += 60;
			}
			/* Carrier to 15% */
			ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0);
			ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
			ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 512);
			ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
			if (impulseArray[actualSecond] == 1) {
			    set_ob_led(0, 5, 5);
			} else if (impulseArray[actualSecond] == 2) {
			    set_ob_led(5, 5, 0);
			}
			break;
	case 1:		if (impulseArray[actualSecond] == 1) {
			    /* Carrier back to 100% */
			    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 512);
    			    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
    			    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0);
    			    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
			    set_ob_led(0, 1, 0);
			}
			break;
	case 2:		/* Carrier back to 100% */
                        ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 512);
                        ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
                        ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0);
                        ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
			set_ob_led(0, 1, 0);
			break;
	case 9:		impulseCount = 0;
    			/*
    			 * To spread the CPU load, we set all bits during the first seconds
    			 * because we don't use these bits.
    			 */
    			switch (actualSecond) {
			    case 0:	/*
					 * Bit 0 is always 0.
					 * Bits 1..14 are used to transmit weather information.
					 * Just some fixed values here. 
					 * Bit 15, Antenna bit. 0 = normal operation, 1 = fault
					 */
				    	for (i = 0; i < 16; i++)
					    impulseArray[i] = 1;
					break;
			    case 1:	localtime_r(&dcf_now, &dcf_tm);
					char strftime_buf[64];
					strftime(strftime_buf, sizeof(strftime_buf), "%c", &dcf_tm);
					ESP_LOGI(TAG, "The current date/time to send is: %s", strftime_buf);
					break;
			    case 2:	/* DST bits */
					if (dcf_tm.tm_isdst == 0) {
					    impulseArray[17] = 1;	/* Set when DST is in effect.		*/
					    impulseArray[18] = 2;	/* Set when DST is not in effect.	*/
					} else {
					    impulseArray[17] = 2;
					    impulseArray[18] = 1;
					}
					/* Start of encoded time. Always set */
					impulseArray[20] = 2;
					break;
			    case 3:	/* announce DST on-off bit 16 */
					int month = dcf_tm.tm_mon + 1;
					bool announce = false;
					if (dcf_tm.tm_mday >= 25 || dcf_tm.tm_wday == 0) {
					    /* Last sunday in the month */
					    if (month == 3) {
						if (dcf_tm.tm_isdst == 0 && dcf_tm.tm_hour == 1 && dcf_tm.tm_min != 0) {
						    announce = true;	/* Wintertime to summertime */
						}
					    } else if (month == 10) {
						if (dcf_tm.tm_isdst > 0 && dcf_tm.tm_hour == 1 && dcf_tm.tm_min != 0) {
						    announce = true;	/* Summertime to wintertime */
						}
					    }
					}
					ESP_LOGI(TAG, "%d announce TZ change %s", dcf_tm.tm_isdst, announce ? "true":"false");
					impulseArray[16] = (announce) ? 2:1;
					break;
			    case 4:	/*
					 * We don't announce the leap second. It is not always sure when this will
					 * happen, possible at end of 2023, but it is not sure. And the next?
					 * SNTP timesync will deal with this and we will see a timejump.
					 */
					break;
			    case 5:	tmpin = bin2bcd(dcf_tm.tm_min);
					parity = 0;
					for (i = 21; i < 28; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					impulseArray[28] = (parity & 1) ? 2:1;
					ESP_LOGI(TAG, "minute %d%d%d%d%d%d%d  P1 %d", impulseArray[21], impulseArray[22], impulseArray[23],
							impulseArray[24], impulseArray[25], impulseArray[26], impulseArray[27], impulseArray[28]);
					break;
			    case 6:	tmpin = bin2bcd(dcf_tm.tm_hour);
					parity = 0;
					for (i = 29; i < 35; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					impulseArray[35] = (parity & 1) ? 2:1;
					ESP_LOGI(TAG, "hour   %d%d%d%d%d%d   P2 %d", impulseArray[29], impulseArray[30], impulseArray[31],
                                                        impulseArray[32], impulseArray[33], impulseArray[34], impulseArray[35]);
					break;
			    case 7:	tmpin = bin2bcd(dcf_tm.tm_mday);
					parity = 0;
					for (i = 36; i < 42; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					ESP_LOGI(TAG, "mday   %d%d%d%d%d%d", impulseArray[36], impulseArray[37], impulseArray[38],
                                                        impulseArray[39], impulseArray[40], impulseArray[41]);
					break;
			    case 8:	tmpin = bin2bcd(dcf_tm.tm_wday);
					if (tmpin == 0)
					    tmpin = 7;
					for (i = 42; i < 45; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					ESP_LOGI(TAG, "wday   %d%d%d", impulseArray[42], impulseArray[43], impulseArray[44]);
					break;
			    case 9:	tmpin = bin2bcd(dcf_tm.tm_mon + 1);
					for (i = 45; i < 50; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					ESP_LOGI(TAG, "month  %d%d%d%d%d", impulseArray[45], impulseArray[46], impulseArray[47],
                                                        impulseArray[48], impulseArray[49]);
					break;
			    case 10:	tmpin = bin2bcd(dcf_tm.tm_year - 100);
					for (int i = 50; i < 58; i++) {
					    tmp = tmpin & 1;
					    impulseArray[i] = tmp + 1;
					    parity += tmp;
					    tmpin >>= 1;
					}
					impulseArray[58] = (parity & 1) ? 2:1;
					ESP_LOGI(TAG, "year   %d%d%d%d%d%d%d%d P3 %d", impulseArray[50], impulseArray[51], impulseArray[52],
                                                impulseArray[53], impulseArray[54], impulseArray[55], impulseArray[56], impulseArray[57], impulseArray[58]);
					break;
    			}
			if (actualSecond < 59)	/* Can include leap second */
                            actualSecond++;
                        break;
    }

    if (actualSecond >= 59) {
	time_t	now = time(NULL);
	localtime_r(&now, &dcf_tm);
	if (dcf_tm.tm_sec == 0) {
	    actualSecond = impulseCount = 0;
	}
    }
}



void task_DCF(void *pvParameters)
{
    ESP_LOGI(TAG, "Starting DCF77");

    xEventGroupDCF = xEventGroupCreate();
    xSemaphoreDCF = xSemaphoreCreateMutex();
    dcf_state = malloc(sizeof(DCF_State));
    memset(dcf_state, 0x00, sizeof(DCF_State));

    esp_timer_create_args_t timerDCF = {
        .callback = &DCFout,
        .name = "DCF timer"
    };
    ESP_ERROR_CHECK(esp_timer_create(&timerDCF, &timerHandle));

    /*
     * Prepare the LEDC PWM channels
     */
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
    ESP_ERROR_CHECK(ledc_channel_config(&dcf77_100tx));
    ESP_ERROR_CHECK(ledc_channel_config(&dcf77_15tx));
    ESP_LOGI(TAG, "DCF77 antennas at pins %d and %d", CONFIG_ANTENNA_100_PIN, CONFIG_ANTENNA_15_PIN);
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);

    for (int i = 0; i < 59; i++)
	impulseArray[i] = 1;
    impulseArray[59] = impulseArray[60] = 0;

    xEventGroupClearBits(xEventGroupDCF, TASK_DCF_RUN);
    EventBits_t uxBits;

    for (;;) {
	uxBits = xEventGroupWaitBits(xEventGroupDCF, TASK_DCF_REQUEST_START | TASK_DCF_REQUEST_STOP, pdFALSE, pdFALSE, portMAX_DELAY );

	if (uxBits & TASK_DCF_REQUEST_START) {
	    if (dcf_state->DCF_running) {
		/* Already running */
	    } else {
		actualSecond = 0;
		impulseCount = 0;
	    	esp_timer_start_periodic(timerHandle, 100000);
		dcf_state->DCF_running = true;
	    }
	    xEventGroupClearBits(xEventGroupDCF, TASK_DCF_REQUEST_START);

	} else if (uxBits & TASK_DCF_REQUEST_STOP) {
	    esp_timer_stop(timerHandle);
	}
    } /* for(;;) */
}

mercurial