/* Ansteuerung des Temperatur- und Feuchte-Sensor-Schaltkreises
* SHT11 mit dem PC am Parallelport, Seriellen Port oder HID-USB
* h#s 12/03
*090602 mittels InpOut32.DLL, nur Win32, Teletubbie-Optik, auch COM-Port
-090626 falsche String-IDs bei den Fehlermeldungen
+090627 Ressource zweisprachig
*111027 Konfigurationsdaten nach HKEY_CURRENT_USER, Vista-Manifest
+1303xx Win64-Kompilat
+130715 HID-USB dazu
*/
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <setupapi.h>
#include <devguid.h>
extern "C"{
#include <hidsdi.h>
#include <hidpi.h>
}
#include <stdio.h>
#include <math.h>
#include <tchar.h>
struct TConfig{
POINTS WinPos; // Fensterposition des Dialogs
BYTE Where; // Welche Schnittstelle? 1-3=parallel (1=direkt, 2=PortTalk, 3=InpOut32),
// 4=h#s-InpOut32.DLL, 5=seriell, 6=(PnPSA) HID-USB
BYTE SerialNo; // Nummer der seriellen Schnittstelle, 0 = COM1
WORD ParallelAddr; // Adresse des Parallelports (zumeist 0x378)
FLOAT Ucc; // Speisespannung - zur Berechnung der Temperatur
BYTE SensVersion; // Sensor-Version - zur Berechnung der rel. Feuchte, nur 3 oder 4, 5 == SHT21?
BYTE ParallelNo; // LPT-Nummer via h#s InpOut32.DLL, 0 = LPT1
WORD Interval; // Abfrage-Intervall in ms
BYTE HidNo; // Nummer des HID-USB, 0 = erstes
BYTE unused; // frei
BYTE flags; // AutoLog ...
BYTE sht11ctl; // Voreinstellungs-Bits für den Sensor (FastMode usw.)
static void Changed();
}Config;
TCHAR LogFile[MAX_PATH];
int InstNr; // Instanz-Nummer, nullbasiert
HWND MainWnd; // Hauptfenster
#if _MSC_VER < 1400 // Visual C++ 6 (kein 64bit)
# include <conio.h> // _inp und _out
#else
# include <intrin.h> // __inbyte und __outbyte
#endif
#include <winioctl.h>
#include "PortTalk_IOCTL.h"
#include "../inpout32/InpOut32.h"
#ifdef _M_IX64
# define INPOUT32DLL "InpOutX64"
#else
# define INPOUT32DLL "InpOut32"
#endif
/**********
* WUtils *
**********/
HINSTANCE hInstance;
EXTERN_C int _fltused;
int _fltused;
typedef const TCHAR *PCTSTR, NEAR*NPCTSTR, FAR*LPCTSTR;
#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)
#define nobreak
TCHAR MBoxTitle[64];
static TCHAR sDecimal[2];
int vMBox(HWND Wnd, UINT id, UINT style, va_list arglist) {
TCHAR buf1[256],buf2[1024];
LoadString(hInstance,id,buf1,elemof(buf1));
_vsntprintf(buf2,elemof(buf2),buf1,arglist);
return MessageBox(Wnd,buf2,MBoxTitle,style);
}
int _cdecl MBox(HWND Wnd, UINT id, UINT style,...) {
va_list va;
va_start(va,style);
return vMBox(Wnd,id,style,va);
}
// Punkt durch Komma ersetzen
void PK(PTSTR s) {
s=_tcschr(s,T('.'));
if (s) *s=sDecimal[0];
}
bool GetDlgItemFloat(HWND Wnd, UINT id, float*f) {
TCHAR s[32];
GetDlgItemText(Wnd,id,s,elemof(s));
PTSTR p=_tcschr(s,sDecimal[0]);
if (p) *p='.';
return _stscanf(s,T("%g"),f)==1 ? true : false;
}
void SetEditFocus(HWND Wnd, UINT id) {
Wnd=GetDlgItem(Wnd,id);
SetFocus(Wnd);
Edit_SetSel(Wnd,0,-1);
}
void SetCheckboxGroup(HWND Wnd, UINT u, UINT o, UINT bits) {
for (;u<=o; u++) {
CheckDlgButton(Wnd,u,bits&1);
bits>>=1;
}
}
UINT GetCheckboxGroup(HWND Wnd, UINT u, UINT o) {
UINT r=0;
for (;o>=u; o--) {
r<<=1;
if (IsDlgButtonChecked(Wnd,o)) r++;
}
return r;
}
// Nur String setzen wenn verändert! (Flackern vermeiden)
void MySetDlgItemText(UINT id, LPCTSTR s) {
TCHAR buf[64];
GetDlgItemText(MainWnd,id,buf,elemof(buf));
if (lstrcmp(buf,s)) SetDlgItemText(MainWnd,id,s);
}
void _cdecl ErrorPrint(UINT id,...) {
TCHAR s[64],t[64];
LoadString(hInstance,id,t,elemof(t));
_vsntprintf(s,elemof(s),t,(va_list)(&id+1));
MySetDlgItemText(111,s);
}
bool ShortYield(void) { // true wenn OK zum Fortsetzen
Sleep(1);
MSG msg;
if (!PeekMessage(&msg,0,0,0,PM_REMOVE)) return true;
if (msg.hwnd==MainWnd) switch (msg.message) {
case WM_CLOSE: PostMessage(msg.hwnd,msg.message,msg.wParam,msg.lParam); // zurück
return false;
case WM_TIMER: return true;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
return true;
}
struct dynaprocs{
HINSTANCE hLib; // Libname = erster String
FARPROC proc[1]; // dynamisches Array aus Funktionszeigern; die Länge ergibt sich aus der Anzahl der
bool dynaload(const char*e); // nullterminierte Strings (doppel-null-terminiert)
};
bool dynaprocs::dynaload(const char*e) {
int i;
if (!(hLib=LoadLibraryA(e))) return false;
for (i=0;;i++) {
e+=lstrlenA(e)+1;
if (!*e) return true; // Ende erreicht
if (!(proc[i]=GetProcAddress(hLib,*e=='#'?(const char*)atoi(e+1):e))) return false;
}
}
struct crc8{
BYTE crc;
void put(BYTE x);
};
void crc8::put(BYTE x) { // Prüfsummen-Maschinerie
int i;
for (i=0; i<8; i++) {
crc=(crc>>1)^(((signed char)(x^(crc<<7))>>7)&0x8C);
x<<=1;
}
}
#define TIMEOUT 400 // Millisekunden
/******************************
* Hardwarenahe Kommunikation *
******************************/
struct SHT11base{ // Negative Werte = Fehlerkode
mutable bool ok; // <true> solange Verbindung steht
mutable bool verbose; // vor Enum() oder Open() zu setzen, damit MessageBox erscheint
virtual void Enum(HWND hCombo, const TConfig*Cfg) const=0; // ComboBox füllen
virtual void OnCb(HWND hCombo, TConfig *Cfg) const=0; // ComboBox-Auswahl nach Cfg auslesen
virtual void Open()=0; // Verbindung herstellen
virtual int GetRaw(BYTE addr, DWORD to=TIMEOUT)=0; // I²C-Wert holen, 3=Temperatur, 5=Feuchte, 7=Flags
virtual int SetRaw(BYTE addr, int v)=0;// I²C-Wert setzen: 6=Flags, v=Flag-Byte, 30=Soft-Reset
virtual void Close()=0; // Verbindung beenden
};
/*** Klasse ***/
struct SHT11port:SHT11base{ // Alles was direkt vom PC aus angesprochen wird
bool ifsync; // wenn Interface OK (in Sync), keine Dummy-Takte generieren
DWORD inbuf; // Ergebnisbits (alle I/O-Arten)
BYTE inidx; // Anzahl getätigter DATAQ()
BYTE b; // Spiegel des Ausgabeports
BYTE crc_start;
crc8 crc;
virtual void SCKL()=0;
virtual void SCKH()=0;
virtual void DATAL()=0;
virtual void DATAH()=0;
virtual void DATAQ()=0;
virtual DWORD InResult() {DWORD t=inbuf; inbuf=inidx=0; return t;};
void TransmissionStart();
void Open() {ZeroMemory(this,sizeof*this);}
void SendByte(BYTE x);
void RecvByte(bool more);
bool WaitReady(DWORD timeout);
int GetRaw(BYTE addr, DWORD to=TIMEOUT);
int SetRaw(BYTE addr, int v);
};
// LptInOut: Benötigt 19*2 Bytes <outbuf>, 1 Byte <inbuf>
void SHT11port::SendByte(BYTE x) {
for (int i=0;i<8;i++) {
if (x&0x80) DATAH(); else DATAL();
SCKH(); SCKL();
x<<=1;
}
DATAH();
SCKH();
DATAQ();
SCKL();
}
// LptInOut: Benötigt 19*2 Bytes <outbuf>, 8 Byte <inbuf>
void SHT11port::RecvByte(bool more) {
for (int i=0;i<8;i++) {
SCKH();
DATAQ();
SCKL();
}
if (more) DATAL();
SCKH();
SCKL();
DATAH();
}
void SHT11port::TransmissionStart() {
if (!ifsync) for (int i=0;i<9;i++) {SCKH();SCKL();} // Startsequenz
SCKH(); DATAL(); SCKL();
SCKH(); DATAH(); SCKL();
}
bool SHT11port::WaitReady(DWORD timeout) {
DWORD t=GetTickCount();
while (DATAQ(),InResult()) { // warten solange High
if ((unsigned)GetTickCount()-t>timeout) return false;
if (!ShortYield()) return false;
}return true;
}
// Routine mit abgetrennter Ergebnisphase
int SHT11port::GetRaw(BYTE addr, DWORD to) {
if (!ok) {ErrorPrint(9); return -3;} // Port-Verbindung verloren
DWORD w;
crc.crc=crc_start;
TransmissionStart();
SendByte(addr);
if (InResult()) {ifsync=false; ErrorPrint(13,addr); return -1;} // Kein ACK? "Übertragungsfehler"!
crc.put(addr);
if (!WaitReady(to)) {ifsync=false; ErrorPrint(14,to); return -2;} // "Zeitüberschreitung!"
if (addr!=0x07) RecvByte(true);
RecvByte(true);
RecvByte(false); // CRC
w=InResult(); // Ergebnisbytes (16 oder 24 bit) auslesen
if (addr!=0x07) crc.put(BYTE(w>>16));
crc.put(BYTE(w>>8));
if (crc.crc!=BYTE(w)) ErrorPrint(15,crc.crc,BYTE(w)); // "Prüfsumme, %02X!=%02X!"
ifsync=true;
if (addr==0x07) crc_start=(w>>8)&0x0F;
return w>>8;
}
int SHT11port::SetRaw(BYTE addr, int v) {
if (!ok) {ErrorPrint(9); return -3;} // Port-Verbindung verloren
TransmissionStart();
SendByte(addr);
if (addr!=30) SendByte(BYTE(v));
if (InResult()) {ifsync=false; ErrorPrint(13); return -1;} // irgendein NAK?
ifsync=true;
if (addr==0x06) crc_start=v&0x0F;
return 0;
}
/*** Klasse ***/
struct SHT11ppt:SHT11port{
union{
WORD addr; // für Direkt(1), PortTalk(2), InpOut32(3)
HQUEUE hQueue; // Für neue InpOut32-Schnittstelle (4)
};
virtual void SCKL() {out(b&~0x40);}
virtual void SCKH() {out(b|0x40);}
virtual void DATAL() {out(b&~0x80);}
virtual void DATAH() {out(b|0x80);}
virtual void DATAQ();
virtual BYTE in()=0;
virtual void out()=0;
void out(BYTE n) {if (b==n) return; b=n; out();}
void Enum(HWND hCombo, const TConfig*Cfg) const;
void OnCb(HWND hCombo, TConfig*Cfg) const;
void Open();
void Close() {out(0x00);} // Strom abschalten
};
static const WORD DefAddrs[]={0x378,0x278,0x3BC};
static SP_CLASSIMAGELIST_DATA ild;
// Füllt Combobox mit typischen Parallelportadressen
void SHT11ppt::Enum(HWND hCombo, const TConfig*Cfg) const {
bool set=false;
TCHAR buf[20];
COMBOBOXEXITEM cbei;
cbei.mask=CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_PORTS,&cbei.iImage);
cbei.iSelectedImage=cbei.iImage;
cbei.pszText=buf;
ComboBox_ResetContent(hCombo);
SetWindowText(hCombo,NULL);
for (cbei.iItem=0; cbei.iItem<elemof(DefAddrs); cbei.iItem++) {
_sntprintf(buf,elemof(buf),T("%Xh (%u, LPT%u)"),
DefAddrs[cbei.iItem],DefAddrs[cbei.iItem],cbei.iItem+1);
SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
// ComboBox_AddString(Wnd,buf);
if (Cfg->ParallelAddr==DefAddrs[cbei.iItem]) {
ComboBox_SetCurSel(hCombo,cbei.iItem);
set=true;
}
}
if (!set) {
_sntprintf(buf,elemof(buf),T("%03Xh"),Cfg->ParallelAddr);
SetWindowText(hCombo,buf);
}
}
void SHT11ppt::OnCb(HWND hCombo, TConfig*Cfg) const{
TCHAR s[32];
GetWindowText(hCombo,s,elemof(s));
Cfg->ParallelAddr=(WORD)_tcstol(s,NULL,16);
}
void SHT11ppt::Open() {
inbuf=inidx=0; // Ergebnispuffer vorbereiten
b=0xA0; out(); // Stromversorgung zuschalten, SCK = L, DATA = H
}
void SHT11ppt::DATAQ() {
if (!ok) return;
if (++inidx>32) {ok=false; return;}
inbuf<<=1;
if (in()&0x40) inbuf|=1;
}
/*** Klasse ***/
struct SHT11dio:SHT11ppt{
void Enum(HWND hCombo, const TConfig*Cfg) const;
void Open();
void Close() {}
BYTE in();
void out();
};
void SHT11dio::Enum(HWND hCombo, const TConfig*Cfg) const {
if ((long)GetVersion()<0 || !verbose || MBox(GetParent(hCombo),1,MB_YESNO|MB_ICONQUESTION)==IDYES) {
SHT11ppt::Enum(hCombo,Cfg);
}else ok=false;
}
void SHT11dio::Open() {
if ((long)GetVersion()<0 || !verbose || MBox(MainWnd,1,MB_YESNO|MB_ICONQUESTION)==IDYES) {
addr=Config.ParallelAddr;
ok=true;
SHT11ppt::Open();
}else ok=false;
}
BYTE SHT11dio::in() {
#if _MSC_VER < 1400 // Visual C++ 6 (kein 64bit)
return _inp(addr+1);
#else
return __inbyte(addr+1);
#endif
}
void SHT11dio::out() {
#if _MSC_VER < 1400 // Visual C++ 6 (kein 64bit)
_outp(addr,b);
#else
__outbyte(addr,b);
#endif
}
/*** Klasse: InpOut32.dll ***/
struct SHT11par:SHT11ppt{
mutable HINSTANCE hInpOut32;
mutable Inp32_t pIn;
mutable Out32_t pOut;
void Enum(HWND hCombo, const TConfig*Cfg) const;
void Open();
void Close() {FreeLibrary(hInpOut32);}
BYTE in() {return pIn(addr+1);}
void out() {pOut(addr,b);}
};
void SHT11par::Enum(HWND hCombo, const TConfig*Cfg) const {
SHT11ppt::Enum(hCombo,Cfg);
ok=((dynaprocs*)&hInpOut32)->dynaload(INPOUT32DLL"\0Inp32\0Out32\0");
if (!ok && verbose) MBox(GetParent(hCombo),3,MB_OK,T(INPOUT32DLL)T(".dll"));
}
void SHT11par::Open() {
ok=((dynaprocs*)&hInpOut32)->dynaload(INPOUT32DLL"\0Inp32\0Out32\0");
if (!ok) {
if (verbose) MBox(MainWnd,3,MB_OK,T(INPOUT32DLL)T(".dll"));
return;
}
addr=Config.ParallelAddr;
SHT11ppt::Open();
}
/*** Klasse: PortTalk-IOCTL ***/
struct SHT11ioctl:SHT11ppt{
HANDLE hDev;
void Open();
void Close() {CloseHandle(hDev);}
BYTE in();
void out();
};
void SHT11ioctl::Open() {
hDev=CreateFile(T("\\\\.\\PortTalk"),GENERIC_READ,0,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if (hDev==INVALID_HANDLE_VALUE) hDev=0;
ok=hDev!=0;
if (!ok && verbose) MBox(MainWnd,2,MB_OK);
}
BYTE SHT11ioctl::in() {
BYTE b=0xFF;
if (!ok) return b;
WORD a=addr+1;
DWORD bret;
if (!DeviceIoControl(hDev,IOCTL_READ_PORT_UCHAR,&a,2,&b,1,&bret,NULL) || bret!=1) ok=false;
return b;
}
void SHT11ioctl::out() {
DWORD bret;
struct{
WORD a;
BYTE b;
}s={addr,b};
if (ok && !DeviceIoControl(hDev,IOCTL_WRITE_PORT_UCHAR,&s,3,NULL,0,&bret,NULL)) ok=false;
}
/*** Klasse: LPT-Mikrocode ***/
struct SHT11lpt:SHT11ppt{
mutable HINSTANCE hInpOut32;
mutable LptOpen_t pOpen;
mutable LptInOut_t pInOut;
mutable LptClose_t pClose;
BYTE ucode[200]; // Mikrocode-Puffer
int ucidx; // Mikrocode-Füllstand
void Enum(HWND hCombo, const TConfig *Cfg) const;
void OnCb(HWND hCombo, TConfig *Cfg) const;
void Open();
BYTE in() {In();return 0xFF;}
void In();
void out();
DWORD InResult();
void Close();
};
void SHT11lpt::Enum(HWND hCombo, const TConfig *Cfg) const {
COMBOBOXEXITEM cbei;
cbei.mask=CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_LPARAM;
TCHAR buf[12];
cbei.pszText=buf;
cbei.iItem=0;
SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_PORTS,&cbei.iImage);
cbei.iSelectedImage=cbei.iImage;
ComboBox_ResetContent(hCombo);
SetWindowText(hCombo,NULL);
if (((dynaprocs*)&hInpOut32)->dynaload(INPOUT32DLL"\0#21\0#22\0#27\0")) {
for (int i=0; i<10; i++) {
HQUEUE q=pOpen(i,0);
if (q) {
pClose(q);
_sntprintf(buf,elemof(buf),T("LPT%u"),i+1);
cbei.lParam=i;
SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
if (i==Cfg->ParallelNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
cbei.iItem++;
}
}
}else if (verbose) MBox(GetParent(hCombo),hInpOut32?8:3,MB_OK,T(INPOUT32DLL)T(".dll"));
}
void SHT11lpt::OnCb(HWND hCombo, TConfig*Cfg) const{
int i=ComboBox_GetCurSel(hCombo);
if (i>=0) Cfg->ParallelNo=(BYTE)ComboBox_GetItemData(hCombo,i);
}
void SHT11lpt::Open() {
pClose=NULL;
ok=((dynaprocs*)&hInpOut32)->dynaload(INPOUT32DLL"\0#21\0#22\0#27\0");
if (!ok) {
if (verbose) MBox(MainWnd,hInpOut32?8:3,MB_OK,T(INPOUT32DLL)T(".dll"));
hQueue=0;
return;
}
hQueue=pOpen(Config.ParallelNo,0);
if (!hQueue) {
if (verbose) MBox(MainWnd,4,MB_OK,Config.ParallelNo+1,T(INPOUT32DLL)T(".dll"));
ok=false;
}
ucidx=inidx=0;
}
void SHT11lpt::In() {
if (ucidx>=sizeof ucode) {ok=false; return;}
ucode[ucidx++]=0x11; // Mikrocode generieren: In(+1)
}
void SHT11lpt::out() {
if (ucidx>=sizeof ucode-1) {ok=false; return;}
ucode[ucidx++]=0;
ucode[ucidx++]=b; // Mikrocode generieren: Out(+0)
}
// Aufgesammelten Mikrocode ausführen lassen (Flush) und Ergebnisbits bereitstellen
DWORD SHT11lpt::InResult() {
if (!ok) return 0;
BYTE buf[32];
if (pInOut(hQueue,ucode,ucidx,buf,inidx)!=inidx) {ok=false; return 0;}
DWORD ret=0;
for (int i=0; i<inidx; i++) {
ret<<=1;
if (buf[i]&0x40) ret|=1; // suche gesetzte ACK-Bits
}
ucidx=inidx=0;
return ret;
}
void SHT11lpt::Close() {
if (pClose) pClose(hQueue);
FreeLibrary(hInpOut32);
}
/*** Klasse ***/
struct SHT11ser:SHT11port{
HANDLE hCom;
void Enum(HWND hCombo, const TConfig*Cfg) const;
void OnCb(HWND hCombo, TConfig*Cfg) const;
void Open();
void Close();
void SCKL() {if (ok && !EscapeCommFunction(hCom,CLRDTR)) ok=false;} // DTR = Pin 4 = Taktausgang
void SCKH() {if (ok && !EscapeCommFunction(hCom,SETDTR)) ok=false;}
void DATAL() {if (!ok ||!b) return; if (!EscapeCommFunction(hCom,CLRRTS)) ok=false;} // RTS = Pin 7 = Datenausgang
void DATAH() {if (!ok || b) return; if (!EscapeCommFunction(hCom,SETRTS)) ok=false;}
void DATAQ();
};
void SHT11ser::Enum(HWND hCombo, const TConfig*Cfg) const {
COMBOBOXEXITEM cbei;
cbei.mask=CBEIF_TEXT|CBEIF_LPARAM|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_OVERLAY;
cbei.iItem=0;
SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_PORTS,&cbei.iImage);
cbei.iSelectedImage=cbei.iImage;
ComboBox_ResetContent(hCombo);
SetWindowText(hCombo,NULL);
HANDLE devs=SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,NULL,0,DIGCF_PRESENT);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVINFO_DATA devInfo;
devInfo.cbSize=sizeof devInfo;
for (UINT i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
HKEY hKey;
TCHAR s[16];
DWORD slen=sizeof s;
*s=0;
if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
==INVALID_HANDLE_VALUE) continue;
RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&slen);
RegCloseKey(hKey);
if (*s=='C') { // Fehlschläge und LPTx ausfiltern
int i=_tcstol(s+3,NULL,0)-1; // Nullbasierte COM-Nummer
cbei.pszText=s;
cbei.lParam=i;
cbei.iOverlay=0;
if (i==Cfg->SerialNo) {
if (!hCom) cbei.iOverlay=2; //durchkreuzt!
}else{
TCHAR buf[12];
_sntprintf(buf,elemof(buf),T("\\\\.\\COM%u"),i+1);
HANDLE h=CreateFile(buf,GENERIC_READ,0,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if (h!=INVALID_HANDLE_VALUE) CloseHandle(h);
else cbei.iOverlay=2;
}
SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
if (i==Cfg->SerialNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
cbei.iItem++;
}
}
}
}
void SHT11ser::OnCb(HWND hCombo, TConfig*Cfg) const{
int i=ComboBox_GetCurSel(hCombo);
if (i>=0) Cfg->SerialNo=(BYTE)ComboBox_GetItemData(hCombo,i);
}
void SHT11ser::Open() {
TCHAR buf[12];
_sntprintf(buf,elemof(buf),T("\\\\.\\COM%d"),Config.SerialNo+1);
hCom=CreateFile(buf,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (hCom==INVALID_HANDLE_VALUE) {
ok=false;
hCom=0;
if (verbose) MBox(MainWnd,5,MB_OK,Config.SerialNo+1); // "Kann COM%d nicht öffnen"
}else{
ok=true;
if (!EscapeCommFunction(hCom,SETBREAK)) ok=false; // TxD = Pin 3 = Stromversorgung
b=false;
SCKL();
DATAH();
if (!ok && verbose) MBox(MainWnd,9,MB_OK); //"Verbindungsverlust"
}
}
void SHT11ser::Close() {
if (ok) EscapeCommFunction(hCom,CLRBREAK);
if (hCom) CloseHandle(hCom);
}
void SHT11ser::DATAQ() {
if (!ok) return;
DWORD ModemStat;
if (!GetCommModemStatus(hCom,&ModemStat)) {ok=false; return;}
if (++inidx>32) {ok=false; return;} // hier: interner Programmfehler
inbuf<<=1;
if (ModemStat&MS_CTS_ON) inbuf|=1; // CTS = Pin 8 = Dateneingang (verbunden mit RTS)
}
/*** Klasse ***/
struct SHT11hid:SHT11base{
HANDLE hDev;
PHIDP_PREPARSED_DATA pd;
HIDP_CAPS hidcaps;
HIDP_VALUE_CAPS *vcaps;
BYTE *report; // genügend großer Transferpuffer
void Enum(HWND hCombo, const TConfig*Cfg) const;
void OnCb(HWND hCombo, TConfig*Cfg) const;
void Open();
int GetRaw(BYTE addr, DWORD to=TIMEOUT);
int SetRaw(BYTE addr, int v);
void Close();
};
void SHT11hid::Enum(HWND hCombo, const TConfig*Cfg) const {
COMBOBOXEXITEM cbei;
cbei.mask=CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
TCHAR buf[12];
cbei.pszText=buf;
cbei.iItem=0;
SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_HIDCLASS,&cbei.iImage);
cbei.iSelectedImage=cbei.iImage;
ComboBox_ResetContent(hCombo);
SetWindowText(hCombo,NULL);
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
HANDLE devs=SetupDiGetClassDevs(&hidGuid,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVICE_INTERFACE_DATA di;
for (DWORD i=0; di.cbSize=sizeof di,SetupDiEnumDeviceInterfaces(devs,0,&hidGuid,i,&di); i++) {
struct{
SP_DEVICE_INTERFACE_DETAIL_DATA d;
TCHAR space[MAX_PATH];
}d;
d.d.cbSize=sizeof d.d;
if (SetupDiGetDeviceInterfaceDetail(devs,&di,&d.d,sizeof d,NULL,NULL)) {
HANDLE h=CreateFile(d.d.DevicePath,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if (h!=INVALID_HANDLE_VALUE) {
PHIDP_PREPARSED_DATA pd;
if (HidD_GetPreparsedData(h,&pd)) {
HIDP_CAPS hidcaps;
HIDP_VALUE_CAPS *vcaps;
if ((int)HidP_GetCaps(pd,&hidcaps)>=0
&& hidcaps.NumberFeatureValueCaps>=2
&& (vcaps=new HIDP_VALUE_CAPS[hidcaps.NumberFeatureValueCaps])) {
if ((int)HidP_GetValueCaps(HidP_Feature,vcaps,&hidcaps.NumberFeatureValueCaps,pd)>=0
// Nach einem Messwert "Temperatur" suchen (hier: erster Messwert)
&& vcaps[0].Units==0x00010001 // Einheit K (Kelvin)
&& vcaps[0].IsAbsolute
// Nach einem Messwert "Feuchte" suchen (hier: zweiter Messwert)
&& vcaps[1].Units==0x00000001 // dimensionslos
&& vcaps[1].IsAbsolute) {
_sntprintf(buf,elemof(buf),T("HID%u"),cbei.iItem);
SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
if (cbei.iItem==Cfg->HidNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
cbei.iItem++;
}
delete[] vcaps;
}
HidD_FreePreparsedData(pd);
}
CloseHandle(h);
}
}
}
SetupDiDestroyDeviceInfoList(devs);
}
}
void SHT11hid::OnCb(HWND hCombo, TConfig*Cfg) const{
int i=ComboBox_GetCurSel(hCombo);
if (i>=0) Cfg->HidNo=(BYTE)ComboBox_GetItemData(hCombo,i);
}
void SHT11hid::Open() {
int n=Config.HidNo+1;
ok=false;
vcaps=NULL;
pd=NULL;
report=NULL;
hDev=0;
// Leider dasselbe fast komplett noch einmal! (Siehe Enum()) Lässt sich hier kaum vereinfachen.
// Denn die simple HID-Gerätenummer zu speichern hätte den Nachteil, dass diese sich ändern kann,
// wenn der Anwender eine USB-Maus umsteckt.
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
HDEVINFO devs=SetupDiGetClassDevs(&hidGuid,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs!=INVALID_HANDLE_VALUE) {
SP_DEVICE_INTERFACE_DATA di;
for (DWORD i=0; di.cbSize=sizeof di, SetupDiEnumDeviceInterfaces(devs,0,&hidGuid,i,&di);i++) {
struct{
SP_DEVICE_INTERFACE_DETAIL_DATA d;
TCHAR space[MAX_PATH];
}d;
d.d.cbSize=sizeof d.d;
if (SetupDiGetDeviceInterfaceDetail(devs,&di,&d.d,sizeof d,NULL,NULL)) {
HANDLE h=CreateFile(d.d.DevicePath,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if (h!=INVALID_HANDLE_VALUE) {
PHIDP_PREPARSED_DATA pd;
if (HidD_GetPreparsedData(h,&pd)) {
HIDP_CAPS hidcaps;
HIDP_VALUE_CAPS *vcaps;
if ((int)HidP_GetCaps(pd,&hidcaps)>=0
&& hidcaps.NumberFeatureValueCaps>=2
&& (vcaps=new HIDP_VALUE_CAPS[hidcaps.NumberFeatureValueCaps])) {
if ((int)HidP_GetValueCaps(HidP_Feature,vcaps,&hidcaps.NumberFeatureValueCaps,pd)>=0
// Nach einem Messwert "Temperatur" suchen (hier: erster Messwert)
&& vcaps[0].Units==0x00010001 // Einheit K (Kelvin)
&& vcaps[0].IsAbsolute
// Nach einem Messwert "Feuchte" suchen (hier: zweiter Messwert)
&& vcaps[1].Units==0x00000001 // dimensionslos
&& vcaps[1].IsAbsolute) {
if (!--n) { // gewünschtes passendes PnPSA gefunden
hDev=h;
this->pd=pd;
this->hidcaps=hidcaps;
this->vcaps=vcaps;
report=new BYTE[hidcaps.FeatureReportByteLength];
if (report) ok=true;
SetupDiDestroyDeviceInfoList(devs);
return;
}
}
delete[] vcaps;
}
HidD_FreePreparsedData(pd);
}
CloseHandle(h);
}
}
}
SetupDiDestroyDeviceInfoList(devs);
}
if (verbose) MBox(MainWnd,6,MB_OK,Config.HidNo);
}
int SHT11hid::GetRaw(BYTE addr, DWORD to) {
if (!ok) {ErrorPrint(9); return -3;}
report[0]=addr;
if (!HidD_GetFeature(hDev,report,hidcaps.FeatureReportByteLength)) ok=false;
return addr==0x07 ? report[1] : *(WORD*)(&report[1]);
}
int SHT11hid::SetRaw(BYTE addr, int v) {
if (!ok) {ErrorPrint(9); return -3;}
report[0]=addr|1;
report[1]=BYTE(v);
if (!HidD_SetFeature(hDev,report,hidcaps.FeatureReportByteLength)) ok=false;
return 0;
}
void SHT11hid::Close() {
if (report) delete[] report;
if (pd) HidD_FreePreparsedData(pd);
if (vcaps) delete[] vcaps;
}
// Erzeugt ein Schnittstellen-Objekt, bereit zum Enumerieren, für OnCb sowie Open
SHT11base *SHT11factory(int what) {
switch (what) {
case 1: return new SHT11dio;
case 2: return new SHT11ioctl;
case 3: return new SHT11par;
case 4: return new SHT11lpt;
case 5: return new SHT11ser;
case 6: return new SHT11hid;
}
return NULL;
}
/************************
* Konfigurationsdialog *
************************/
SHT11base *comobj;
void TConfig::Changed() {
KillTimer(MainWnd,1);
if (comobj) {
comobj->Close();
delete comobj;
}
comobj=SHT11factory(Config.Where);
if (comobj) {
comobj->verbose=true;
comobj->Open();
if (comobj->ok) {
PostMessage(MainWnd,WM_TIMER,1,0); // sofort loslegen
SetTimer(MainWnd,1,Config.Interval,NULL);
}else{
MySetDlgItemText(108,T("--"));
MySetDlgItemText(110,T("--"));
MySetDlgItemText(112,T("--"));
MySetDlgItemText(114,T("--"));
}
}
}
// Lädt Konfigurationsdaten aus Registrierung
static void LoadConfig() {
static const TConfig DefConfig={
{16,16}, // WinPos
5, // Where: Seriell
0, // SerialNo: COM1
0x378, // ParallelAddr: LPT1
5.0F, // Ucc: 5 V
4, // Sensor-Version (jeweils andere Linearisierungs-Koeffizienten)
0, // USB2LPT native: LPT1
1000, // Abfrage-Intervall in ms
0,0,0,0}; // UsbHid: 0, PnpHid: 0, flags: 0, sht11ctl: 0
HKEY key;
bool ok=false;
if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\SHT11"),
0,KEY_QUERY_VALUE,&key)) {
TCHAR tag[16];
_sntprintf(tag,elemof(tag),T("Config%d"),InstNr);
DWORD size=sizeof(Config);
if (!RegQueryValueEx(key,tag,NULL,NULL,(LPBYTE)&Config,&size)
&& size==sizeof(Config) && Config.Interval) ok=true;
_sntprintf(tag,elemof(tag),T("LogFile%d"),InstNr);
size=sizeof(LogFile);
RegQueryValueEx(key,tag,NULL,NULL,(LPBYTE)&LogFile,&size);
RegCloseKey(key);
}
if (!ok) Config=DefConfig;
Config.Changed();
}
// Speichert Konfigurationsdaten in Registrierung
static void SaveConfig() {
HKEY key;
if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\SHT11"),
0,NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&key,NULL)) return;
TCHAR s[256];
RegSetValue(key,NULL,REG_SZ,s,LoadString(hInstance,0,s,elemof(s))*sizeof(TCHAR));
TCHAR tag[16];
_sntprintf(tag,elemof(tag),T("Config%d"),InstNr);
RegSetValueEx(key,tag,0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
_sntprintf(tag,elemof(tag),T("LogFile%d"),InstNr);
if (*LogFile) RegSetValueEx(key,tag,0,REG_SZ,(LPBYTE)&LogFile,(lstrlen(LogFile)+1)*sizeof(TCHAR));
else RegDeleteValue(key,tag);
RegCloseKey(key);
}
#ifdef _M_IX86
__forceinline long lround(float f) { // "lround" aus avr-gcc 4.x
long i;
_asm fld f
_asm fistp i
return i;
}
#else
# define lround // Win64: Type-Cast rundet nicht, halb so schlimm
#endif
static INT_PTR CALLBACK SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
TConfig *D=(TConfig*)GetWindowLongPtr(Wnd,DWLP_USER);
switch (Msg) {
case WM_INITDIALOG: {
D=new TConfig;
*D=Config;
SetWindowLongPtr(Wnd,DWLP_USER,(LONG_PTR)D);
ild.cbSize=sizeof(ild);
SetupDiGetClassImageList(&ild);
SendDlgItemMessage(Wnd,110,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList);
CheckDlgButton(Wnd,100+D->Where,TRUE);
CheckDlgButton(Wnd,10+D->SensVersion,TRUE);
SendMessage(Wnd,WM_TIMER,2,0);
TCHAR s[32];
_sntprintf(s,elemof(s),T("%g"),D->Ucc);
PK(s);
SetDlgItemText(Wnd,111,s);
_sntprintf(s,elemof(s),T("%g"),D->Interval*0.001F);
PK(s);
SetDlgItemText(Wnd,112,s);
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 13:
case 14:
D->SensVersion=(BYTE)(wParam-10);
break;
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
D->Where=(BYTE)(wParam-100);
SendMessage(Wnd,WM_TIMER,1,0);
break;
case IDOK: {
float f;
if (!GetDlgItemFloat(Wnd,112,&f) || f<0.1) {
SetEditFocus(Wnd,112);
break;
}
D->Interval=(WORD)lround(f*1000);
if (!GetDlgItemFloat(Wnd,111,&D->Ucc)) {
SetEditFocus(Wnd,111);
break;
}
SHT11base*t=SHT11factory(D->Where);
if (t) {
t->OnCb(GetDlgItem(Wnd,110),D);
delete t;
}
Config=*D;
SetTimer(MainWnd,1,D->Interval,NULL);
Config.Changed();
SaveConfig();
}nobreak;
case IDCANCEL: {
if (D) delete D;
SetWindowLongPtr(Wnd,DWLP_USER,0);
EndDialog(Wnd,wParam);
}break;
}break;
case WM_TIMER: switch (wParam) {
case 1: KillTimer(Wnd,1); nobreak; // verbose
case 2: { // silent
SHT11base *t=SHT11factory(D->Where);
if (t) {
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
t->verbose = wParam!=2;
t->Enum(GetDlgItem(Wnd,110),D);
delete t;
}
}break;
}break;
case WM_DEVICECHANGE: {
SetTimer(Wnd,1,1500,NULL);
}break;
case WM_DESTROY: {
SetupDiDestroyClassImageList(&ild);
}break;
}
return FALSE;
}
/**************
* DDE-Server *
**************/
HFONT LargeFont,MediumFont;
HBRUSH BlueBrush;
HBRUSH YellowBrush; // für DDE-Advise-Variablen
ATOM AtomBack;
DWORD DdeInst;
HSZ hszService,hszItem[4];
#define hszTopic hszService
HSZ CreateStringHandle(PCTSTR Str) {
return DdeCreateStringHandle(DdeInst,Str,CP_WINNEUTRAL);
}
void FreeStringHandle(HSZ hsz) {
if (hsz) DdeFreeStringHandle(DdeInst,hsz);
}
int CheckHsz(HSZ hsz) {
// liefert Fenster-ID für zu holenden String, 0 bei Fehler
if (!DdeCmpStringHandles(hsz,hszItem[0])) return 108;
if (!DdeCmpStringHandles(hsz,hszItem[1])) return 110;
if (!DdeCmpStringHandles(hsz,hszItem[2])) return 112;
if (!DdeCmpStringHandles(hsz,hszItem[3])) return 114;
return 0; // alles andere ist Fehler
}
HDDEDATA CALLBACK DdeCallback(UINT type, UINT fmt, HCONV conv,
HSZ hsz1, HSZ hsz2, HDDEDATA data, ULONG_PTR data1, ULONG_PTR data2) {
switch (type) {
case XTYP_CONNECT: {
if (hsz1==hszTopic) {
ErrorPrint(11); // "DDE-Verbindung gestartet"
return (HDDEDATA)TRUE;
}
}break;
case XTYP_DISCONNECT: {
ErrorPrint(12); // "DDE-Verbindung beendet"
}break;
case XTYP_REQUEST:
case XTYP_ADVREQ: {
if (fmt!=CF_TEXT) break;
int id=CheckHsz(hsz2);
if (!id) break;
char s[16];
return DdeCreateDataHandle(DdeInst,(BYTE*)s,
GetDlgItemTextA(MainWnd,id,s,elemof(s))+1,
0,hsz2,fmt,0);
}
case XTYP_ADVSTART:
case XTYP_ADVSTOP: {
if (fmt!=CF_TEXT) break;
int id=CheckHsz(hsz2);
if (!id) break;
HWND w=GetDlgItem(MainWnd,id);
id=(int)GetProp(w,(LPTSTR)AtomBack);
id+=type==XTYP_ADVSTART?+1:-1;
if (id) SetProp(w,(LPTSTR)AtomBack,(HANDLE)id);
else RemoveProp(w,(LPTSTR)AtomBack);
InvalidateRect(w,NULL,TRUE);
return (HDDEDATA)TRUE;
}
}
return (HDDEDATA)FALSE;
}
void DdeStart(void) {
DdeInitialize(&DdeInst,DdeCallback,
CBF_FAIL_POKES|CBF_FAIL_EXECUTES|
CBF_SKIP_REGISTRATIONS|CBF_SKIP_UNREGISTRATIONS,0);
hszService=CreateStringHandle(T("SHT11"));
hszItem[0]=CreateStringHandle(T("T"));
hszItem[1]=CreateStringHandle(T("H"));
hszItem[2]=CreateStringHandle(T("D"));
hszItem[3]=CreateStringHandle(T("A"));
AtomBack=AddAtom(T("DdeAdvises"));
YellowBrush=CreateSolidBrush(0xC0FFFFL);
DdeNameService(DdeInst,hszService,0,DNS_REGISTER);
}
void DdeStop(void) {
DdeUninitialize(DdeInst);
DeleteObject(YellowBrush);
}
/***************
* Haupfenster *
***************/
// Lineare Approximation
static float trafo(float x, float a, float e, float A, float E) {
e-=a;
if (!e) return 0;
return (x-a)*(E-A)/e+A;
}
// Ermittelt D1 in Abhängigkeit von der Betriebsspannung
static float GetD1() {
// Tabellenwerte aus Datenblatt für Temperaturberechnung
static const float u[]={2.5F,3.0F,3.5F,4.0F,5.0F};
static const float d[]={-39.4F,-39.6F,-39.7F,-39.8F,-40.1F};
int i;
for (i=1; i<elemof(u); i++) {
if (Config.Ucc<u[i]) break;
}
return trafo(Config.Ucc,u[i-1],u[i],d[i-1],d[i]);
}
static float GetRH(WORD w, float t, bool w8bit) {
float f;
if (Config.SensVersion==4) {
f=-2.0468F+ (w8bit ? 0.5872F*w-4.0845E-6F*w*w : 0.0367F*w-1.5955E-6F*w*w);
if (f>=99) f=100;
}else{
f=-4+ (w8bit ? 0.648F*w-7.2E-6F*w*w : 0.0405F*w-2.8E-6F*w*w);
f+=(t-25)*(0.01F+0.00008F*w);
}
if (f>100) f=100;
if (f<0) f=0;
return f;
}
static BOOL RetrieveWinPos() { // liefert <TRUE> bei Änderung
WINDOWPLACEMENT WP;
WP.length=sizeof(WP);
GetWindowPlacement(MainWnd,&WP);
POINTS p={(short)WP.rcNormalPosition.left,(short)WP.rcNormalPosition.top};
BOOL ret=*(BOOL*)&Config.WinPos^*(BOOL*)&p; // Bits unterschiedlich?
Config.WinPos=p;
return ret;
}
INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
switch (Msg) {
case WM_INITDIALOG: {
MainWnd=Wnd;
GetWindowText(Wnd,MBoxTitle,elemof(MBoxTitle));
LoadConfig();
HFONT f=(HFONT)SendDlgItemMessage(Wnd,108,WM_GETFONT,0,0);
LOGFONT fnt;
GetObject(f,sizeof(fnt),&fnt);
LargeFont=CreateFont(MulDiv(fnt.lfHeight,40,-13),0,0,0,700,0,0,0,0,0,0,0,0,T("Times"));
MediumFont=CreateFont(MulDiv(fnt.lfHeight,26,-13),0,0,0,700,0,0,0,0,0,0,0,0,T("Times"));
BlueBrush=CreateSolidBrush(0xFFC0C0L);
SendDlgItemMessage(Wnd,108,WM_SETFONT,(WPARAM)LargeFont,0);
SendDlgItemMessage(Wnd,110,WM_SETFONT,(WPARAM)LargeFont,0);
SendDlgItemMessage(Wnd,112,WM_SETFONT,(WPARAM)MediumFont,0);
SendDlgItemMessage(Wnd,114,WM_SETFONT,(WPARAM)MediumFont,0);
DdeStart();
SetTimer(Wnd,1,Config.Interval,NULL);
SendMessage(Wnd,WM_WININICHANGE,0,0);
HICON ico=LoadIcon(hInstance,MAKEINTRESOURCE(100));
SetClassLongPtr(Wnd,GCLP_HICON,(LONG_PTR)ico);
SetClassLongPtr(Wnd,GCLP_HICONSM,(LONG_PTR)ico);
SetWindowPos(Wnd,0,Config.WinPos.x,Config.WinPos.y,0,0,SWP_NOSIZE|SWP_NOZORDER);
STARTUPINFO si;
si.cb=sizeof(si);
GetStartupInfo(&si);
ShowWindow(Wnd,si.wShowWindow);
}return TRUE;
case WM_WININICHANGE: {
GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
}break;
case WM_TIMER: {
static int Fehler;
bool ok=true;
int NK; // Nachkommastellen: 2 (voll), 1 (reduzierte Auflösung)
int w;
float t,f;
TCHAR s[16];
MySetDlgItemText(111,T("")); // Statuszeile leeren
w=comobj->GetRaw(0x07); // Statusregister
_sntprintf(s,elemof(s),T("%02X"),(BYTE)w); MySetDlgItemText(102,s);
SetCheckboxGroup(Wnd,32,35,w&7|(w>>4)&8);
NK=w&1?1:2;
w=comobj->GetRaw(0x03); // Temperatur
_sntprintf(s,elemof(s),T("%0*X"),NK+2,(WORD)w); MySetDlgItemText(107,s);
if (w>=0) {
t=GetD1()+ (NK==1 ? 0.04F: 0.01F)*w;
_sntprintf(s,elemof(s),T("%.*f °C"),NK,t);
PK(s);
MySetDlgItemText(108,s);
DdePostAdvise(DdeInst,hszTopic,hszItem[0]);
Fehler=0;
}else{
ok=false;
Fehler++; // Ab 10 Fehler Fenstertext löschen!
if (Fehler>10) MySetDlgItemText(108,T("--"));
}
w=comobj->GetRaw(0x05);
_sntprintf(s,elemof(s),T("%0*X"),NK+1,(WORD)w); MySetDlgItemText(109,s);
if (w>=0) {
f=GetRH(w,t,NK==1);
_sntprintf(s,elemof(s),T("%.*f %%"),NK,f);
PK(s);
MySetDlgItemText(110,s);
DdePostAdvise(DdeInst,hszTopic,hszItem[1]);
Fehler=0;
}else{
ok=false;
Fehler++;
if (Fehler>10) MySetDlgItemText(110,T("--"));
}
if (ok) {
/* Taupunkt, in °C */
f*=0.01F; // 0..1
float M = t>=0 ? 17.62F : 22.46F; //dimensionslos; über Wasser oder über Eis
float N = t>=0 ? 243.12F : 272.62F; //[°C]
float d = -273.15F;
if (f) {
float h = (float)log(f); //dimensionslos
float j = M*t/(N+t); //dimensionslos
d = N*(h+j)/(M-h-j); //[°C]
}
_sntprintf(s,elemof(s),T("%.*f °C"),NK,d);
PK(s);
MySetDlgItemText(112,s);
DdePostAdvise(DdeInst,hszTopic,hszItem[2]);
/* absolute Feuchte, in g/m³ */
float a=216.7F*(f*6.112F*(float)exp(17.62F*t/(243.12F+t))/(273.15F+t));
_sntprintf(s,elemof(s),T("%.*f g/m³"),NK,a);
PK(s);
MySetDlgItemText(114,s);
DdePostAdvise(DdeInst,hszTopic,hszItem[3]);
}else if (Fehler>10) {
MySetDlgItemText(112,T("--"));
MySetDlgItemText(114,T("--"));
}
if (IsIconic(Wnd)) goto settitle;
}break;
case WM_SIZE: if (wParam==SIZEICONIC) {
TCHAR buf[32];
settitle:
GetDlgItemText(Wnd,108,buf,16);
lstrcat(buf,T("; "));
GetDlgItemText(Wnd,110,buf+lstrlen(buf),16);
SetWindowText(Wnd,buf);
}else{
SendDlgItemMessage(Wnd,111,Msg,wParam,lParam);
}break;
case WM_QUERYOPEN: {
SetWindowText(Wnd,MBoxTitle);
}break;
case WM_CTLCOLORSTATIC: {
switch (GetDlgCtrlID((HWND)lParam)) {
case 108:
case 110:
case 112:
case 114: {
SetBkMode((HDC)wParam,TRANSPARENT);
if (GetProp((HWND)lParam,(LPTSTR)AtomBack)) return (BOOL)YellowBrush;
}return (BOOL)BlueBrush;
}
}break;
case WM_DEVICECHANGE: {
if (comobj && !comobj->ok) {
comobj->Close();
comobj->verbose=false;
comobj->Open();
}
}break;
case WM_COMMAND: switch (LOWORD(wParam)){
case 48:
case 49:
case 50: {
// ConnectionReset();
if (comobj->SetRaw(0x06,GetCheckboxGroup(Wnd,48,50))<0) ErrorPrint(10); //"Schreibversuch"
}break;
case 11: DialogBox(hInstance,MAKEINTRESOURCE(101),Wnd,SetupDlgProc); break;
case 1:
case 2:
if (RetrieveWinPos()) SaveConfig();
Config.Where=0;
Config.Changed(); // Handles ordentlich schließen
EndDialog(Wnd,wParam);
}break;
case WM_ENDSESSION: if (wParam) {
if (RetrieveWinPos()) SaveConfig();
}break;
case WM_DESTROY: {
DdeStop();
DeleteObject(LargeFont);
DeleteObject(MediumFont);
DeleteObject(BlueBrush);
}break;
}
return FALSE;
}
INT_PTR CALLBACK WinMainCRTStartup() {
hInstance=GetModuleHandle(NULL);
INITCOMMONCONTROLSEX icc={sizeof(icc),ICC_USEREX_CLASSES};
InitCommonControlsEx(&icc);
return DialogBox(hInstance,MAKEINTRESOURCE(100),0,MainDlgProc);
}
Detected encoding: ANSI (CP1252) | 4
|
|