Die Sourcecodeseite des S100 Wakeupboards

Die Mikrocontrollerfirmware main.c

/*
* Author: Michael Albert
* 11.03.2008

* Configure your ttyS0 to 2400 8N1
* Wakes an ATX PC in xx Seconds transmitted by uart
*
* Unterstands 3 commands:
* ATC resets/deletes an active wakeup
* ATS1234 starts PC in 1234 seconds
* ATP prints out the Timer state
*
* Editor/avr-gcc WinAVR 20071221
* License:  GNU General Public License 

* LICENSE:
*    Copyright (C) 2008 Michael Albert
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
* Feel free to modify!
*
* Changes:
* 11.04.2008 Michael Albert V1.1 Option to start the box delayed after power is connected.
*/

/* TODO INTERRUPT */
#include <avr/io.h>
#include <avr/interrupt.h> 
#include <string.h>
#include <stdlib.h>
#include <util/delay.h>
// Peter fleury's uart library
#include "uart.h"

// For bool variables
#define TRUE 1
#define FALSE 0

// 9600 does not work for my atmega8 ????
#define BAUD_RATE 2400
// Should we echo each received byte back to uart?
#define UART_ECHO 1

// Define my Port BIT Names 
// Timer running PortC 2, atmega8 Pin 25  
#define COUNTER_ACTIVE_LED PC2 
// Board running PortC 1, atmega8 Pin 24  
#define ACTIVE_LED PC1 
//  PIN for POWER button, atmega8 Pin 23  
#define POWER_PIN PC0
// Input PIN for 5V VDC, atmega8 Pin 28
#define PIN_5VDC PINC5
// Input PIN for Press Powerbutton after xx Seconds wenn Powersupply is connected to electricity network.
// atmega8 Pin 26, is Low Active, means Powerbutton would only pressed wenn PIN26 is conntect to GND
#define IN_AUTO_PRESS_POWERBUTTON_STARTUP PINC3
// When AUTO_PRESS_POWERBUTTON_STARTUP how much seconds should we wait
#define AUTO_PRESS_POWERBUTTON_STARTUP_WAIT_TIME 15

// Macros
// Toogles a LED/Bit at Port and pin
#define TOGGLE_LED(_port,_pin){((_port) & (1<<(_pin)))?((_port)&=~(1<<(_pin))):((_port)|=(1<<(_pin)));}
// Clear LED at Port and pin = VOltage to High 5V
#define CLEAR_LED(_port,_pin){((_port)|=(1<<(_pin)));}
// Set LED on at Port and pin = VOltage to Low 0V  
#define SET_LED(_port,_pin){((_port)&=~((1<<(_pin))));}
// Press powerbutton for 200ms  0V, wait 200ms, 5V
// #define PRESS_POWERBUTTON(_port,_pin){((_port)&=~(1<<(_pin)));(_delay_ms(200));((_port)|=(1<<(_pin)));}
#define PRESS_POWERBUTTON {((PORTC)&=~(1<<(POWER_PIN)));(_delay_ms(200));((PORTC)|=(1<<(POWER_PIN)));}
// Is powersupply still on
#define IS_POWER_ON ((PINC) & (1<<PIN_5VDC))

// Prefix of the commands, 2 chars
char sATCommandPrefix[3]="AT";
// Complete String which is received from uart
char sReceived[30]="";
// Position of Pointer when we parse the string
charsStrPos
// Position of Errorpointer when we convert string to long
charpStrErrPos
// Receives 2Bytes from uart, lower byte data, higher byte stat
unsigned int iReceive;
// Is a complete string received ?
uint8_t  bSringComplete;
// Seconds to start my PC
// As long -1=Timer inactive
long iSecondsToPowerOn=-1;
// Should we start the box?
uint8_t bStartBox;
// Auto Press Counter
long iPressCounterForAutoStart=0;

// Function prototypes
static int  is_at_command(char *sCom,char *sATPrefix);
static void init_ioports(void);
static void init_counter(void);
static void fPrintCounterState(void);

int main(void){
        // IO port init
        init_ioports();
        // counter init with external 32,768khz xtal
        init_counter();
        // UART Init
        uart_init(UART_BAUD_SELECT(BAUD_RATE,F_CPU));
        //Interrupts On
        sei();
        // We say hello  ;-)
        uart_puts("\rS100 Wakeup V1.1 \r\n");
        // init
        bSringComplete=FALSE;
        bStartBox=FALSE;
        for(;;){
                /*************************** begin read string from uart ***********************/
                // Get data from uart
                iReceive=uart_getc();
                // Errors
                if ( iReceive & (UART_FRAME_ERROR | UART_OVERRUN_ERROR | UART_BUFFER_OVERFLOW)  ){
                }
                // No data
                else if(iReceive & UART_NO_DATA){
                        // Do nothing
                }
                // Data available
                else{
                        // echo char
                        if (UART_ECHO){uart_putc((char)iReceive);}
                        // ...until Return Character is 0x0d = 13 is received
                        if ((char)iReceive=='\r'){
                                bSringComplete=TRUE;
                                if (UART_ECHO){uart_putc('\n');}
                        }
                        else {
                                // Only if enough space in sReceived free
                                if(sizeof(sReceived)>strlen(sReceived)){
                                        // Concatinate all characters to a string....., we need only the lower byte. 
                                        // Valid character or return??
                                        if ((((uint8_t)iReceive >= 32)&&((uint8_t)iReceive <= 126)) || ((uint8_t)iReceive==13)){
                                                strcat(sReceived,(char*)&iReceive);
                                        }
                                }
                        }
                }
                /*************************** end read string from uart ***********************/
                // When string is now completed
                if (bSringComplete){
                                // Is an AT Command
                                if (is_at_command(sReceived,sATCommandPrefix)){
                                        sStrPos=sReceived;
                                        // String pointer to 3rd Character
                                        sStrPos+=2;
                                        // Which AT command, allowed are S,P and C
                                        switch (*sStrPos) {
                                        case 'S':
                                                // String pointer to 4th Character
                                                sStrPos++;
                                                iSecondsToPowerOn=0;
                                                // Try to convert string to long
                                                iSecondsToPowerOn=strtol(sStrPos,&pStrErrPos,10);
                                                if (*pStrErrPos=='\0'){
                                                        fPrintCounterState();
                                                }
                                                else{
                                                        iSecondsToPowerOn=0;
                                                        uart_puts("Convert error at char: ");
                                                        uart_puts(pStrErrPos);
                                                        uart_puts("\r\n");
                                                }

                                                break;
                                        case 'C':
                                                //Clear Counter
                                                iSecondsToPowerOn=-1;
                                                break;
                                        case 'P':
                                                // We print the Timer state
                                                if (iSecondsToPowerOn>=0){
                                                        // Timer is active
                                                        fPrintCounterState();
                                                }
                                                else{
                                                        // Timer is  inactive
                                                        uart_puts("Timer inactive\n\r");
                                                }
                                                break;

                                        }       
                                }
                                // No AT command
                                else{
                                }
                                // String reset
                                strcpy(sReceived,"");
                                // 
                                bSringComplete=FALSE;
                }
                // End string is complete received
                // Any actions?
                // Must we press the power button?
                // Either on Active Counter or at box startup 
                if (bStartBox)
                {
                        if (! IS_POWER_ON){                     
                                uart_puts("Pressing powerbutton.\r\n");
                                PRESS_POWERBUTTON;
                        }
                        else{
                                uart_puts("Pressing powerbutton canceled. Power is active.\r\n");
                        }
                        // Waiting for next
                        bStartBox=FALSE;
                        CLEAR_LED(PORTC,COUNTER_ACTIVE_LED);
                }
                
        }               
        // end loop 
        return(0);
}

// END MAIN

// Check if first 2 Characters are right prefix
static int is_at_command(char *sCom,char *sATPrefix){
        if (strncmp(sCom,sATPrefix,2)){
                return(0);
        }
        else{
                return(1);
        }
        
}
// Init the oi ports
static void init_ioports(void){
        DDRB  = 0xff;
        
        /*
                Ports definition
                PORTC output:
                        PC0=Power button
                        PC1=Timer active, blinking 1Hz
                        PC2=Counter active, blinking 1Hz
                PORTC input:
                        PC4=Debug Mode, if this connect to GND.
                        PC5=Input +5 VDC PC, to see if the box is already running
        */
 
        // PORT
        // All Output Ports=off=5V, on input pins pullups on
        PORTC = (0xff);
        DDRC  = 0x0f;
}
// init counter
static void init_counter(void){
        // Timer2 clocked by external 32.768kHz
        ASSR |= (1<<AS2);

        // Clock/128 for 0,5Hz blinking and timer overflow every 1 second
        TCCR2 |= (1<<CS22) | (1<<CS20); 

        // Clock/64=2Hz for 1Hz blinking with toogle_bit
        TCNT2 = 0;      
        // Timer2(8Bit) Overflow Interrupt enabled (256)
        TIMSK |= (1<<TOIE2);
}
// timer overflow, every 1 second
SIGNAL(SIG_OVERFLOW2){
        // toogle led to show the board is alive
        TOGGLE_LED(PORTC,ACTIVE_LED);
        // Counter active when >= 0, decrement counter
        if (iSecondsToPowerOn>=0){
                // dec one second  
                iSecondsToPowerOn--;
                TOGGLE_LED(PORTC,COUNTER_ACTIVE_LED);
                // Counter = 0, it's time we must start the box
                if (iSecondsToPowerOn==0){
                        // We press the power button in mainloop, not in interrupt
                        bStartBox=TRUE;
                }
                
        }
        // Timer is inactive, we do nothing
        /* else{
                CLEAR_LED(PORTC,COUNTER_ACTIVE_LED);
        }*/

        // AutoStartCounter
        // IN_AUTO_PRESS_POWERBUTTON_STARTUP = Low(0V) and Counter < Wait Time
        if ((~PINC & (1<<IN_AUTO_PRESS_POWERBUTTON_STARTUP)) && (iPressCounterForAutoStart<=AUTO_PRESS_POWERBUTTON_STARTUP_WAIT_TIME)){
                if (iPressCounterForAutoStart==AUTO_PRESS_POWERBUTTON_STARTUP_WAIT_TIME){
                        bStartBox=TRUE
                }
                TOGGLE_LED(PORTC,COUNTER_ACTIVE_LED);
                iPressCounterForAutoStart++;
        }
}
// Print Counter to uart
static void fPrintCounterState(void){
        char sCounterState[35]="";
        char sCounter[10]="";
        strcpy(sCounterState,"Timer active: ");
        // Long 2 ASCII
        ltoa(iSecondsToPowerOn,sCounter,10);
        strcat(sCounterState,sCounter);
        strcat(sCounterState,"s\n\r");
        // Print the string out
        uart_puts(sCounterState);
}

Das VDR <-> Controllerkopplungsscript vdr-wakeup.pl

#!/usr/bin/perl
###############################################################################
#  Gets the /proc/acpi/alarm  or clear string from vdr and send it to the S100 atmega8 wakeup board
# Commandline arguments:
# 1. Wakeuptime  as ACPI Time String (2006-02-09 23:05:00) or UNIX Systemdate (1141941900)
# or via STDIN
# 
# 25.03.2008  
# changes
# 
# Michael Albert
# Comments to michael@albert-hetzles.de
# 1. FCN forever http://www.fcn.de
###############################################################################
use Time::Local;
use File::Basename;
use Sys::Syslog;

# S100 Serial port
$sSerialPort="/dev/ttyS0";
#$sSerialPort="/tmp/ATx.txt";
# Script messages to syslog
$bUseSysLog=1;
# Set system time
$bSetSystemTime=0;
# Time Servers
@aTimeServer=("ptbtime1.ptb.de","ptbtime2.ptb.de");
# ntpbin
$sNTPDate="sudo /usr/sbin/ntpdate -u ";
# Setup /InitSerialPort
$bSetupSerial=1;
# stty exe string, BAUD 2400, Bits 8, Parity None, 1 Stopbit, noflowcontrol, no hardware handshake
$ssttyBin="stty -F $sSerialPort 2400 cs8 -cstopb -crtscts -ixon -ixoff";

# $sArg=Variable to store first command line parameter
# $iACPIStartTime=Variable to store in systemtime (1141941900)
my ($sArg,$iACPIStartTime,$sMessage);
$sMessage=" Starting VDR wakeup...";
print $sMessage."\n";
($bUseSysLog) && &fWriteSyslog($sMessage);
# Set time
if ($bSetSystemTime){
	foreach (@aTimeServer){
		$sMessage="Set systemtime from ntpserver $_\n";
		print $sMessage."\n";
		($bUseSysLog) && &fWriteSyslog($sMessage);
		$iResult=system($sNTPDate.$_);
		if ($iResult){
			$sMessage="Error while setting systemtime $iResult\n";
			print $sMessage."\n";
			($bUseSysLog) && &fWriteSyslog($sMessage);
		}
	}
}


# Getting parameter
if (@ARGV[0]) {
	# Get command line parameter
	$sArg=@ARGV[0];
}
else{
	# Or from stdin
	$sArg=<STDIN>;
}
# cut \n 
chomp($sArg);
# Check if format is already UNIX systemtime
if ($sArg=~/^[0-9]+$/){
	# Yes, cool
	$sMessage=" Get UNIX system time \($sArg\) via command line.";
	print $sMessage."\n";
	($bUseSysLog) && &fWriteSyslog($sMessage);
	$iACPIStartTime=$sArg;
}
else{
	# No, we must convert it.
	$sMessage=" Get /proc/acpi/alarm formated string \($sArg\) via command line.";
	print $sMessage."\n";
	($bUseSysLog) && &fWriteSyslog($sMessage);
	# Convert it to system time format. 2006-02-09 23:05:00 -> 1141941900
	$iACPIStartTime=&fGetSystemTimeFromACPIStartTime($sArg);
}



if ($iACPIStartTime){
	($bSetupSerial) && system($ssttyBin);
	my ($iTimeNow,$iTimeDiff);
	# print "ACPI Time: $iACPIStartTime\n";
	# Calculating difference between Starttime and now in seconds
	$iTimeDiff=$iACPIStartTime-time;
	if ($iTimeDiff>0){
		$sMessage=" Starting VDR in $iTimeDiff seconds.";
		print $sMessage."\n";
		($bUseSysLog) && &fWriteSyslog($sMessage);
		# send command string to Board via serial port
		#open(hSTTY,">$sSerialPort")||die "Can't open $sSerialPort\n";
		print 'echo -e "\\rATS'.$iTimeDiff.'\\r" > '.$sSerialPort;
		system('echo -e "\\rATS'.$iTimeDiff.'\\r" > '.$sSerialPort);
		#close(hSTTY);
	}
	else{
		# Think positive :-)
		$iTimeDiff*=-1;
		$sMessage=" VDR starting time is $iTimeDiff seconds in the past\! Resetting timer.";
		warn $sMessage."\n";
		($bUseSysLog) && &fWriteSyslog($sMessage);
		system('echo -e "\\rATC\\r" > '.$sSerialPort);
	}
}
else{
	warn " Unknown command or time format\n";
}


###############################################################################
# 1. Parameter ACPI Time (Format: 2006-02-09 23:05:00)
# Return systemtime
sub fGetSystemTimeFromACPIStartTime(){
	my ($sACPIStartTime,$sDate,$sTime,$sSec,$sMin,$sHour,$sDay,$sMon,$sYear);
	$sACPIStartTime=shift;
	($sDate,$sTime)=split(/ /,$sACPIStartTime);
	($sHour,$sMin,$sSec)=split(/\:/,$sTime);
	($sYear,$sMon,$sDay)=split(/\-/,$sDate);
	 # print"$sSec,$sMin,$sHour,$sDay,$sMon,$sYear\n";
	 # timelocal wants Month from 0..11
	 $sMon-=1;
	# We try to convert it
	eval{
		$iACPISysTime = timelocal($sSec,$sMin,$sHour,$sDay,$sMon,$sYear);
	};
	# successfully? 
	if ($@){
		# No
		my $sErrorMessage;
		$sErrorMessage=$@;
		chomp($sErrorMessage);
		warn " ERROR $sErrorMessage.\n Cannot convert \"$sACPIStartTime\". Wrong format or incorrect date.\n";
		($bUseSysLog) && &fWriteSyslog(" ERROR $sErrorMessage.\n Cannot convert \"$sACPIStartTime\". Wrong format or incorrect date.\n");
		return(0);
	}
	else{
		# Yes
		return($iACPISysTime);
	}
}

# 1. Parameter Text write to syslog
sub fWriteSyslog(){
    my $sText;
    $sText = @_[0];
    openlog($program . basename($0), 'pid', 'user');
    syslog('info', $sText);
    #syslog('mail|warning', 'this is a better test: %d', time);
    closelog();
}


Michael Albert, 22.04.2008