Merge branch 'master' into flashnew

# Conflicts:
#	src/rp2040_arduino_platform.cpp
#	src/rp2040_arduino_platform.h
This commit is contained in:
SirSydom 2022-02-10 00:09:48 +01:00
commit 9098478614
63 changed files with 1631 additions and 523 deletions

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<KNX xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" CreatedBy="KNX MT" ToolVersion="5.6.407.26745" xmlns="http://knx.org/xml/project/11">
<ManufacturerData>
<Manufacturer RefId="M-00FA">
<Catalog>
<CatalogSection Id="M-00FA_CS-1" Name="Geräte" Number="1" DefaultLanguage="en-US">
<CatalogItem Id="M-00FA_H-12288-1_HP-1074-0B-0000_CI-EC05-1" Name="KNX to 433mhz (DIO)" Number="1" ProductRefId="M-00FA_H-12288-1_P-EC05" Hardware2ProgramRefId="M-00FA_H-12288-1_HP-1074-0B-0000" DefaultLanguage="en-US" />
</CatalogSection>
</Catalog>
<ApplicationPrograms>
<ApplicationProgram Id="M-00FA_A-1074-0B-0000" ApplicationNumber="4212" ApplicationVersion="11" ProgramType="ApplicationProgram" MaskVersion="MV-07B0" Name="SAMD-DIO" LoadProcedureStyle="MergedProcedure" PeiType="0" DefaultLanguage="en-US" DynamicTableManagement="false" Linkable="false" MinEtsVersion="4.0">
<Static>
<Code>
<RelativeSegment Id="M-00FA_A-1074-0B-0000_RS-04-00000" Name="Parameters" Offset="0" Size="34" LoadStateMachine="4" />
</Code>
<ParameterTypes>
<ParameterType Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout" Name="StartupTimeout">
<TypeRestriction Base="Value" SizeInBit="8">
<Enumeration Text="Disabled" Value="0" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-0" />
<Enumeration Text="1 s" Value="1" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-1" />
<Enumeration Text="2 s" Value="2" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-2" />
<Enumeration Text="3 s" Value="3" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-3" />
<Enumeration Text="4 s" Value="4" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-4" />
<Enumeration Text="5 s" Value="5" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-5" />
<Enumeration Text="6 s" Value="6" Id="M-00FA_A-1074-0B-0000_PT-StartupTimeout_EN-6" />
</TypeRestriction>
</ParameterType>
<ParameterType Id="M-00FA_A-1074-0B-0000_PT-TimeCycle" Name="TimeCycle">
<TypeRestriction Base="Value" SizeInBit="8">
<Enumeration Text="Disabled" Value="0" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-0" />
<Enumeration Text="1 sec" Value="1" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-1" />
<Enumeration Text="5 sec" Value="2" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-2" />
<Enumeration Text="15 sec" Value="3" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-3" />
<Enumeration Text="1 min" Value="4" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-4" />
<Enumeration Text="5 min" Value="5" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-5" />
<Enumeration Text="15 min" Value="6" Id="M-00FA_A-1074-0B-0000_PT-TimeCycle_EN-6" />
</TypeRestriction>
</ParameterType>
<ParameterType Id="M-00FA_A-1074-0B-0000_PT-pulseValue" Name="pulseValue">
<TypeNumber SizeInBit="32" Type="signedInt" minInclusive="-2147483648" maxInclusive="2147483647" />
</ParameterType>
</ParameterTypes>
<Parameters>
<Parameter Id="M-00FA_A-1074-0B-0000_P-1" Name="startupTimeout" ParameterType="M-00FA_A-1074-0B-0000_PT-StartupTimeout" Text="Startup delaytime" Value="1">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="0" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-2" Name="timeCycleRefresh" ParameterType="M-00FA_A-1074-0B-0000_PT-TimeCycle" Text="refresh period" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="1" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-3" Name="value1" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 1 On" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="2" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-4" Name="value2" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 1 Off" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="6" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-5" Name="value3" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 2 On" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="10" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-6" Name="value4" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 2 Off" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="14" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-7" Name="value5" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 3 On" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="18" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-8" Name="value6" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Button 3 Off" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="22" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-9" Name="value7" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Shift On All" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="26" BitOffset="0" />
</Parameter>
<Parameter Id="M-00FA_A-1074-0B-0000_P-10" Name="value8" ParameterType="M-00FA_A-1074-0B-0000_PT-pulseValue" Text="Shift Off All" Value="0">
<Memory CodeSegment="M-00FA_A-1074-0B-0000_RS-04-00000" Offset="30" BitOffset="0" />
</Parameter>
</Parameters>
<ParameterRefs>
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-1_R-1" RefId="M-00FA_A-1074-0B-0000_P-1" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-2_R-2" RefId="M-00FA_A-1074-0B-0000_P-2" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-3_R-3" RefId="M-00FA_A-1074-0B-0000_P-3" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-4_R-4" RefId="M-00FA_A-1074-0B-0000_P-4" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-5_R-5" RefId="M-00FA_A-1074-0B-0000_P-5" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-6_R-6" RefId="M-00FA_A-1074-0B-0000_P-6" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-7_R-7" RefId="M-00FA_A-1074-0B-0000_P-7" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-8_R-8" RefId="M-00FA_A-1074-0B-0000_P-8" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-9_R-9" RefId="M-00FA_A-1074-0B-0000_P-9" />
<ParameterRef Id="M-00FA_A-1074-0B-0000_P-10_R-10" RefId="M-00FA_A-1074-0B-0000_P-10" />
</ParameterRefs>
<ComObjectTable>
<ComObject Id="M-00FA_A-1074-0B-0000_O-1" Name="trigger value 1" Text="trigger value 1" Number="1" FunctionText="get/set button 1" ObjectSize="1 Bit" ReadFlag="Enabled" WriteFlag="Enabled" CommunicationFlag="Enabled" TransmitFlag="Enabled" UpdateFlag="Disabled" ReadOnInitFlag="Disabled" />
<ComObject Id="M-00FA_A-1074-0B-0000_O-2" Name="trigger value 2" Text="trigger value 2" Number="2" FunctionText="get/set button 2" ObjectSize="1 Bit" ReadFlag="Enabled" WriteFlag="Enabled" CommunicationFlag="Enabled" TransmitFlag="Enabled" UpdateFlag="Disabled" ReadOnInitFlag="Disabled" />
<ComObject Id="M-00FA_A-1074-0B-0000_O-3" Name="trigger value 3" Text="trigger value 3" Number="3" FunctionText="get/set button 3" ObjectSize="1 Bit" ReadFlag="Enabled" WriteFlag="Enabled" CommunicationFlag="Enabled" TransmitFlag="Enabled" UpdateFlag="Disabled" ReadOnInitFlag="Disabled" />
<ComObject Id="M-00FA_A-1074-0B-0000_O-4" Name="trigger value 4" Text="trigger value 4" Number="4" FunctionText="get/set All buttons" ObjectSize="1 Bit" ReadFlag="Enabled" WriteFlag="Enabled" CommunicationFlag="Enabled" TransmitFlag="Enabled" UpdateFlag="Disabled" ReadOnInitFlag="Disabled" />
<ComObject Id="M-00FA_A-1074-0B-0000_O-5" Name="trigger progMode" Text="trigger progMode" Number="5" FunctionText="put device in ETS programmation" ObjectSize="1 Bit" ReadFlag="Disabled" WriteFlag="Enabled" CommunicationFlag="Enabled" TransmitFlag="Enabled" UpdateFlag="Disabled" ReadOnInitFlag="Disabled" />
</ComObjectTable>
<ComObjectRefs>
<ComObjectRef Id="M-00FA_A-1074-0B-0000_O-1_R-1" RefId="M-00FA_A-1074-0B-0000_O-1" />
<ComObjectRef Id="M-00FA_A-1074-0B-0000_O-2_R-2" RefId="M-00FA_A-1074-0B-0000_O-2" />
<ComObjectRef Id="M-00FA_A-1074-0B-0000_O-3_R-3" RefId="M-00FA_A-1074-0B-0000_O-3" />
<ComObjectRef Id="M-00FA_A-1074-0B-0000_O-4_R-4" RefId="M-00FA_A-1074-0B-0000_O-4" />
<ComObjectRef Id="M-00FA_A-1074-0B-0000_O-5_R-5" RefId="M-00FA_A-1074-0B-0000_O-5" />
</ComObjectRefs>
<AddressTable MaxEntries="65000" />
<AssociationTable MaxEntries="65000" />
<LoadProcedures>
<LoadProcedure MergeId="2">
<LdCtrlRelSegment LsmIdx="4" Size="34" Mode="0" Fill="0" AppliesTo="full" />
</LoadProcedure>
<LoadProcedure MergeId="4">
<LdCtrlWriteRelMem ObjIdx="4" Offset="0" Size="34" Verify="true" />
</LoadProcedure>
</LoadProcedures>
<Options />
</Static>
<Dynamic>
<ChannelIndependentBlock>
<ParameterBlock Id="M-00FA_A-1074-0B-0000_PB-1" Name="ParameterPage" Text="Common Parameters">
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-1_R-1" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-2_R-2" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-3_R-3" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-4_R-4" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-5_R-5" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-6_R-6" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-7_R-7" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-8_R-8" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-9_R-9" />
<ParameterRefRef RefId="M-00FA_A-1074-0B-0000_P-10_R-10" />
<ComObjectRefRef RefId="M-00FA_A-1074-0B-0000_O-1_R-1" />
<ComObjectRefRef RefId="M-00FA_A-1074-0B-0000_O-2_R-2" />
<ComObjectRefRef RefId="M-00FA_A-1074-0B-0000_O-3_R-3" />
<ComObjectRefRef RefId="M-00FA_A-1074-0B-0000_O-4_R-4" />
<ComObjectRefRef RefId="M-00FA_A-1074-0B-0000_O-5_R-5" />
</ParameterBlock>
</ChannelIndependentBlock>
</Dynamic>
</ApplicationProgram>
</ApplicationPrograms>
<Hardware>
<Hardware Id="M-00FA_H-12288-1" Name="SAMD21-KNXto433" SerialNumber="12288" VersionNumber="1" BusCurrent="10" HasIndividualAddress="true" HasApplicationProgram="true">
<Products>
<Product Id="M-00FA_H-12288-1_P-EC05" Text="KNX to 433mhz (DIO)" OrderNumber="EC05" IsRailMounted="false" DefaultLanguage="en-US">
<RegistrationInfo RegistrationStatus="Registered" />
</Product>
</Products>
<Hardware2Programs>
<Hardware2Program Id="M-00FA_H-12288-1_HP-1074-0B-0000" MediumTypes="MT-0">
<ApplicationProgramRef RefId="M-00FA_A-1074-0B-0000" />
<RegistrationInfo RegistrationStatus="Registered" RegistrationNumber="0001/111" />
</Hardware2Program>
</Hardware2Programs>
</Hardware>
</Hardware>
</Manufacturer>
</ManufacturerData>
</KNX>

View File

@ -0,0 +1,32 @@
Control Chacon/DIO 433Mhz plug like that: (not an affiliated link just for info)
https://www.amazon.fr/DiO-Connected-Home-t%C3%A9l%C3%A9command%C3%A9es-t%C3%A9l%C3%A9commande/dp/B005LKMAW0
Hardware:
- Samd21 (gy-samd21)
- FS 1000A (cheap 433 transmitter)
- TpUart KNX
- logic level converter
- Chacon/DIO 433Mhz plug (must work with every dumb 433mhz plug/remote that support the HomeEasy protocol)
Software
- change the rfPin variable, compile and tranfert into the samd21 (or any microcontroller)
- (maybe) adapt THIGH and TLOW in rfCode class for your case.
Before configuring ETS, you need to receive the 433mhz code for each button of your remote.
Take an arduino UNO, plug the FS1000a receiver, look on github/google to receive the code rc-switch or https://charleslabs.fr/fr/project-Contr%C3%B4le+de+prises+DiO+avec+Arduino (in french), or https://caron.ws/diy-cartes-microcontroleurs/piloter-vos-prises-chacon-rf433mhz-arduino/ ( in french too)
for me it's : 1806947472 and 1806947456 for the channel 1 on/off, etc...
Now in ETS put the received 433Mhz code into parameters.
-> Full Download in ETS and enjoy.
Feature:
This code is delay() free, blocking code free (ie: no long while or for loop), to call the knx.loop() as fast as possible.

Binary file not shown.

View File

@ -0,0 +1,368 @@
#include <knx.h>
//#include <DiOremote.h>
//#define DEBUGSERIAL 1
#ifdef DEBUGSERIAL
#define DPRINT(...) SerialUSB.print(__VA_ARGS__)
#define DPRINTLN(...) SerialUSB.println(__VA_ARGS__)
#include <MemoryFree.h>
#include <pgmStrToRAM.h>
#else
#define DPRINT(...) //now defines a blank line
#define DPRINTLN(...) //now defines a blank line
#endif
#define goButton1 knx.getGroupObject(1)
#define goButton2 knx.getGroupObject(2)
#define goButton3 knx.getGroupObject(3)
#define goButtonAll knx.getGroupObject(4)
#define goProgMode knx.getGroupObject(5)
//DiOremote myRemote = DiOremote(6);
//void function2();
//void function1(void (*)());
//void loop() {
// function1(function2);
//}
//Global Const
const uint8_t ets_startupTimeout[7] = {0, 1, 2, 3, 4, 5, 6};
const uint16_t ets_timePeriod[7] = {0, 1, 5, 15, 1 * 60, 5 * 60, 15 * 60};
const uint8_t ets_progMode[7] = {0, 1, 2 * 60, 3 * 60, 4 * 60, 5 * 60, 10 * 60}; //need knxprod update... ?
const uint8_t ledPin = LED_BUILTIN;
const uint8_t rfPin = 6;
// //Protocol timing (in us)
// #define DiOremote_START_FRAME_1 220
// #define DiOremote_START_FRAME_0 2675
// #define DiOremote_THIGH 220
// #define DiOremote_TLOW_0 350 short
// #define DiOremote_TLOW_1 1400 long
// #define DiOremote_END_FRAME_1 220
// #define DiOremote_END_FRAME_0 10600
// Global Variable
bool progMode = true;
// bool codeSendindBlock = false;
uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ?
uint32_t timePeriod = 0; // same here,
uint32_t timerProgMode = 0; // same here,
uint32_t ch1_on, ch1_off, ch2_on, ch2_off, ch3_on, ch3_off, chall_on, chall_off;
class RfCode {
private:
uint8_t _rfPin;
uint32_t _codeValueOn;
uint32_t _codeValueOff;
uint8_t _GOaddress;
const uint16_t THIGH = 220, TSTART = 2675, TSHORT = 220, TLONG = 1400, TEND = 10600;
uint8_t _loopCount = 0; // fixed ==5
bool lastState = false;
uint32_t _codePending = 0;
uint32_t _codePendingMemory = _codePending;
uint8_t _loopPending = 0; // fixed ==32
bool _sendMsgPending= false;
enum PulseStates {
PULSE_INIT,
PULSE_KEY1, //32 times loop
PULSE_KEY2, //32 times loop
PULSE_END
};
PulseStates pulseStates = PULSE_INIT;
bool pulseSend(const uint16_t delayHigh, const uint16_t delayLow){ //alike state machine?
static bool initPulse = false;
static bool highDone = false;
static uint32_t pulseLastTime = 0;
uint32_t currentTime = micros();
if (!initPulse){
initPulse = true;
digitalWrite(_rfPin, HIGH);
pulseLastTime = currentTime;
}
else if (currentTime - pulseLastTime >= delayHigh && !highDone)
{
digitalWrite(_rfPin, LOW);
pulseLastTime = currentTime;
highDone = true;
}
else if (currentTime - pulseLastTime >= delayLow && highDone)
{
initPulse = false;
highDone = false;
}
return !initPulse;
}
uint16_t TTime(bool invert){
uint16_t TTIME;
if (_codePending & 0x80000000L)// future bug if uint32_t _codePengin > 2^32 / 2 ?
{
TTIME = invert ? TSHORT : TLONG;
}
else
{
TTIME = invert ? TLONG : TSHORT;
}
return TTIME;
}
public:
RfCode(uint8_t rfPin){
_rfPin = rfPin;
pinMode(_rfPin, OUTPUT);
}
// void init(uint8_t addr, uint32_t codeValueOn, uint32_t codeValueOff){ //GroupObject &device, long timeOn){
// _codeValueOn = codeValueOn;
// _codeValueOff = codeValueOff;
// _GOaddress = addr;
// }
void setState(bool modeOnOff, uint32_t codeValueOn, uint32_t codeValueOff){
if (!_sendMsgPending)
{
_sendMsgPending = true;
_codeValueOn = codeValueOn;
_codeValueOff = codeValueOff;
// _GOaddress = addr;
if (modeOnOff){
_codePending = _codeValueOn;
// SerialUSB.println(_codePending);
}
else
{
_codePending = _codeValueOff;
// SerialUSB.println(_codePending);
}
_codePendingMemory = _codePending;
}
}
// bool getMsgPendingState(){
// return _sendMsgPending;
// }
void loop(){
if (_sendMsgPending)
{
// needed to block setState of another Class, yes FIFO is better...
// codeSendindBlock = true;
if (_loopCount < 5)
{
switch (pulseStates){
case PULSE_INIT:
if (pulseSend(THIGH, TSTART)){
pulseStates = PULSE_KEY1;
}
break;
case PULSE_KEY1:
if (pulseSend(THIGH, TTime(false)))
{
pulseStates = PULSE_KEY2;
}
break;
case PULSE_KEY2:
if (pulseSend(THIGH, TTime(true)))
{
if (_loopPending < 32){
_codePending <<= 1;
_loopPending++;
pulseStates = PULSE_KEY1; // next loop !
}
else{ // finish!
pulseStates = PULSE_END;
}
}
break;
case PULSE_END:
if (pulseSend(THIGH, TEND)){
_loopCount++;
_codePending = _codePendingMemory;
_loopPending = 0;
pulseStates = PULSE_INIT;
}
break;
default:
break;
}
}
else
{
_loopCount = 0;
_sendMsgPending = false;
// codeSendindBlock = false;
}
}
}
};
class Blinker
{
private:
uint8_t ledPin_; // the number of the LED pin
uint32_t OnTime = 1000; // milliseconds of on-time
uint32_t OffTime = 1000; // milliseconds of off-time
bool ledState = LOW; // ledState used to set the LED
uint32_t previousMillis; // will store last time LED was updated
void setOutput(bool state_, uint32_t currentMillis_){
ledState = state_;
previousMillis = currentMillis_;
digitalWrite(ledPin_, state_);
}
public:
Blinker(uint8_t pin)
{
ledPin_ = pin;
pinMode(ledPin_, OUTPUT);
previousMillis = 0;
}
void set(uint32_t on, uint32_t off){
OnTime = on;
OffTime = off;
}
void loop(){
uint32_t currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
setOutput(LOW, currentMillis);
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
setOutput(HIGH, currentMillis);
}
}
};
RfCode button = RfCode(5);
Blinker led = Blinker(ledPin);
void callBackButton1(GroupObject& go){
button.setState((bool)go.value(), ch1_on, ch1_off);
}
void callBackButton2(GroupObject& go){
button.setState((bool)go.value(), ch2_on, ch2_off);
}
void callBackButton3(GroupObject& go){
button.setState((bool)go.value(), ch3_on, ch3_off);
}
void callBackButtonAll(GroupObject& go){
button.setState((bool)go.value(), chall_on, ch3_off);
}
void callBackProgMode(GroupObject& go){
progMode = (bool)go.value();
}
void setup() {
// #ifdef DEBUGSERIAL
// SerialUSB.begin(9600);
// while (!SerialUSB) { //wait for DEBUGING
// ; // wait for serial port to connect. Needed for native USB port only
// }
// ArduinoPlatform::SerialDebug = &SerialUSB;
// #endif
randomSeed(millis());
// knx.bau().deviceObject().individualAddress(1);
knx.readMemory();
if (knx.configured())
{
DPRINT("Setup: KNX Configuration...");
progMode = false; // don't need to put device in progMode.
int confStartupTime = ets_startupTimeout[knx.paramByte(0)] * 1000;
delay(confStartupTime); // the only delay used, why make a withoutDelay function for that?
timePeriod = ets_timePeriod[knx.paramByte(1)] * 1000;
// timerProgMode = ets_progMode[knx.paramByte(34)] * 1000;
ch1_on = knx.paramInt(2);
ch1_off = knx.paramInt(6);
goButton1.callback(callBackButton1);
goButton1.dataPointType(DPT_Switch);
ch2_on = knx.paramInt(10);
ch2_off = knx.paramInt(14);
goButton2.callback(callBackButton2);
goButton2.dataPointType(DPT_Switch);
ch3_on = knx.paramInt(18);
ch3_off = knx.paramInt(22);
goButton3.callback(callBackButton3);
goButton3.dataPointType(DPT_Switch);
chall_on = knx.paramByte(26);
chall_off = knx.paramByte(30);
goButtonAll.callback(callBackButtonAll);
goButtonAll.dataPointType(DPT_Switch);
goProgMode.callback(callBackProgMode);
goProgMode.dataPointType(DPT_Trigger);
DPRINTLN("Finished");
}
knx.ledPin(5);
knx.ledPinActiveOn(HIGH);
knx.buttonPin(9);
knx.start();
led.set(2000, 2000);
}
void loop()
{
knx.loop();
led.loop();
if (knx.configured() && !progMode)
{
button.loop();
}
else if (progMode)
{
prodModeLoop();
}
}
void prodModeLoop(){ // run Only if progMode triggered ( at start or callback)
const uint32_t timerProgMode = ( 15 * 60 * 1000 ) ; // 15min
static uint32_t timerProgPrevMillis = 0;
if (!knx.progMode())
{
knx.progMode(true);
led.set(500, 500);
timerProgPrevMillis = millis();
DPRINTLN("progModeLoop Start");
}
else
{
if (millis() - timerProgPrevMillis > timerProgMode) {
knx.progMode(false);
goProgMode.value(false);
progMode = 0;
led.set(100, 100); // panic!
DPRINTLN("progModeLoop Stop");
}
}
}

View File

@ -25,7 +25,7 @@
void checkIaqSensorStatus(void); void checkIaqSensorStatus(void);
void errLeds(void); void errLeds(void);
uint8_t* saveBme680State(uint8_t* buffer); uint8_t* saveBme680State(uint8_t* buffer);
uint8_t* loadBme680State(uint8_t* buffer); const uint8_t* loadBme680State(const uint8_t* buffer);
void triggerCallback(GroupObject& go); void triggerCallback(GroupObject& go);
void updateState(); void updateState();
@ -53,6 +53,10 @@ void setup(void)
wifiManager.autoConnect("knx-bme680"); wifiManager.autoConnect("knx-bme680");
#endif #endif
// set save and restore callbacks
knx.setSaveCallback(saveBme680State);
knx.setRestoreCallback(loadBme680State);
// read adress table, association table, groupobject table and parameters from eeprom // read adress table, association table, groupobject table and parameters from eeprom
knx.readMemory(); knx.readMemory();
@ -60,7 +64,6 @@ void setup(void)
if(knx.configured()) if(knx.configured())
goTriggerSample.callback(triggerCallback); goTriggerSample.callback(triggerCallback);
// Configure Wire pins before this call if needed. // Configure Wire pins before this call if needed.
Wire.begin(); Wire.begin();
// depends on sensor board. Try BME680_I2C_ADDR_PRIMARY if it doen't work. // depends on sensor board. Try BME680_I2C_ADDR_PRIMARY if it doen't work.
@ -87,9 +90,6 @@ void setup(void)
BSEC_OUTPUT_GAS_PERCENTAGE BSEC_OUTPUT_GAS_PERCENTAGE
}; };
knx.setSaveCallback(saveBme680State);
knx.setRestoreCallback(loadBme680State);
if (knx.configured()) if (knx.configured())
{ {
cyclSend = knx.paramInt(0); cyclSend = knx.paramInt(0);
@ -214,7 +214,7 @@ void errLeds(void)
delay(100); delay(100);
} }
uint8_t* loadBme680State(uint8_t* buffer) const uint8_t* loadBme680State(const uint8_t* buffer)
{ {
// Existing state in EEPROM // Existing state in EEPROM
Serial.println("Reading state from EEPROM"); Serial.println("Reading state from EEPROM");

View File

@ -0,0 +1,129 @@
#include <knx.h>
#ifdef ARDUINO_ARCH_ESP8266
#include <WiFiManager.h>
#endif
/*****************************************
* changes necessary for SMALL_GROUPOBJECT
* are commented with //**
* This project can be used with any
* of the knxprod files of the original
* knx-demo project.
*****************************************/
// create named references for easy access to group objects
#define goCurrent knx.getGroupObject(1)
#define goMax knx.getGroupObject(2)
#define goMin knx.getGroupObject(3)
#define goReset knx.getGroupObject(4)
float currentValue = 0;
float maxValue = 0;
float minValue = RAND_MAX;
long lastsend = 0;
void measureTemp()
{
long now = millis();
if ((now - lastsend) < 2000)
return;
lastsend = now;
int r = rand();
currentValue = (r * 1.0) / (RAND_MAX * 1.0);
currentValue *= 100 * 100;
// write new value to groupobject
goCurrent.value(currentValue, DPT_Value_Temp); //** each value access needs to done with according DPT parameter
if (currentValue > maxValue)
{
maxValue = currentValue;
goMax.value(maxValue, DPT_Value_Temp); //** each value access needs to done with according DPT parameter
}
if (currentValue < minValue)
{
minValue = currentValue;
goMin.value(minValue, DPT_Value_Temp); //** each value access needs to done with according DPT parameter
}
}
// callback from reset-GO
void resetCallback(GroupObject& go)
{
//** callbacks are now handled in the class, not per instance,
//** this means, we have to check, which GroupObject is calling back
if (go.asap() == goReset.asap())
{
if (go.value(DPT_Trigger)) //** each value access needs to done with according DPT parameter
{
maxValue = 0;
minValue = 10000;
}
}
}
void setup()
{
Serial.begin(115200);
ArduinoPlatform::SerialDebug = &Serial;
randomSeed(millis());
#ifdef ARDUINO_ARCH_ESP8266
WiFiManager wifiManager;
wifiManager.autoConnect("knx-demo");
#endif
// read adress table, association table, groupobject table and parameters from eeprom
knx.readMemory();
// print values of parameters if device is already configured
if (knx.configured())
{
// register callback for reset GO
GroupObject::classCallback(resetCallback); //** callbacks are now handled per class, not per instance
//** there is no global assignment of DPT for GroupObjects
// goReset.dataPointType(DPT_Trigger);
// goCurrent.dataPointType(DPT_Value_Temp);
// goMin.dataPointType(DPT_Value_Temp);
// goMax.dataPointType(DPT_Value_Temp);
Serial.print("Timeout: ");
Serial.println(knx.paramByte(0));
Serial.print("Zykl. senden: ");
Serial.println(knx.paramByte(1));
Serial.print("Min/Max senden: ");
Serial.println(knx.paramByte(2));
Serial.print("Aenderung senden: ");
Serial.println(knx.paramByte(3));
Serial.print("Abgleich: ");
Serial.println(knx.paramByte(4));
}
// pin or GPIO the programming led is connected to. Default is LED_BUILTIN
// knx.ledPin(LED_BUILTIN);
// is the led active on HIGH or low? Default is LOW
// knx.ledPinActiveOn(HIGH);
// pin or GPIO programming button is connected to. Default is 0
// knx.buttonPin(0);
// Is the interrup created in RISING or FALLING signal? Default is RISING
// knx.buttonPinInterruptOn(FALLING);
// start the framework.
knx.start();
}
void loop()
{
// don't delay here to much. Otherwise you might lose packages or mess up the timing with ETS
knx.loop();
// only run the application code if the device was configured with ETS
if (!knx.configured())
return;
measureTemp();
}

View File

@ -0,0 +1,120 @@
;PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
; We have to keep libdeps dir out the project directory otherwise,
; library scanner seems to have issues so compilation fails
libdeps_dir = /tmp/libdeps
src_dir = .
;--- SAMD --------------------------------------------------
; SMALL_GROUPOBJECT just tested with TP on SAMD, but should work also in other environments
[env:zeroUSB]
platform = atmelsam
board = zeroUSB
framework = arduino
; We consider that the this projects is opened within its project directory
; while working with VS Code.
lib_extra_dirs = ../../../
lib_deps =
SPI
https://github.com/thelsing/FlashStorage.git
knx
build_flags =
-DMASK_VERSION=0x07B0
-DSMALL_GROUPOBJECT
-Wno-unknown-pragmas
; [env:adafruit_feather_m0_rf]
; platform = atmelsam
; board = adafruit_feather_m0
; framework = arduino
; ; We consider that the this projects is opened within its project directory
; ; while working with VS Code.
; lib_extra_dirs = ../../../
; lib_deps =
; SPI
; https://github.com/thelsing/FlashStorage.git
; knx
; build_flags =
; -DMASK_VERSION=0x27B0
; -Wno-unknown-pragmas
;-----------------------------------------------------------
;--- ESP8266 -----------------------------------------------
#[env:nodemcuv2_ip]
#platform = espressif8266
#board = nodemcuv2
#framework = arduino
; We consider that the this projects is opened within its project directory
; while working with VS Code.
#lib_extra_dirs = ../../../
#lib_deps =
# WifiManager
# knx
#build_flags =
# -DMASK_VERSION=0x57B0
# -Wno-unknown-pragmas
; [env:nodemcuv2_tp]
; platform = espressif8266
; board = nodemcuv2
; framework = arduino
; ; We consider that the this projects is opened within its project directory
; ; while working with VS Code.
; lib_extra_dirs = ../../../
; lib_deps =
; WifiManager
; knx
; build_flags =
; -DMASK_VERSION=0x07B0
; -Wno-unknown-pragmas
;---------------------------------------------------------
;--- ESP32 -----------------------------------------------
; [env:esp32dev_ip]
; platform = espressif32
; board = esp32dev
; framework = arduino
; ; We consider that the this projects is opened within its project directory
; ; while working with VS Code.
; lib_extra_dirs = ../../../
; lib_deps =
; knx
; build_flags =
; -DMASK_VERSION=0x57B0
; -Wno-unknown-pragmas
; [env:esp32dev_tp]
; platform = espressif32
; board = esp32dev
; framework = arduino
; ; We consider that the this projects is opened within its project directory
; ; while working with VS Code.
; lib_extra_dirs = ../../../
; lib_deps =
; knx
; build_flags =
; -DMASK_VERSION=0x07B0
; -Wno-unknown-pragmas

View File

@ -1,6 +1,7 @@
#include <Arduino.h>
#include <knx.h> #include <knx.h>
#ifdef ARDUINO_ARCH_ESP8266 #if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32)
#include <WiFiManager.h> #include <WiFiManager.h>
#endif #endif
@ -59,7 +60,7 @@ void setup()
randomSeed(millis()); randomSeed(millis());
#ifdef ARDUINO_ARCH_ESP8266 #if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32)
WiFiManager wifiManager; WiFiManager wifiManager;
wifiManager.autoConnect("knx-demo"); wifiManager.autoConnect("knx-demo");
#endif #endif

View File

@ -40,7 +40,7 @@ build_flags =
-DUSE_DATASECURE -DUSE_DATASECURE
[env:nodemcuv2_tp] [env:nodemcuv2_tp]
platform = espressif8266 platform = espressif8266@^2
board = nodemcuv2 board = nodemcuv2
framework = arduino framework = arduino
lib_deps = lib_deps =
@ -61,6 +61,7 @@ platform = espressif32
board = esp32dev board = esp32dev
framework = arduino framework = arduino
lib_deps = lib_deps =
https://github.com/tzapu/WiFiManager.git
knx knx
build_flags = build_flags =

View File

@ -51,7 +51,7 @@ build_flags =
# -Wno-unknown-pragmas # -Wno-unknown-pragmas
[env:nodemcuv2_tp] [env:nodemcuv2_tp]
platform = espressif8266 platform = espressif8266@^2
board = nodemcuv2 board = nodemcuv2
framework = arduino framework = arduino
; We consider that the this projects is opened within its project directory ; We consider that the this projects is opened within its project directory
@ -59,7 +59,7 @@ framework = arduino
lib_extra_dirs = ../../../ lib_extra_dirs = ../../../
lib_deps = lib_deps =
WifiManager WifiManager@0.15.0
knx knx
build_flags = build_flags =
@ -79,6 +79,7 @@ framework = arduino
lib_extra_dirs = ../../../ lib_extra_dirs = ../../../
lib_deps = lib_deps =
https://github.com/tzapu/WiFiManager.git
knx knx
build_flags = build_flags =

View File

@ -82,7 +82,7 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa
int next = 1; int next = 1;
int bitsLeft = 8; int bitsLeft = 8;
while (count < bufSize && (bitsLeft > 0 || next < length)) while (bitsLeft > 0 || next < length)
{ {
if (bitsLeft < 5) if (bitsLeft < 5)
{ {

View File

@ -82,7 +82,7 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa
int next = 1; int next = 1;
int bitsLeft = 8; int bitsLeft = 8;
while (count < bufSize && (bitsLeft > 0 || next < length)) while (bitsLeft > 0 || next < length)
{ {
if (bitsLeft < 5) if (bitsLeft < 5)
{ {

View File

@ -38,7 +38,7 @@ const uint8_t physicalCount = 6; // voltage,current,power_factor,power,energy,fr
uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ? uint8_t percentCycle = 0; // better to define a global or read knx.paramByte each time... ?
uint32_t timePeriod = 0; // same here, uint32_t timePeriod = 0; // same here,
uint8_t resetPeriod = 0; //same here ... uint8_t resetPeriod = 0; //same here ...
uint8_t resetEnergy = 0; // and here... disabled/day/week/month //uint8_t resetEnergy = 0; // and here... disabled/day/week/month
bool progMode = true; bool progMode = true;
@ -54,11 +54,10 @@ struct Physical {
void loop(){ void loop(){
// unsigned long currentMillis = millis(); // unsigned long currentMillis = millis();
// Delta Change update as defined in ETS // Delta Change update as defined in ETS
int32_t deltaPercent = ( 100 * ( _value - _lastValue ) / _value ); float deltaPercent = ( 100 * ( _value - _lastValue ) / _value );
if ( percentCycle != 0 && abs(deltaPercent) >= percentCycle ) if ( percentCycle != 0 && abs(deltaPercent) >= percentCycle )
{ {
_trigger = true; _trigger = true;
_lastValue = _value;
} }
// Refresh groupAddress value as defined in ETS since last update // Refresh groupAddress value as defined in ETS since last update
@ -70,6 +69,7 @@ struct Physical {
// UpdateGO but send to bus only if triggered by time or value change percentage // UpdateGO but send to bus only if triggered by time or value change percentage
if (_trigger){ if (_trigger){
knx.getGroupObject(_GOaddr).value(_value, _dpt); knx.getGroupObject(_GOaddr).value(_value, _dpt);
_lastValue = _value;
_lastMillis = millis(); _lastMillis = millis();
_trigger = false; _trigger = false;
}else{ }else{
@ -143,7 +143,6 @@ class Blinker
Blinker led = Blinker(ledPin); Blinker led = Blinker(ledPin);
void callBackProgMode(GroupObject& go){ void callBackProgMode(GroupObject& go){
progMode = (bool)go.value(); progMode = (bool)go.value();
} }
@ -172,7 +171,7 @@ void resetCallback(GroupObject& go)
{ {
if (go.value()) if (go.value())
{ {
resetEnergy = true; pzem.resetEnergy();
goReset.value(false); goReset.value(false);
} }
} }
@ -181,7 +180,7 @@ void setup() {
pinPeripheral(PIN_SERIAL2_RX, PIO_SERCOM); pinPeripheral(PIN_SERIAL2_RX, PIO_SERCOM);
pinPeripheral(PIN_SERIAL2_TX, PIO_SERCOM); pinPeripheral(PIN_SERIAL2_TX, PIO_SERCOM);
SerialUSB.begin(9600); // SerialUSB.begin(9600);
Serial2.begin(9600); Serial2.begin(9600);
ArduinoPlatform::SerialDebug = &SerialUSB; ArduinoPlatform::SerialDebug = &SerialUSB;
@ -238,12 +237,17 @@ void loop() {
if (knx.configured() && !progMode) if (knx.configured() && !progMode)
{ {
refreshValueLoop(); refreshValueLoop();
resetEnergyLoop();
for (uint8_t i=0; i< physicalCount; i++) for (uint8_t i=0; i< physicalCount; i++)
{ {
Physical[i].loop(); Physical[i].loop();
} }
if (timeStatus() == timeSet && resetPeriod != 0)
{
resetEnergyLoop();
}
} }
else if (progMode) else if (progMode)
{ {
@ -282,12 +286,19 @@ void refreshValueLoop(){
default: default:
break; break;
} }
if(!isnan(isaValue)) if(!isnan(isaValue))
{ {
Physical[i].setValue(isaValue); Physical[i].setValue(isaValue);
} }
else
{
Physical[i].setValue(-1);
} }
} }
lastPzemUpdate = millis();
led.set(500, 1000);
}
} }
void resetEnergyLoop(){ void resetEnergyLoop(){
@ -339,14 +350,14 @@ void prodModeLoop(){ // run Only if progMode triggered ( at start or callback)
{ {
knx.progMode(true); knx.progMode(true);
timerProgPrevMillis = millis(); timerProgPrevMillis = millis();
led.set(500, 250); led.set(50, 100);
} }
else else
{ {
if (millis() - timerProgPrevMillis > timerProgMode) { if (millis() - timerProgPrevMillis > timerProgMode) {
knx.progMode(false); knx.progMode(false);
goProgMode.value(false); goProgMode.value(false);
progMode = 0; progMode = false;
} }
} }
} }

View File

@ -8,7 +8,7 @@
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[env:adafruit_feather_m0] [env:adafruit_feather_m0]
platform = atmelsam platform = atmelsam@6.0.1
board = adafruit_feather_m0 board = adafruit_feather_m0
framework = arduino framework = arduino

View File

@ -13,7 +13,7 @@
libdeps_dir = /tmp/libdeps libdeps_dir = /tmp/libdeps
[env:adafruit_feather_m0] [env:adafruit_feather_m0]
platform = atmelsam platform = atmelsam@6.0.1
board = adafruit_feather_m0 board = adafruit_feather_m0
framework = arduino framework = arduino
; We consider that the this projects is opened within its project directory ; We consider that the this projects is opened within its project directory
@ -27,7 +27,7 @@ board_build.usb_product="KNX RF - USB Interface"
lib_deps = lib_deps =
SPI SPI
Adafruit TinyUSB Library Adafruit TinyUSB Library@0.7.1
https://github.com/thelsing/FlashStorage.git https://github.com/thelsing/FlashStorage.git
knx knx

View File

@ -2,9 +2,17 @@
#include "knx/bits.h" #include "knx/bits.h"
#include <Arduino.h> #include <Arduino.h>
#ifndef KNX_NO_SPI
#include <SPI.h> #include <SPI.h>
#endif
#ifndef KNX_NO_PRINT
Stream* ArduinoPlatform::SerialDebug = &Serial; Stream* ArduinoPlatform::SerialDebug = &Serial;
#endif
ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr)
{
}
ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSerial) ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSerial)
{ {
@ -27,6 +35,7 @@ void ArduinoPlatform::fatalError()
void ArduinoPlatform::knxUart( HardwareSerial* serial ) void ArduinoPlatform::knxUart( HardwareSerial* serial )
{ {
if (_knxSerial)
closeUart(); closeUart();
_knxSerial = serial; _knxSerial = serial;
setupUart(); setupUart();
@ -94,6 +103,7 @@ size_t ArduinoPlatform::readBytesUart(uint8_t *buffer, size_t length)
return length; return length;
} }
#ifndef KNX_NO_SPI
void ArduinoPlatform::setupSpi() void ArduinoPlatform::setupSpi()
{ {
SPI.begin(); SPI.begin();
@ -111,7 +121,9 @@ int ArduinoPlatform::readWriteSpi(uint8_t *data, size_t len)
SPI.transfer(data, len); SPI.transfer(data, len);
return 0; return 0;
} }
#endif
#ifndef KNX_NO_PRINT
void printUint64(uint64_t value, int base = DEC) void printUint64(uint64_t value, int base = DEC)
{ {
char buf[8 * sizeof(uint64_t) + 1]; char buf[8 * sizeof(uint64_t) + 1];
@ -284,3 +296,4 @@ void println(void)
{ {
ArduinoPlatform::SerialDebug->println(); ArduinoPlatform::SerialDebug->println();
} }
#endif // KNX_NO_PRINT

View File

@ -2,11 +2,10 @@
#include "Arduino.h" #include "Arduino.h"
extern Stream& _serialDBG;
class ArduinoPlatform : public Platform class ArduinoPlatform : public Platform
{ {
public: public:
ArduinoPlatform();
ArduinoPlatform(HardwareSerial* knxSerial); ArduinoPlatform(HardwareSerial* knxSerial);
// basic stuff // basic stuff
@ -24,11 +23,14 @@ class ArduinoPlatform : public Platform
virtual size_t readBytesUart(uint8_t* buffer, size_t length); virtual size_t readBytesUart(uint8_t* buffer, size_t length);
//spi //spi
#ifndef KNX_NO_SPI
void setupSpi() override; void setupSpi() override;
void closeSpi() override; void closeSpi() override;
int readWriteSpi (uint8_t *data, size_t len) override; int readWriteSpi (uint8_t *data, size_t len) override;
#endif
#ifndef KNX_NO_PRINT
static Stream* SerialDebug; static Stream* SerialDebug;
#endif
protected: protected:
HardwareSerial* _knxSerial; HardwareSerial* _knxSerial;

View File

@ -134,6 +134,7 @@ void delayMicroseconds (unsigned int howLong)
ClockP_usleep(howLong); ClockP_usleep(howLong);
} }
#ifndef KNX_NO_PRINT
size_t write(uint8_t c) size_t write(uint8_t c)
{ {
#if defined(PRINT_UART) #if defined(PRINT_UART)
@ -402,6 +403,7 @@ void println(double num)
// default: print 10 digits // default: print 10 digits
println(num, 10); println(num, 10);
} }
#endif // KNX_NO_PRINT
uint32_t digitalRead(uint32_t dwPin) uint32_t digitalRead(uint32_t dwPin)
{ {

View File

@ -6,7 +6,10 @@
#include "knx/bits.h" #include "knx/bits.h"
Esp32Platform::Esp32Platform() : ArduinoPlatform(&Serial1) Esp32Platform::Esp32Platform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1)
#endif
{ {
} }
@ -34,6 +37,14 @@ void Esp32Platform::macAddress(uint8_t * addr)
esp_wifi_get_mac(WIFI_IF_STA, addr); esp_wifi_get_mac(WIFI_IF_STA, addr);
} }
uint32_t Esp32Platform::uniqueSerialNumber()
{
uint64_t chipid = ESP.getEfuseMac();
uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF;
uint32_t lowerId = (chipid & 0xFFFFFFFF);
return (upperId ^ lowerId);
}
void Esp32Platform::restart() void Esp32Platform::restart()
{ {
println("restart"); println("restart");
@ -81,6 +92,18 @@ int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
return len; return len;
} }
bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
{
IPAddress ucastaddr(htonl(addr));
println("sendBytesUniCast endPacket fail");
if(_udp.beginPacket(ucastaddr, port) == 1) {
_udp.write(buffer, len);
if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail");
}
else println("sendBytesUniCast beginPacket fail");
return true;
}
uint8_t * Esp32Platform::getEepromBuffer(uint16_t size) uint8_t * Esp32Platform::getEepromBuffer(uint16_t size)
{ {
EEPROM.begin(size); EEPROM.begin(size);
@ -89,6 +112,7 @@ uint8_t * Esp32Platform::getEepromBuffer(uint16_t size)
void Esp32Platform::commitToEeprom() void Esp32Platform::commitToEeprom()
{ {
EEPROM.getDataPtr(); // trigger dirty flag in EEPROM lib to make sure data will be written to flash
EEPROM.commit(); EEPROM.commit();
} }

View File

@ -16,6 +16,9 @@ public:
uint32_t currentDefaultGateway() override; uint32_t currentDefaultGateway() override;
void macAddress(uint8_t* addr) override; void macAddress(uint8_t* addr) override;
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff // basic stuff
void restart(); void restart();
@ -25,6 +28,9 @@ public:
bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override;
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override;
//unicast
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override;
//memory //memory
uint8_t* getEepromBuffer(uint16_t size); uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom(); void commitToEeprom();

View File

@ -7,7 +7,10 @@
#include "knx/bits.h" #include "knx/bits.h"
EspPlatform::EspPlatform() : ArduinoPlatform(&Serial) EspPlatform::EspPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial)
#endif
{ {
} }
@ -35,6 +38,11 @@ void EspPlatform::macAddress(uint8_t * addr)
wifi_get_macaddr(STATION_IF, addr); wifi_get_macaddr(STATION_IF, addr);
} }
uint32_t EspPlatform::uniqueSerialNumber()
{
return ESP.getChipId();
}
void EspPlatform::restart() void EspPlatform::restart()
{ {
println("restart"); println("restart");
@ -43,9 +51,9 @@ void EspPlatform::restart()
void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port)
{ {
_mulitcastAddr = htonl(addr); _multicastAddr = htonl(addr);
_mulitcastPort = port; _multicastPort = port;
IPAddress mcastaddr(_mulitcastAddr); IPAddress mcastaddr(_multicastAddr);
Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
WiFi.localIP().toString().c_str()); WiFi.localIP().toString().c_str());
@ -61,7 +69,7 @@ void EspPlatform::closeMultiCast()
bool EspPlatform::sendBytesMultiCast(uint8_t * buffer, uint16_t len) bool EspPlatform::sendBytesMultiCast(uint8_t * buffer, uint16_t len)
{ {
//printHex("<- ",buffer, len); //printHex("<- ",buffer, len);
_udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); _udp.beginPacketMulticast(_multicastAddr, _multicastPort, WiFi.localIP());
_udp.write(buffer, len); _udp.write(buffer, len);
_udp.endPacket(); _udp.endPacket();
return true; return true;
@ -84,6 +92,18 @@ int EspPlatform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
return len; return len;
} }
bool EspPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
{
IPAddress ucastaddr(htonl(addr));
println("sendBytesUniCast endPacket fail");
if(_udp.beginPacket(ucastaddr, port) == 1) {
_udp.write(buffer, len);
if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail");
}
else println("sendBytesUniCast beginPacket fail");
return true;
}
uint8_t * EspPlatform::getEepromBuffer(uint16_t size) uint8_t * EspPlatform::getEepromBuffer(uint16_t size)
{ {
EEPROM.begin(size); EEPROM.begin(size);

View File

@ -16,6 +16,9 @@ class EspPlatform : public ArduinoPlatform
uint32_t currentDefaultGateway() override; uint32_t currentDefaultGateway() override;
void macAddress(uint8_t* addr) override; void macAddress(uint8_t* addr) override;
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff // basic stuff
void restart(); void restart();
@ -25,13 +28,16 @@ class EspPlatform : public ArduinoPlatform
bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override;
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override;
//unicast
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override;
//memory //memory
uint8_t* getEepromBuffer(uint16_t size); uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom(); void commitToEeprom();
private: private:
WiFiUDP _udp; WiFiUDP _udp;
uint32_t _mulitcastAddr; uint32_t _multicastAddr;
uint16_t _mulitcastPort; uint16_t _multicastPort;
}; };
#endif #endif

View File

@ -2,9 +2,9 @@
#include "table_object.h" #include "table_object.h"
/** /**
* This class represents the group address table. It provides a mapping between tranport layer * This class represents the group address table. It provides a mapping between transport layer
* service access points (TSAP) and group addresses. The TSAP can be imagined as an index to the array * service access points (TSAP) and group addresses. The TSAP can be imagined as an index to the array
* of group adresses. * of group addresses.
* *
* See section 4.10 of @cite knx:3/5/1 for further details. * See section 4.10 of @cite knx:3/5/1 for further details.
* It implements realisation type 7 (see section 4.10.7 of @cite knx:3/5/1). * It implements realisation type 7 (see section 4.10.7 of @cite knx:3/5/1).
@ -13,9 +13,9 @@ class AddressTableObject : public TableObject
{ {
public: public:
/** /**
* The contructor. * The constructor.
* *
* @param memory This parameter is only passed to the custructor of TableObject an not used by this class. * @param memory This parameter is only passed to the constructor of TableObject and is not used by this class.
*/ */
AddressTableObject(Memory& memory); AddressTableObject(Memory& memory);
const uint8_t* restore(const uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override;
@ -35,7 +35,7 @@ class AddressTableObject : public TableObject
/** /**
* Get the TSAP mapped to a group address. * Get the TSAP mapped to a group address.
* *
* @param groupAddress the group address of whicht to get the TSAP for. * @param groupAddress the group address of which to get the TSAP for.
* *
* @return the TSAP if found or zero if no tsap was found. * @return the TSAP if found or zero if no tsap was found.
*/ */

View File

@ -66,7 +66,7 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori
case GroupValueWrite: case GroupValueWrite:
_bau.groupValueWriteIndication(asap, priority, hopType, secCtrl, data, len); _bau.groupValueWriteIndication(asap, priority, hopType, secCtrl, data, len);
default: default:
/* other apdutypes ar not valid here. If the appear do nothing */ /* other apdutypes are not valid here. If they appear do nothing */
break; break;
} }
} }
@ -908,7 +908,7 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap,
uint8_t* apdudata = apdu.data(); uint8_t* apdudata = apdu.data();
if (dataLength == 0) if (dataLength == 0)
{ {
// data size is six bit or less. So store int first byte // data size is six bit or less. So store in first byte
*apdudata &= ~0x3f; *apdudata &= ~0x3f;
*apdudata |= (*data & 0x3f); *apdudata |= (*data & 0x3f);
} }
@ -916,7 +916,7 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap,
{ {
memcpy(apdudata + 1, data, dataLength); memcpy(apdudata + 1, data, dataLength);
} }
// no need to check if there is a tsap. This is a response, so the read got trough // no need to check if there is a tsap. This is a response, so the read got through
uint16_t tsap = (uint16_t)_assocTable->translateAsap(asap); uint16_t tsap = (uint16_t)_assocTable->translateAsap(asap);
dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl);
dataGroupIndication(hopType, priority, tsap, apdu, secCtrl); dataGroupIndication(hopType, priority, tsap, apdu, secCtrl);
@ -1129,7 +1129,7 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
_bau.keyWriteAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1]); _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1]);
break; break;
default: default:
print("Indiviual-indication: unhandled APDU-Type: "); print("Individual-indication: unhandled APDU-Type: ");
println(apdu.type()); println(apdu.type());
} }
} }
@ -1240,7 +1240,7 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio
_bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], status); _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], status);
break; break;
default: default:
print("Indiviual-confirm: unhandled APDU-Type: "); print("Individual-confirm: unhandled APDU-Type: ");
println(apdu.type()); println(apdu.type());
} }
} }

View File

@ -11,7 +11,7 @@ class TransportLayer;
* This is an implementation of the application layer as specified in @cite knx:3/5/1. * This is an implementation of the application layer as specified in @cite knx:3/5/1.
* It provides methods for the BusAccessUnit to do different things and translates this * It provides methods for the BusAccessUnit to do different things and translates this
* call to an APDU and calls the correct method of the TransportLayer. * call to an APDU and calls the correct method of the TransportLayer.
* It also takes calls from TransportLayer, decodes the submitted APDU and calls the coresponding * It also takes calls from TransportLayer, decodes the submitted APDU and calls the corresponding
* methods of the BusAccessUnit class. * methods of the BusAccessUnit class.
*/ */
class ApplicationLayer class ApplicationLayer
@ -31,12 +31,12 @@ class ApplicationLayer
void associationTableObject(AssociationTableObject& assocTable); void associationTableObject(AssociationTableObject& assocTable);
// from transport layer // from transport layer
// Note: without data secure feature, the application layer is just used with SecurtyControl.dataSecurity = None // Note: without data secure feature, the application layer is just used with SecurityControl.dataSecurity = None
// hooks that can be implemented by derived class (e.g. SecureApplicationLayer) // hooks that can be implemented by derived class (e.g. SecureApplicationLayer)
#pragma region Transport - Layer - Callbacks #pragma region Transport - Layer - Callbacks
/** /**
* Somebody send us an APDU via multicast communiation. See 3.2 of @cite knx:3/3/4. * Somebody send us an APDU via multicast communication. See 3.2 of @cite knx:3/3/4.
* See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. * See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest.
* This method is called by the TransportLayer. * This method is called by the TransportLayer.
* *
@ -51,7 +51,7 @@ class ApplicationLayer
*/ */
virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu);
/** /**
* Report the status of an APDU that we sent via multicast communiation back to us. See 3.2 of @cite knx:3/3/4. * Report the status of an APDU that we sent via multicast communication back to us. See 3.2 of @cite knx:3/3/4.
* See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. This method is called by * See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. This method is called by
* the TransportLayer. * the TransportLayer.
* *

View File

@ -29,6 +29,29 @@ ApplicationProgramObject::ApplicationProgramObject(Memory& memory)
TableObject::initializeProperties(sizeof(properties), properties); TableObject::initializeProperties(sizeof(properties), properties);
} }
uint8_t* ApplicationProgramObject::save(uint8_t* buffer)
{
uint8_t programVersion[5];
property(PID_PROG_VERSION)->read(programVersion);
buffer = pushByteArray(programVersion, 5, buffer);
return TableObject::save(buffer);
}
const uint8_t* ApplicationProgramObject::restore(const uint8_t* buffer)
{
uint8_t programVersion[5];
buffer = popByteArray(programVersion, 5, buffer);
property(PID_PROG_VERSION)->write(programVersion);
return TableObject::restore(buffer);
}
uint16_t ApplicationProgramObject::saveSize()
{
return TableObject::saveSize() + 5; // sizeof(programVersion)
}
uint8_t * ApplicationProgramObject::data(uint32_t addr) uint8_t * ApplicationProgramObject::data(uint32_t addr)
{ {
return TableObject::data() + addr; return TableObject::data() + addr;

View File

@ -7,6 +7,9 @@ class ApplicationProgramObject : public TableObject
{ {
public: public:
ApplicationProgramObject(Memory& memory); ApplicationProgramObject(Memory& memory);
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
uint8_t* data(uint32_t addr); uint8_t* data(uint32_t addr);
uint8_t getByte(uint32_t addr); uint8_t getByte(uint32_t addr);
uint16_t getWord(uint32_t addr); uint16_t getWord(uint32_t addr);

View File

@ -25,7 +25,7 @@ uint16_t AssociationTableObject::entryCount()
uint16_t AssociationTableObject::getTSAP(uint16_t idx) uint16_t AssociationTableObject::getTSAP(uint16_t idx)
{ {
if (idx < 0 || idx >= entryCount()) if (idx >= entryCount())
return 0; return 0;
return ntohs(_tableData[2 * idx + 1]); return ntohs(_tableData[2 * idx + 1]);
@ -33,7 +33,7 @@ uint16_t AssociationTableObject::getTSAP(uint16_t idx)
uint16_t AssociationTableObject::getASAP(uint16_t idx) uint16_t AssociationTableObject::getASAP(uint16_t idx)
{ {
if (idx < 0 || idx >= entryCount()) if (idx >= entryCount())
return 0; return 0;
return ntohs(_tableData[2 * idx + 2]); return ntohs(_tableData[2 * idx + 2]);

View File

@ -62,7 +62,7 @@ uint8_t BauSystemB::checkmasterResetValidity(EraseCode eraseCode, uint8_t channe
case EraseCode::ResetIA: case EraseCode::ResetIA:
{ {
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER) // TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetAP requested. Not implemented yet."); println("ResetIA requested. Not implemented yet.");
return successCode; return successCode;
} }
case EraseCode::ResetLinks: case EraseCode::ResetLinks:
@ -500,7 +500,7 @@ bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl)
void BauSystemB::connectConfirm(uint16_t tsap) void BauSystemB::connectConfirm(uint16_t tsap)
{ {
if (_restartState == Connecting && tsap >= 0) if (_restartState == Connecting)
{ {
/* restart connection is confirmed, go to the next state */ /* restart connection is confirmed, go to the next state */
_restartState = Connected; _restartState = Connected;
@ -532,7 +532,7 @@ void BauSystemB::nextRestartState()
} }
break; break;
case Restarted: case Restarted:
/* restart is finished, we send a discommect */ /* restart is finished, we send a disconnect */
if (millis() - _restartDelay > 30) if (millis() - _restartDelay > 30)
{ {
applicationLayer().disconnectRequest(SystemPriority); applicationLayer().disconnectRequest(SystemPriority);

View File

@ -114,9 +114,13 @@ void BauSystemBDevice::updateGroupObject(GroupObject & go, uint8_t * data, uint8
memcpy(goData, data, length); memcpy(goData, data, length);
go.commFlag(Updated); go.commFlag(Updated);
#ifdef SMALL_GROUPOBJECT
GroupObject::processClassCallback(go);
#else
GroupObjectUpdatedHandler handler = go.callback(); GroupObjectUpdatedHandler handler = go.callback();
if (handler) if (handler)
handler(go); handler(go);
#endif
} }
bool BauSystemBDevice::configured() bool BauSystemBDevice::configured()

View File

@ -8,6 +8,7 @@ const uint8_t* popByte(uint8_t& b, const uint8_t* data)
return data; return data;
} }
#ifndef KNX_NO_PRINT
void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline) void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline)
{ {
print(suffix); print(suffix);
@ -21,6 +22,7 @@ void printHex(const char* suffix, const uint8_t *data, size_t length, bool newli
println(); println();
} }
} }
#endif
const uint8_t* popWord(uint16_t& w, const uint8_t* data) const uint8_t* popWord(uint16_t& w, const uint8_t* data)
{ {

View File

@ -63,6 +63,7 @@ typedef void (*voidFuncPtr)(void);
void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode); void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode);
#endif #endif
#ifndef KNX_NO_PRINT
void print(const char[]); void print(const char[]);
void print(char); void print(char);
void print(unsigned char, int = DEC); void print(unsigned char, int = DEC);
@ -87,6 +88,11 @@ void println(double);
void println(void); void println(void);
void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline = true); void printHex(const char* suffix, const uint8_t *data, size_t length, bool newline = true);
#else
#define print(...) do {} while(0)
#define println(...) do {} while(0)
#define printHex(...) do {} while(0)
#endif
const uint8_t* popByte(uint8_t& b, const uint8_t* data); const uint8_t* popByte(uint8_t& b, const uint8_t* data);
const uint8_t* popWord(uint16_t& w, const uint8_t* data); const uint8_t* popWord(uint16_t& w, const uint8_t* data);

View File

@ -60,6 +60,22 @@
// Define via a compiler -D flag if required // Define via a compiler -D flag if required
// #define USE_DATASECURE // #define USE_DATASECURE
// option to have GroupObjects (KO in German) use 8 bytes mangement information RAM instead of 19 bytes
// see knx-demo-small-go for example
// this option might be also set via compiler flag -DSMALL_GROUPOBJECT if required
//#define SMALL_GROUPOBJECT
// Some defines to reduce footprint
// Do not perform conversion from KNXValue(const char*) to other types, it mainly avoids the expensive strtod
//#define KNX_NO_STRTOx_CONVERSION
// Do not print messages
//#define KNX_NO_PRINT
// Do not use SPI (Arduino variants)
//#define KNX_NO_SPI
// Do not use the default UART (Arduino variants), it must be defined by ArduinoPlatform::knxUart
// (combined with other flags (HWSERIAL_NONE for stm32) - avoid allocation of RX/TX buffers for all serial lines)
//#define KNX_NO_DEFAULT_UART
#endif #endif
#if !defined(MASK_VERSION) #if !defined(MASK_VERSION)

View File

@ -39,5 +39,5 @@ public:
uint8_t defaultHopCount(); uint8_t defaultHopCount();
private: private:
uint8_t _prgMode = 0; uint8_t _prgMode = 0;
uint16_t _ownAddress = 0; uint16_t _ownAddress = 65535; // 15.15.255;
}; };

View File

@ -357,6 +357,7 @@
#define DPT_FlaggedScaling Dpt(239, 1) #define DPT_FlaggedScaling Dpt(239, 1)
#define DPT_CombinedPosition Dpt(240, 800) #define DPT_CombinedPosition Dpt(240, 800)
#define DPT_StatusSAB Dpt(241, 800) #define DPT_StatusSAB Dpt(241, 800)
#define DPT_Colour_RGBW Dpt(251, 600)
class Dpt class Dpt
{ {

View File

@ -22,12 +22,6 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1) if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1)
return busValueToBinaryControl(payload, payload_length, datatype, value); return busValueToBinaryControl(payload, payload_length, datatype, value);
// DPT 3.* - Step Control // DPT 3.* - Step Control
if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1)
return busValueToStepControl(payload, payload_length, datatype, value);
// DPT 4.* - Character// DPT 2.* - Binary Control
if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1)
return busValueToBinaryControl(payload, payload_length, datatype, value);
// DPT 3.* - Step Control
if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1) if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1)
return busValueToStepControl(payload, payload_length, datatype, value); return busValueToStepControl(payload, payload_length, datatype, value);
// DPT 4.* - Character // DPT 4.* - Character
@ -42,8 +36,8 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
// DPT 6.020 - Status with Mode // DPT 6.020 - Status with Mode
if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5) if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5)
return busValueToStatusAndMode(payload, payload_length, datatype, value); return busValueToStatusAndMode(payload, payload_length, datatype, value);
// DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer
if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index) if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || (datatype.subGroup == 600)) && !datatype.index)
return busValueToUnsigned16(payload, payload_length, datatype, value); return busValueToUnsigned16(payload, payload_length, datatype, value);
// DPT 7.002-DPT 7.007 - Time Period // DPT 7.002-DPT 7.007 - Time Period
if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
@ -55,108 +49,7 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
return busValueToTimeDelta(payload, payload_length, datatype, value); return busValueToTimeDelta(payload, payload_length, datatype, value);
// DPT 9.* - 16 Bit Float // DPT 9.* - 16 Bit Float
if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index) if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index)
return busValueToFloat16(payload, payload_length, datatype, value);
// DPT 10.* - Time and Weekday
if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToTime(payload, payload_length, datatype, value);
// DPT 11.* - Date
if (datatype.mainGroup == 11 && datatype.subGroup == 1 && !datatype.index)
return busValueToDate(payload, payload_length, datatype, value);
// DPT 12.* - Unsigned 32 Bit Integer
if (datatype.mainGroup == 12 && datatype.subGroup == 1 && !datatype.index)
return busValueToUnsigned32(payload, payload_length, datatype, value);
// DPT 13.001/13.002/13.010-13.015 - Signed 32 Bit Integer
if (datatype.mainGroup == 13 && (datatype.subGroup == 1 || datatype.subGroup == 2 || (datatype.subGroup >= 10 && datatype.subGroup <= 15)) && !datatype.index)
return busValueToSigned32(payload, payload_length, datatype, value);
// DPT 13.100 - Long Time Period
if (datatype.mainGroup == 13 && datatype.subGroup == 100 && !datatype.index)
return busValueToLongTimePeriod(payload, payload_length, datatype, value);
// DPT 14.* - 32 Bit Float
if (datatype.mainGroup == 14 && datatype.subGroup <= 79 && !datatype.index)
return busValueToFloat32(payload, payload_length, datatype, value);
// DPT 15.* - Access Data
if (datatype.mainGroup == 15 && !datatype.subGroup && datatype.index <= 5)
return busValueToAccess(payload, payload_length, datatype, value);
// DPT 16.* - String
if (datatype.mainGroup == 16 && datatype.subGroup <= 1 && !datatype.index)
return busValueToString(payload, payload_length, datatype, value);
// DPT 17.* - Scene Number
if (datatype.mainGroup == 17 && datatype.subGroup == 1 && !datatype.index)
return busValueToScene(payload, payload_length, datatype, value);
// DPT 18.* - Scene Control
if (datatype.mainGroup == 18 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToSceneControl(payload, payload_length, datatype, value);
// DPT 19.* - Date and Time
if (datatype.mainGroup == 19 && datatype.subGroup == 1 && (datatype.index <= 3 || datatype.index == 9 || datatype.index == 10))
return busValueToDateTime(payload, payload_length, datatype, value);
// DPT 26.* - Scene Info
if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToSceneInfo(payload, payload_length, datatype, value);
// DPT 28.* - Unicode String
if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index)
return busValueToUnicode(payload, payload_length, datatype, value);
// DPT 29.* - Signed 64 Bit Integer
if (datatype.mainGroup == 29 && datatype.subGroup >= 10 && datatype.subGroup <= 12 && !datatype.index)
return busValueToSigned64(payload, payload_length, datatype, value);
// DPT 219.* - Alarm Info
if (datatype.mainGroup == 219 && datatype.subGroup == 1 && datatype.index <= 10)
return busValueToAlarmInfo(payload, payload_length, datatype, value);
// DPT 221.* - Serial Number
if (datatype.mainGroup == 221 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToSerialNumber(payload, payload_length, datatype, value);
// DPT 217.* - Version
if (datatype.mainGroup == 217 && datatype.subGroup == 1 && datatype.index <= 2)
return busValueToVersion(payload, payload_length, datatype, value);
// DPT 225.001/225.002 - Scaling Speed and Scaling Step Time
if (datatype.mainGroup == 225 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && datatype.index <= 1)
return busValueToScaling(payload, payload_length, datatype, value);
// DPT 225.003 - Next Tariff
if (datatype.mainGroup == 225 && datatype.subGroup == 3 && datatype.index <= 1)
return busValueToTariff(payload, payload_length, datatype, value);
// DPT 231.* - Locale
if (datatype.mainGroup == 231 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToLocale(payload, payload_length, datatype, value);
// DPT 232.600 - RGB
if (datatype.mainGroup == 232 && datatype.subGroup == 600 && !datatype.index)
return busValueToRGB(payload, payload_length, datatype, value);
// DPT 234.* - Language and Region
if (datatype.mainGroup == 234 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
return busValueToLocale(payload, payload_length, datatype, value);
// DPT 235.* - Active Energy
if (datatype.mainGroup == 235 && datatype.subGroup == 1 && datatype.index <= 3)
return busValueToActiveEnergy(payload, payload_length, datatype, value);
// DPT 238.* - Scene Config
if (datatype.mainGroup == 238 && datatype.subGroup == 1 && datatype.index <= 2)
return busValueToSceneConfig(payload, payload_length, datatype, value);
// DPT 239.* - Flagged Scaling
if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToFlaggedScaling(payload, payload_length, datatype, value);
if (datatype.mainGroup == 4 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
return busValueToCharacter(payload, payload_length, datatype, value);
// DPT 5.* - Unsigned 8 Bit Integer
if (datatype.mainGroup == 5 && ((datatype.subGroup >= 1 && datatype.subGroup <= 6 && datatype.subGroup != 2) || datatype.subGroup == 10) && !datatype.index)
return busValueToUnsigned8(payload, payload_length, datatype, value);
// DPT 6.001/6.010 - Signed 8 Bit Integer
if (datatype.mainGroup == 6 && (datatype.subGroup == 1 || datatype.subGroup == 10) && !datatype.index)
return busValueToSigned8(payload, payload_length, datatype, value);
// DPT 6.020 - Status with Mode
if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5)
return busValueToStatusAndMode(payload, payload_length, datatype, value);
// DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer
if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index)
return busValueToUnsigned16(payload, payload_length, datatype, value);
// DPT 7.002-DPT 7.007 - Time Period
if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
return busValueToTimePeriod(payload, payload_length, datatype, value);
// DPT 8.001/8.010/8.011 - Signed 16 Bit Integer
if (datatype.mainGroup == 8 && (datatype.subGroup == 1 || datatype.subGroup == 10 || datatype.subGroup == 11) && !datatype.index)
return busValueToSigned16(payload, payload_length, datatype, value);
// DPT 8.002-DPT 8.007 - Time Delta
if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
return busValueToTimeDelta(payload, payload_length, datatype, value);
// DPT 9.* - 16 Bit Float
if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index)
return busValueToFloat16(payload, payload_length, datatype, value); return busValueToFloat16(payload, payload_length, datatype, value);
// DPT 10.* - Time and Weekday // DPT 10.* - Time and Weekday
if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1) if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1)
@ -233,6 +126,9 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
// DPT 239.* - Flagged Scaling // DPT 239.* - Flagged Scaling
if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1) if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToFlaggedScaling(payload, payload_length, datatype, value); return busValueToFlaggedScaling(payload, payload_length, datatype, value);
// DPT 251.600 - RGBW
if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1)
return busValueToRGBW(payload, payload_length, datatype, value);
} }
return false; return false;
} }
@ -260,8 +156,8 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len
// DPT 6.020 - Status with Mode // DPT 6.020 - Status with Mode
if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5) if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5)
return valueToBusValueStatusAndMode(value, payload, payload_length, datatype); return valueToBusValueStatusAndMode(value, payload, payload_length, datatype);
// DPT 7.001/7.010/7.011/7.012/7.013 - Unsigned 16 Bit Integer // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer
if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13)) && !datatype.index) if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || datatype.subGroup == 600) && !datatype.index)
return valueToBusValueUnsigned16(value, payload, payload_length, datatype); return valueToBusValueUnsigned16(value, payload, payload_length, datatype);
// DPT 7.002-DPT 7.007 - Time Period // DPT 7.002-DPT 7.007 - Time Period
if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
@ -273,7 +169,7 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len
if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index) if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
return valueToBusValueTimeDelta(value, payload, payload_length, datatype); return valueToBusValueTimeDelta(value, payload, payload_length, datatype);
// DPT 9.* - 16 Bit Float // DPT 9.* - 16 Bit Float
if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 && datatype.subGroup != 9) || (datatype.subGroup >= 20 && datatype.subGroup <= 28)) && !datatype.index) if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 ) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index)
return valueToBusValueFloat16(value, payload, payload_length, datatype); return valueToBusValueFloat16(value, payload, payload_length, datatype);
// DPT 10.* - Time and Weekday // DPT 10.* - Time and Weekday
if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1) if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1)
@ -350,6 +246,9 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len
// DPT 239.* - Flagged Scaling // DPT 239.* - Flagged Scaling
if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1) if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1)
return valueToBusValueFlaggedScaling(value, payload, payload_length, datatype); return valueToBusValueFlaggedScaling(value, payload, payload_length, datatype);
// DPT 251.600 - RGBW
if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1)
return valueToBusValueRGBW(value, payload, payload_length, datatype);
return false; return false;
} }
@ -904,12 +803,27 @@ int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& data
{ {
ASSERT_PAYLOAD(3); ASSERT_PAYLOAD(3);
uint32_t rgb = unsigned16FromPayload(payload, 0) * 256 + unsigned8FromPayload(payload, 2); uint32_t rgb = unsigned16FromPayload(payload, 0) * 256 + unsigned8FromPayload(payload, 2);
if (rgb > 16777215)
return false;
value = rgb; value = rgb;
return true; return true;
} }
int busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
ASSERT_PAYLOAD(6);
switch (datatype.index) {
case 0: // The RGBW value
{
uint32_t rgbw = unsigned32FromPayload(payload, 0);
value = rgbw;
}
return true;
case 1: // The mask bits only
value = unsigned8FromPayload(payload,5);
return true;
}
return false;
}
int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value) int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{ {
ASSERT_PAYLOAD(2); ASSERT_PAYLOAD(2);
@ -993,7 +907,7 @@ int valueToBusValueStepControl(const KNXValue& value, uint8_t* payload, size_t p
int valueToBusValueCharacter(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) int valueToBusValueCharacter(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{ {
if ((uint64_t)value < INT64_C(0) || (uint64_t)value > INT64_C(255) || (datatype.subGroup == 1 && (uint64_t)value > INT64_C(127))) if ((uint64_t)value > INT64_C(255) || (datatype.subGroup == 1 && (uint64_t)value > INT64_C(127)))
return false; return false;
unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF); unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
return true; return true;
@ -1260,7 +1174,7 @@ int valueToBusValueAccess(const KNXValue& value, uint8_t* payload, size_t payloa
break; break;
case 5: case 5:
{ {
if ((uint64_t)value < INT64_C(0) || (uint64_t)value > INT64_C(15)) if ((uint64_t)value > INT64_C(15))
return false; return false;
bcdToPayload(payload, payload_length, 7, (uint64_t)value); bcdToPayload(payload, payload_length, 7, (uint64_t)value);
break; break;
@ -1551,7 +1465,7 @@ int valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t paylo
{ {
uint32_t duration = value; uint32_t duration = value;
if (duration < INT64_C(0) || duration > INT64_C(65535)) if (duration > INT64_C(65535))
return false; return false;
ENSURE_PAYLOAD(3); ENSURE_PAYLOAD(3);
@ -1578,7 +1492,7 @@ int valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payloa
{ {
uint32_t duration = value; uint32_t duration = value;
if (duration < INT64_C(0) || duration > INT64_C(65535)) if (duration > INT64_C(65535))
return false; return false;
ENSURE_PAYLOAD(3); ENSURE_PAYLOAD(3);
@ -1626,6 +1540,24 @@ int valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_l
return true; return true;
} }
int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
switch(datatype.index)
{
case 0: // RGBW
{
uint32_t rgbw = (uint32_t)value;
unsigned32ToPayload(payload, payload_length, 0, rgbw, 0xffffffff); // RGBW
}
break;
case 1: // Mask bits
unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f);
break;
}
return true;
}
int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{ {
switch (datatype.index) switch (datatype.index)
@ -1654,7 +1586,6 @@ int valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t
{ {
case 0: case 0:
{ {
if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647)) if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647))
return false; return false;
ENSURE_PAYLOAD(6); ENSURE_PAYLOAD(6);

View File

@ -78,6 +78,7 @@ int busValueToScaling(const uint8_t* payload, size_t payload_length, const Dpt&
int busValueToTariff(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToTariff(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
int busValueToLocale(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToLocale(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
int busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
int busValueToActiveEnergy(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value); int busValueToActiveEnergy(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
@ -116,6 +117,7 @@ int valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t paylo
int valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
int valueToBusValueLocale(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueLocale(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
int valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
int valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype); int valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);

View File

@ -4,23 +4,30 @@
#include "datapoint_types.h" #include "datapoint_types.h"
#include "group_object_table_object.h" #include "group_object_table_object.h"
#ifdef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler GroupObject::_updateHandlerStatic = 0;
#endif
GroupObjectTableObject* GroupObject::_table = 0;
GroupObject::GroupObject() GroupObject::GroupObject()
{ {
_data = 0; _data = 0;
_commFlag = Ok; _commFlag = Ok;
_table = 0;
_dataLength = 0; _dataLength = 0;
#ifndef SMALL_GROUPOBJECT
_updateHandler = 0; _updateHandler = 0;
#endif
} }
GroupObject::GroupObject(const GroupObject& other) GroupObject::GroupObject(const GroupObject& other)
{ {
_data = new uint8_t[other._dataLength]; _data = new uint8_t[other._dataLength];
_commFlag = other._commFlag; _commFlag = other._commFlag;
_table = other._table;
_dataLength = other._dataLength; _dataLength = other._dataLength;
_asap = other._asap; _asap = other._asap;
#ifndef SMALL_GROUPOBJECT
_updateHandler = other._updateHandler; _updateHandler = other._updateHandler;
#endif
memcpy(_data, other._data, _dataLength); memcpy(_data, other._data, _dataLength);
} }
@ -175,6 +182,24 @@ size_t GroupObject::sizeInTelegram()
return asapValueSize(code); return asapValueSize(code);
} }
#ifdef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler GroupObject::classCallback()
{
return _updateHandlerStatic;
}
void GroupObject::classCallback(GroupObjectUpdatedHandler handler)
{
_updateHandlerStatic = handler;
}
void GroupObject::processClassCallback(GroupObject& ko)
{
if (_updateHandlerStatic != 0)
_updateHandlerStatic(ko);
}
#else
void GroupObject::callback(GroupObjectUpdatedHandler handler) void GroupObject::callback(GroupObjectUpdatedHandler handler)
{ {
_updateHandler = handler; _updateHandler = handler;
@ -185,6 +210,7 @@ GroupObjectUpdatedHandler GroupObject::callback()
{ {
return _updateHandler; return _updateHandler;
} }
#endif
void GroupObject::value(const KNXValue& value, const Dpt& type) void GroupObject::value(const KNXValue& value, const Dpt& type)
{ {
@ -205,7 +231,7 @@ bool GroupObject::tryValue(KNXValue& value, const Dpt& type)
return KNX_Decode_Value(_data, _dataLength, type, value); return KNX_Decode_Value(_data, _dataLength, type, value);
} }
#ifndef SMALL_GROUPOBJECT
void GroupObject::dataPointType(Dpt value) void GroupObject::dataPointType(Dpt value)
{ {
_datapointType = value; _datapointType = value;
@ -240,7 +266,7 @@ void GroupObject::valueNoSend(const KNXValue& value)
{ {
valueNoSend(value, _datapointType); valueNoSend(value, _datapointType);
} }
#endif
void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type)
{ {

View File

@ -133,6 +133,8 @@ class GroupObject
* (in german "KO-Nr") * (in german "KO-Nr")
*/ */
uint16_t asap(); uint16_t asap();
#ifndef SMALL_GROUPOBJECT
/** /**
* register a callback for this group object. The registered callback will be called if the group object was changed from the bus. * register a callback for this group object. The registered callback will be called if the group object was changed from the bus.
*/ */
@ -141,16 +143,12 @@ class GroupObject
* returns the registered callback * returns the registered callback
*/ */
GroupObjectUpdatedHandler callback(); GroupObjectUpdatedHandler callback();
#endif
/** /**
* return the current value of the group object. * return the current value of the group object.
* @param type the datapoint type used for the conversion. If this doesn't fit to the group object the returned value is invalid. * @param type the datapoint type used for the conversion. If this doesn't fit to the group object the returned value is invalid.
*/ */
KNXValue value(const Dpt& type); KNXValue value(const Dpt& type);
/**
* return the current value of the group object. The datapoint type must be set with dataPointType(). Otherwise the returned
* value is invalid.
*/
KNXValue value();
/** /**
* set the current value of the group object and changes the state of the group object to ::WriteRequest. * set the current value of the group object and changes the state of the group object to ::WriteRequest.
* @param value the value the group object is set to * @param value the value the group object is set to
@ -159,13 +157,6 @@ class GroupObject
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
*/ */
void value(const KNXValue& value, const Dpt& type); void value(const KNXValue& value, const Dpt& type);
/**
* set the current value of the group object and changes the state of the group object to ::WriteRequest.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*/
void value(const KNXValue& value);
/** /**
* set the current value of the group object. * set the current value of the group object.
* @param value the value the group object is set to * @param value the value the group object is set to
@ -174,13 +165,6 @@ class GroupObject
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
*/ */
void valueNoSend(const KNXValue& value, const Dpt& type); void valueNoSend(const KNXValue& value, const Dpt& type);
/**
* set the current value of the group object.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*/
void valueNoSend(const KNXValue& value);
/** /**
* set the current value of the group object. * set the current value of the group object.
* @param value the value the group object is set to * @param value the value the group object is set to
@ -191,6 +175,27 @@ class GroupObject
* @returns true if the value of the group object was changed successfully. * @returns true if the value of the group object was changed successfully.
*/ */
bool tryValue(KNXValue& value, const Dpt& type); bool tryValue(KNXValue& value, const Dpt& type);
#ifndef SMALL_GROUPOBJECT
/**
* return the current value of the group object. The datapoint type must be set with dataPointType(). Otherwise the returned
* value is invalid.
*/
KNXValue value();
/**
* set the current value of the group object and changes the state of the group object to ::WriteRequest.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*/
void value(const KNXValue& value);
/**
* set the current value of the group object.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*/
void valueNoSend(const KNXValue& value);
/** /**
* set the current value of the group object. * set the current value of the group object.
* @param value the value the group object is set to * @param value the value the group object is set to
@ -209,15 +214,32 @@ class GroupObject
* sets the datapoint type of the group object. * sets the datapoint type of the group object.
*/ */
void dataPointType(Dpt value); void dataPointType(Dpt value);
#else
/**
* Alternative callback processing: register one global callback for all group object.
* The registered callback will be called if any group object was changed from the bus.
* The callback method has to dispatch to the correct handler for this group object.
*/
static GroupObjectUpdatedHandler classCallback();
static void classCallback(GroupObjectUpdatedHandler handler);
static void processClassCallback(GroupObject& ko);
#endif
private: private:
// class members
static GroupObjectTableObject* _table;
#ifdef SMALL_GROUPOBJECT
static GroupObjectUpdatedHandler _updateHandlerStatic;
#endif
size_t asapValueSize(uint8_t code); size_t asapValueSize(uint8_t code);
GroupObjectUpdatedHandler _updateHandler;
size_t goSize(); size_t goSize();
uint16_t _asap = 0; uint16_t _asap = 0;
ComFlag _commFlag = Ok; ComFlag _commFlag = Ok;
uint8_t* _data = 0; uint8_t* _data = 0;
uint8_t _dataLength = 0; uint8_t _dataLength = 0;
GroupObjectTableObject* _table = 0; #ifndef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler _updateHandler;
Dpt _datapointType; Dpt _datapointType;
#endif
}; };

View File

@ -46,7 +46,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf
return 1; return 1;
} }
pushInt(io->_platform.currentIpAddress(), data); pushInt(htonl(io->_platform.currentIpAddress()), data);
return 1; return 1;
}), }),
new CallbackProperty<IpParameterObject>(this, PID_CURRENT_SUBNET_MASK, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, new CallbackProperty<IpParameterObject>(this, PID_CURRENT_SUBNET_MASK, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
@ -59,7 +59,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf
return 1; return 1;
} }
pushInt(io->_platform.currentSubnetMask(), data); pushInt(htonl(io->_platform.currentSubnetMask()), data);
return 1; return 1;
}), }),
new CallbackProperty<IpParameterObject>(this, PID_CURRENT_DEFAULT_GATEWAY, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, new CallbackProperty<IpParameterObject>(this, PID_CURRENT_DEFAULT_GATEWAY, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
@ -72,7 +72,7 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf
return 1; return 1;
} }
pushInt(io->_platform.currentDefaultGateway(), data); pushInt(htonl(io->_platform.currentDefaultGateway()), data);
return 1; return 1;
}), }),
new DataProperty(PID_IP_ADDRESS, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3), new DataProperty(PID_IP_ADDRESS, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3),

View File

@ -35,7 +35,7 @@ uint16_t KnxIpDeviceInformationDIB::individualAddress() const
} }
void KnxIpDeviceInformationDIB::indiviudalAddress(uint16_t value) void KnxIpDeviceInformationDIB::individualAddress(uint16_t value)
{ {
pushWord(value, _data + 4); pushWord(value, _data + 4);
} }
@ -65,7 +65,7 @@ void KnxIpDeviceInformationDIB::serialNumber(const uint8_t* value)
} }
uint32_t KnxIpDeviceInformationDIB::routingMulicastAddress() const uint32_t KnxIpDeviceInformationDIB::routingMulticastAddress() const
{ {
return getInt(_data + 14); return getInt(_data + 14);
} }

View File

@ -16,12 +16,12 @@ class KnxIpDeviceInformationDIB : public KnxIpDIB
uint8_t status() const; uint8_t status() const;
void status(uint8_t value); void status(uint8_t value);
uint16_t individualAddress() const; uint16_t individualAddress() const;
void indiviudalAddress(uint16_t value); void individualAddress(uint16_t value);
uint16_t projectInstallationIdentifier() const; uint16_t projectInstallationIdentifier() const;
void projectInstallationIdentifier(uint16_t value); void projectInstallationIdentifier(uint16_t value);
const uint8_t* serialNumber() const; const uint8_t* serialNumber() const;
void serialNumber(const uint8_t* value); void serialNumber(const uint8_t* value);
uint32_t routingMulicastAddress() const; uint32_t routingMulticastAddress() const;
void routingMulticastAddress(uint32_t value); void routingMulticastAddress(uint32_t value);
const uint8_t* macAddress() const; const uint8_t* macAddress() const;
void macAddress(const uint8_t* value); void macAddress(const uint8_t* value);

View File

@ -19,7 +19,7 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb
_deviceInfo.code(DEVICE_INFO); _deviceInfo.code(DEVICE_INFO);
_deviceInfo.medium(0x20); //KNX-IP FIXME get this value from somewhere else _deviceInfo.medium(0x20); //KNX-IP FIXME get this value from somewhere else
_deviceInfo.status(deviceObject.progMode()); _deviceInfo.status(deviceObject.progMode());
_deviceInfo.indiviudalAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS)); _deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID)); _deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER)); _deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS)); _deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));

View File

@ -187,7 +187,7 @@ KNXValue& KNXValue::operator=(const int16_t value)
KNXValue& KNXValue::operator=(const int32_t value) KNXValue& KNXValue::operator=(const int32_t value)
{ {
_value.boolValue = value; _value.intValue = value;
_type = IntType; _type = IntType;
return *this; return *this;
} }
@ -318,7 +318,7 @@ uint64_t KNXValue::ulongValue() const
switch (_type) switch (_type)
{ {
case ULongType: case ULongType:
return _value.uintValue; return _value.ulongValue;
case BoolType: case BoolType:
return _value.boolValue ? 1 : 0; return _value.boolValue ? 1 : 0;
case UCharType: case UCharType:
@ -343,7 +343,11 @@ uint64_t KNXValue::ulongValue() const
case DoubleType: case DoubleType:
return (uint64_t)_value.doubleValue; return (uint64_t)_value.doubleValue;
case StringType: case StringType:
#ifndef KNX_NO_STRTOx_CONVERSION
return (uint64_t)strtoul(_value.stringValue, NULL, 0); return (uint64_t)strtoul(_value.stringValue, NULL, 0);
#else
return 0;
#endif
} }
return 0; return 0;
} }
@ -444,7 +448,11 @@ int64_t KNXValue::longValue() const
case DoubleType: case DoubleType:
return (int64_t)_value.doubleValue; return (int64_t)_value.doubleValue;
case StringType: case StringType:
#ifndef KNX_NO_STRTOx_CONVERSION
return strtol(_value.stringValue, NULL, 0); return strtol(_value.stringValue, NULL, 0);
#else
return 0;
#endif
} }
return 0; return 0;
} }
@ -476,7 +484,11 @@ double KNXValue::doubleValue() const
case LongType: case LongType:
return _value.longValue; return _value.longValue;
case StringType: case StringType:
#ifndef KNX_NO_STRTOx_CONVERSION
return strtod(_value.stringValue, NULL); return strtod(_value.stringValue, NULL);
#else
return 0;
#endif
} }
return 0; return 0;
} }

View File

@ -74,6 +74,11 @@ uint32_t Platform::currentDefaultGateway()
void Platform::macAddress(uint8_t *data) void Platform::macAddress(uint8_t *data)
{} {}
uint32_t Platform::uniqueSerialNumber()
{
return 0x01020304;
}
void Platform::setupMultiCast(uint32_t addr, uint16_t port) void Platform::setupMultiCast(uint32_t addr, uint16_t port)
{} {}

View File

@ -20,6 +20,9 @@ class Platform
virtual uint32_t currentDefaultGateway(); virtual uint32_t currentDefaultGateway();
virtual void macAddress(uint8_t* data); virtual void macAddress(uint8_t* data);
// unique serial number
virtual uint32_t uniqueSerialNumber();
// basic stuff // basic stuff
virtual void restart() = 0; virtual void restart() = 0;
virtual void fatalError() = 0; virtual void fatalError() = 0;

View File

@ -31,7 +31,7 @@ RfMediumObject::RfMediumObject()
resultData[2] = 0xFF; // permanent bidirectional device resultData[2] = 0xFF; // permanent bidirectional device
resultLength = 3; resultLength = 3;
}), }),
/* This properties are used in NMP_LinkBudget_Measure to diagnose the Link Budget of the communication. /* These properties are used in NMP_LinkBudget_Measure to diagnose the Link Budget of the communication.
This in not implemented yet. This in not implemented yet.
new DataProperty(PID_RF_DIAG_SA_FILTER_TABLE, true, PDT_GENERIC_03, 8, ReadLv3 | WriteLv3), new DataProperty(PID_RF_DIAG_SA_FILTER_TABLE, true, PDT_GENERIC_03, 8, ReadLv3 | WriteLv3),
new DataProperty(PID_RF_DIAG_BUDGET_TABLE, false, PDT_GENERIC_03, 8, ReadLv3 | WriteLv0), new DataProperty(PID_RF_DIAG_BUDGET_TABLE, false, PDT_GENERIC_03, 8, ReadLv3 | WriteLv0),

View File

@ -84,7 +84,6 @@ void RouterObject::initialize(CouplerModel model, uint8_t objIndex, DptMedium me
Property* tableProperties[] = Property* tableProperties[] =
{ {
new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement new DataProperty( PID_COUPLER_SERVICES_CONTROL, true, PDT_GENERIC_01, 1, ReadLv3 | WriteLv0, (uint8_t) 0), // written by ETS TODO: implement
new DataProperty( PID_MCB_TABLE, false, PDT_GENERIC_08, 1, ReadLv3 | WriteLv0), // TODO: improve: move to TableObject once segment size handling is clear
new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // default: invalid filter table, do not use, written by ETS new DataProperty( PID_FILTER_TABLE_USE, true, PDT_BINARY_INFORMATION, 1, ReadLv3 | WriteLv0, (uint16_t) 0 ), // default: invalid filter table, do not use, written by ETS
new FunctionProperty<RouterObject>(this, PID_ROUTETABLE_CONTROL, new FunctionProperty<RouterObject>(this, PID_ROUTETABLE_CONTROL,
// Command Callback of PID_ROUTETABLE_CONTROL // Command Callback of PID_ROUTETABLE_CONTROL
@ -452,27 +451,9 @@ void RouterObject::beforeStateChange(LoadState& newState)
if (newState != LS_LOADED) if (newState != LS_LOADED)
return; return;
// calculate crc16-ccitt for PID_MCB_TABLE
updateMcb();
_filterTableGroupAddresses = (uint16_t*)data(); _filterTableGroupAddresses = (uint16_t*)data();
} }
void RouterObject::updateMcb()
{
uint8_t mcb[propertySize(PID_MCB_TABLE)];
static constexpr uint32_t segmentSize = kFilterTableSize;
uint16_t crc16 = crc16Ccitt(data(), segmentSize);
pushInt(segmentSize, &mcb[0]); // Segment size
pushByte(0x00, &mcb[4]); // CRC control byte -> 0: always valid -> according to coupler spec. it shall always be a valid CRC
pushByte(0xFF, &mcb[5]); // Read access 4 bits + Write access 4 bits (unknown: value taken from real coupler device)
pushWord(crc16, &mcb[6]); // CRC-16 CCITT of filter table
property(PID_MCB_TABLE)->write(mcb);
}
void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel) void RouterObject::masterReset(EraseCode eraseCode, uint8_t channel)
{ {
if (eraseCode == FactoryReset) if (eraseCode == FactoryReset)

View File

@ -52,8 +52,6 @@ private:
void commandClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet); void commandClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet);
bool statusClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet); bool statusClearSetGroupAddress(uint16_t startAddress, uint16_t endAddress, bool bitIsSet);
void updateMcb();
bool _rfSbcRoutingEnabled = false; bool _rfSbcRoutingEnabled = false;
bool _ipSbcRoutingEnabled = false; bool _ipSbcRoutingEnabled = false;
uint16_t* _filterTableGroupAddresses = 0; uint16_t* _filterTableGroupAddresses = 0;

View File

@ -31,6 +31,8 @@ uint8_t* TableObject::save(uint8_t* buffer)
{ {
buffer = pushByte(_state, buffer); buffer = pushByte(_state, buffer);
buffer = pushInt(_size, buffer);
if (_data) if (_data)
buffer = pushInt(_memory.toRelative(_data), buffer); buffer = pushInt(_memory.toRelative(_data), buffer);
else else
@ -46,6 +48,8 @@ const uint8_t* TableObject::restore(const uint8_t* buffer)
buffer = popByte(state, buffer); buffer = popByte(state, buffer);
_state = (LoadState)state; _state = (LoadState)state;
buffer = popInt(_size, buffer);
uint32_t relativeAddress = 0; uint32_t relativeAddress = 0;
buffer = popInt(relativeAddress, buffer); buffer = popInt(relativeAddress, buffer);
@ -80,6 +84,8 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte)
if (doFill) if (doFill)
memset(_data, fillByte, size); memset(_data, fillByte, size);
_size = size;
return true; return true;
} }
@ -229,7 +235,7 @@ void TableObject::errorCode(ErrorCode errorCode)
uint16_t TableObject::saveSize() uint16_t TableObject::saveSize()
{ {
return 5 + InterfaceObject::saveSize(); return 5 + InterfaceObject::saveSize() + sizeof(_size);
} }
void TableObject::initializeProperties(size_t propertiesSize, Property** properties) void TableObject::initializeProperties(size_t propertiesSize, Property** properties)
@ -267,6 +273,21 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert
pushInt(obj->tableReference(), data); pushInt(obj->tableReference(), data);
return 1; return 1;
}), }),
new CallbackProperty<TableObject>(this, PID_MCB_TABLE, false, PDT_GENERIC_08, 1, ReadLv3 | WriteLv0,
[](TableObject* obj, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t {
if (obj->_state != LS_LOADED)
return 0; // need to check return code for invalid
uint32_t segmentSize = obj->_size;
uint16_t crc16 = crc16Ccitt(obj->data(), segmentSize);
pushInt(segmentSize, data); // Segment size
pushByte(0x00, data + 4); // CRC control byte -> 0: always valid
pushByte(0xFF, data + 5); // Read access 4 bits + Write access 4 bits
pushWord(crc16, data + 6); // CRC-16 CCITT of data
return 1;
}),
new DataProperty(PID_ERROR_CODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)E_NO_FAULT) new DataProperty(PID_ERROR_CODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)E_NO_FAULT)
}; };
//TODO: missing //TODO: missing

View File

@ -68,4 +68,11 @@ protected:
LoadState _state = LS_UNLOADED; LoadState _state = LS_UNLOADED;
Memory& _memory; Memory& _memory;
uint8_t *_data = 0; uint8_t *_data = 0;
/**
* used to store size of data() in allocTable(), needed for calculation of crc in PID_MCB_TABLE.
* This value is also saved and restored.
* The size of the memory block cannot be used because it is changed during alignment to page size.
*/
uint32_t _size = 0;
}; };

View File

@ -11,6 +11,9 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
// Activate trace output
//#define DBG_TRACE
// NCN5120 // NCN5120
//#define NCN5120 //#define NCN5120
@ -75,25 +78,28 @@
#define U_STOP_MODE_IND 0x2B #define U_STOP_MODE_IND 0x2B
#define U_SYSTEM_STAT_IND 0x4B #define U_SYSTEM_STAT_IND 0x4B
//loop states //tx states
#define IDLE 0 enum {
#define RX_FIRST_BYTE 1 TX_IDLE,
#define RX_L_DATA 2 TX_FRAME,
#define RX_WAIT_DATA_CON 3 TX_WAIT_ECHO,
#define TX_FRAME 4 TX_WAIT_CONN
};
#define BYTE_TIMEOUT 10 //milli seconds //rx states
enum {
RX_WAIT_START,
RX_L_DATA,
RX_WAIT_EOP
};
#define EOP_TIMEOUT 2 //milli seconds; end of layer-2 packet gap
#define CONFIRM_TIMEOUT 500 //milli seconds #define CONFIRM_TIMEOUT 500 //milli seconds
#define RESET_TIMEOUT 100 //milli seconds #define RESET_TIMEOUT 100 //milli seconds
void TpUartDataLinkLayer::loop() void TpUartDataLinkLayer::loop()
{ {
_receiveBuffer[0] = 0x29;
_receiveBuffer[1] = 0;
uint8_t* buffer = _receiveBuffer + 2;
uint8_t rxByte;
if (!_enabled) if (!_enabled)
{ {
if (millis() - _lastResetChipTime > 1000) if (millis() - _lastResetChipTime > 1000)
@ -107,33 +113,27 @@ void TpUartDataLinkLayer::loop()
if (!_enabled) if (!_enabled)
return; return;
switch (_loopState) // Signals to communicate from rx part with the tx part
bool isEchoComplete = false; // Flag that a complete echo is received
uint8_t dataConnMsg = 0; // The DATA_CONN message just seen or 0
do {
_receiveBuffer[0] = 0x29;
_receiveBuffer[1] = 0;
uint8_t* buffer = _receiveBuffer + 2;
uint8_t rxByte;
bool isEOP = (millis() - _lastByteRxTime > EOP_TIMEOUT); // Flag that an EOP gap is seen
switch (_rxState)
{ {
case IDLE: case RX_WAIT_START:
if (_platform.uartAvailable()) if (_platform.uartAvailable())
{ {
_loopState = RX_FIRST_BYTE;
}
else
{
if (!_waitConfirm && !isTxQueueEmpty())
{
loadNextTxFrame();
_loopState = TX_FRAME;
}
}
break;
case TX_FRAME:
if (sendSingleFrameByte() == false)
{
_waitConfirm = true;
_waitConfirmStartTime = millis();
_loopState = IDLE;
}
break;
case RX_FIRST_BYTE:
rxByte = _platform.readUart(); rxByte = _platform.readUart();
#ifdef DBG_TRACE
print(rxByte, HEX);
#endif
_lastByteRxTime = millis(); _lastByteRxTime = millis();
// Check for layer-2 packets
_RxByteCnt = 0; _RxByteCnt = 0;
_xorSum = 0; _xorSum = 0;
if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND) if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND)
@ -142,7 +142,10 @@ void TpUartDataLinkLayer::loop()
_xorSum ^= rxByte; _xorSum ^= rxByte;
_RxByteCnt++; //convert to L_DATA_EXTENDED _RxByteCnt++; //convert to L_DATA_EXTENDED
_convert = true; _convert = true;
_loopState = RX_L_DATA; _rxState = RX_L_DATA;
#ifdef DBG_TRACE
println("RLS");
#endif
break; break;
} }
else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND) else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND)
@ -150,12 +153,17 @@ void TpUartDataLinkLayer::loop()
buffer[_RxByteCnt++] = rxByte; buffer[_RxByteCnt++] = rxByte;
_xorSum ^= rxByte; _xorSum ^= rxByte;
_convert = false; _convert = false;
_loopState = RX_L_DATA; _rxState = RX_L_DATA;
#ifdef DBG_TRACE
println("RLX");
#endif
break; break;
} }
// Handle all single byte packets here
else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON)
{ {
println("got unexpected L_DATA_CON"); dataConnMsg = rxByte;
} }
else if (rxByte == L_POLL_DATA_IND) else if (rxByte == L_POLL_DATA_IND)
{ {
@ -173,8 +181,12 @@ void TpUartDataLinkLayer::loop()
} }
else if ((rxByte & U_STATE_IND) == U_STATE_IND) else if ((rxByte & U_STATE_IND) == U_STATE_IND)
{ {
print("got U_STATE_IND: 0x"); print("got U_STATE_IND:");
print(rxByte, HEX); if (rxByte & 0x80) print (" SC");
if (rxByte & 0x40) print (" RE");
if (rxByte & 0x20) print (" TE");
if (rxByte & 0x10) print (" PE");
if (rxByte & 0x08) print (" TW");
println(); println();
} }
else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND) else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND)
@ -217,24 +229,26 @@ void TpUartDataLinkLayer::loop()
print(rxByte, HEX); print(rxByte, HEX);
println(); println();
} }
_loopState = IDLE; }
break; break;
case RX_L_DATA: case RX_L_DATA:
if (millis() - _lastByteRxTime > BYTE_TIMEOUT) if (isEOP)
{ {
_RxByteCnt = 0; _rxState = RX_WAIT_START;
_loopState = IDLE; print("EOP inside RX_L_DATA");
println("Timeout during RX_L_DATA"); printHex(" => ", buffer, _RxByteCnt);
break; break;
} }
if (!_platform.uartAvailable()) if (!_platform.uartAvailable())
break; break;
_lastByteRxTime = millis(); _lastByteRxTime = millis();
rxByte = _platform.readUart(); rxByte = _platform.readUart();
#ifdef DBG_TRACE
print(rxByte, HEX);
#endif
if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE)
{ {
_loopState = IDLE; _rxState = RX_WAIT_EOP;
println("invalid telegram size"); println("invalid telegram size");
} }
else else
@ -280,6 +294,7 @@ void TpUartDataLinkLayer::loop()
c |= 0x01; c |= 0x01;
} }
// Hint: We can send directly here, this doesn't disturb other transmissions
_platform.writeUart(c); _platform.writeUart(c);
} }
} }
@ -299,18 +314,21 @@ void TpUartDataLinkLayer::loop()
} }
if (_isEcho) if (_isEcho)
{ {
_loopState = RX_WAIT_DATA_CON; isEchoComplete = true;
} }
else else
{ {
frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); frameBytesReceived(_receiveBuffer, _RxByteCnt + 2);
_loopState = IDLE;
} }
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
println("RX_WAIT_START");
#endif
} }
else else
{ {
println("frame with invalid crc ignored"); println("frame with invalid crc ignored");
_loopState = IDLE; _rxState = RX_WAIT_EOP;
} }
} }
else else
@ -318,40 +336,78 @@ void TpUartDataLinkLayer::loop()
_xorSum ^= rxByte; _xorSum ^= rxByte;
} }
break; break;
case RX_WAIT_DATA_CON: case RX_WAIT_EOP:
if (isEOP)
{
_RxByteCnt = 0;
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
println("RX_WAIT_START");
#endif
break;
}
if (!_platform.uartAvailable()) if (!_platform.uartAvailable())
break; break;
rxByte = _platform.readUart();
_lastByteRxTime = millis(); _lastByteRxTime = millis();
if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) rxByte = _platform.readUart();
{ #ifdef DBG_TRACE
//println("L_DATA_CON received"); print(rxByte, HEX);
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, ((rxByte & SUCCESS) > 0)); #endif
_waitConfirm = false;
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_loopState = IDLE;
}
else
{
//should not happen
println("expected L_DATA_CON not received");
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, false);
_waitConfirm = false;
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_loopState = IDLE;
}
break; break;
default: default:
break; break;
} }
} while (_rxState == RX_L_DATA);
if (_waitConfirm) // Check for spurios DATA_CONN message
if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) {
println("got unexpected L_DATA_CON");
}
switch (_txState)
{ {
if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT) case TX_IDLE:
if (!isTxQueueEmpty())
{
loadNextTxFrame();
_txState = TX_FRAME;
#ifdef DBG_TRACE
println("TX_FRAME");
#endif
}
break;
case TX_FRAME:
if (sendSingleFrameByte() == false)
{
_waitConfirmStartTime = millis();
_txState = TX_WAIT_ECHO;
#ifdef DBG_TRACE
println("TX_WAIT_ECHO");
#endif
}
break;
case TX_WAIT_ECHO:
case TX_WAIT_CONN:
if (isEchoComplete)
{
_txState = TX_WAIT_CONN;
#ifdef DBG_TRACE
println("TX_WAIT_CONN");
#endif
}
else if (dataConnMsg)
{
bool waitEcho = (_txState == TX_WAIT_ECHO);
if (waitEcho) {
println("L_DATA_CON without echo");
}
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0));
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_txState = TX_IDLE;
}
else if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT)
{ {
println("L_DATA_CON not received within expected time"); println("L_DATA_CON not received within expected time");
uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE]; uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE];
@ -359,13 +415,15 @@ void TpUartDataLinkLayer::loop()
cemiBuffer[1] = 0; cemiBuffer[1] = 0;
memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength); memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength);
dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false); dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false);
_waitConfirm = false;
delete[] _sendBuffer; delete[] _sendBuffer;
_sendBuffer = 0; _sendBuffer = 0;
_sendBufferLength = 0; _sendBufferLength = 0;
if (_loopState == RX_WAIT_DATA_CON) _txState = TX_IDLE;
_loopState = IDLE; #ifdef DBG_TRACE
println("TX_IDLE");
#endif
} }
break;
} }
} }
@ -496,16 +554,21 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
cmd[0] = U_L_DATA_END_REQ | _TxByteCnt; cmd[0] = U_L_DATA_END_REQ | _TxByteCnt;
cmd[1] = _sendBuffer[_TxByteCnt]; cmd[1] = _sendBuffer[_TxByteCnt];
#ifdef DBG_TRACE
print(cmd[1], HEX);
#endif
_platform.writeUart(cmd, 2); _platform.writeUart(cmd, 2);
_TxByteCnt++; _TxByteCnt++;
return true;
} }
else
// Check for last byte send
if (_TxByteCnt >= _sendBufferLength)
{ {
_TxByteCnt = 0; _TxByteCnt = 0;
return false; return false;
} }
return true;
} }
void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame) void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame)

View File

@ -31,16 +31,15 @@ class TpUartDataLinkLayer : public DataLinkLayer
private: private:
bool _enabled = false; bool _enabled = false;
bool _waitConfirm = false;
uint8_t* _sendBuffer = 0; uint8_t* _sendBuffer = 0;
uint16_t _sendBufferLength = 0; uint16_t _sendBufferLength = 0;
uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE]; uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE];
uint8_t _loopState = 0; uint8_t _txState = 0;
uint8_t _rxState = 0;
uint16_t _RxByteCnt = 0; uint16_t _RxByteCnt = 0;
uint16_t _TxByteCnt = 0; uint16_t _TxByteCnt = 0;
uint8_t _oldIdx = 0; uint8_t _oldIdx = 0;
bool _isEcho = false; bool _isEcho = false;
bool _isAddressed = false;
bool _convert = false; bool _convert = false;
uint8_t _xorSum = 0; uint8_t _xorSum = 0;
uint32_t _lastByteRxTime; uint32_t _lastByteRxTime;

View File

@ -8,6 +8,10 @@
#include "knx/bau2920.h" #include "knx/bau2920.h"
#include "knx/bau57B0.h" #include "knx/bau57B0.h"
#ifndef USERDATA_SAVE_SIZE
#define USERDATA_SAVE_SIZE 0
#endif
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
#include "samd_platform.h" #include "samd_platform.h"
#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE
@ -45,7 +49,8 @@
#endif #endif
#endif #endif
typedef uint8_t* (*SaveRestoreCallback)(uint8_t* buffer); typedef const uint8_t* (*RestoreCallback)(const uint8_t* buffer);
typedef uint8_t* (*SaveCallback)(uint8_t* buffer);
typedef void (*IsrFunctionPtr)(); typedef void (*IsrFunctionPtr)();
template <class P, class B> class KnxFacade : private SaveRestore template <class P, class B> class KnxFacade : private SaveRestore
@ -54,18 +59,21 @@ template <class P, class B> class KnxFacade : private SaveRestore
KnxFacade() : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) KnxFacade() : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr)
{ {
manufacturerId(0xfa); manufacturerId(0xfa);
bauNumber(platform().uniqueSerialNumber());
_bau.addSaveRestore(this); _bau.addSaveRestore(this);
} }
KnxFacade(B& bau) : _bau(bau) KnxFacade(B& bau) : _bau(bau)
{ {
manufacturerId(0xfa); manufacturerId(0xfa);
bauNumber(platform().uniqueSerialNumber());
_bau.addSaveRestore(this); _bau.addSaveRestore(this);
} }
KnxFacade(IsrFunctionPtr buttonISRFunction) : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr) KnxFacade(IsrFunctionPtr buttonISRFunction) : _platformPtr(new P()), _bauPtr(new B(*_platformPtr)), _bau(*_bauPtr)
{ {
manufacturerId(0xfa); manufacturerId(0xfa);
bauNumber(platform().uniqueSerialNumber());
_bau.addSaveRestore(this); _bau.addSaveRestore(this);
setButtonISRFunction(buttonISRFunction); setButtonISRFunction(buttonISRFunction);
} }
@ -268,12 +276,12 @@ template <class P, class B> class KnxFacade : private SaveRestore
_progButtonISRFuncPtr = progButtonISRFuncPtr; _progButtonISRFuncPtr = progButtonISRFuncPtr;
} }
void setSaveCallback(SaveRestoreCallback func) void setSaveCallback(SaveCallback func)
{ {
_saveCallback = func; _saveCallback = func;
} }
void setRestoreCallback(SaveRestoreCallback func) void setRestoreCallback(RestoreCallback func)
{ {
_restoreCallback = func; _restoreCallback = func;
} }
@ -286,6 +294,40 @@ template <class P, class B> class KnxFacade : private SaveRestore
return _bau.parameters().data(addr); return _bau.parameters().data(addr);
} }
// paramBit(address, shift)
// get state of a parameter as a boolean like "enable/disable", ...
// Declaration in XML file:
// ...
// <ParameterType Id="M-00FA_A-0066-EA-0001_PT-toggle" Name="toggle">
// <TypeRestriction Base="Value" SizeInBit="1">
// <Enumeration Text="Désactivé" Value="0" Id="M-00FA_A-0066-EA-0001_PT-toggle_EN-0"/>
// <Enumeration Text="Activé" Value="1" Id="M-00FA_A-0066-EA-0001_PT-toggle_EN-1"/>
// </TypeRestriction>
// </ParameterType>
// ...
// <Parameter Id="M-00FA_A-0066-EA-0001_P-2" Name="Input 1" ParameterType="M-00FA_A-0066-EA-0001_PT-toggle" Text="Input 1" Value="1">
// <Memory CodeSegment="M-00FA_A-0066-EA-0001_RS-04-00000" Offset="1" BitOffset="0"/>
// </Parameter>
// <Parameter Id="M-00FA_A-0066-EA-0001_P-3" Name="Input 2" ParameterType="M-00FA_A-0066-EA-0001_PT-toggle" Text="Input 2" Value="1">
// <Memory CodeSegment="M-00FA_A-0066-EA-0001_RS-04-00000" Offset="1" BitOffset="1"/>
// </Parameter>
// <Parameter Id="M-00FA_A-0066-EA-0001_P-4" Name="Inout 3" ParameterType="M-00FA_A-0066-EA-0001_PT-toggle" Text="Input 3" Value="1">
// <Memory CodeSegment="M-00FA_A-0066-EA-0001_RS-04-00000" Offset="1" BitOffset="2"/>
// </Parameter>
// ...
// Usage in code :
// if ( knx.paramBit(1,1))
// {
// //do somthings ....
// }
bool paramBit(uint32_t addr, uint8_t shift)
{
if (!_bau.configured())
return 0;
return (bool) ((_bau.parameters().getByte(addr) >> (7-shift)) & 0x01);
}
uint8_t paramByte(uint32_t addr) uint8_t paramByte(uint32_t addr)
{ {
if (!_bau.configured()) if (!_bau.configured())
@ -294,6 +336,19 @@ template <class P, class B> class KnxFacade : private SaveRestore
return _bau.parameters().getByte(addr); return _bau.parameters().getByte(addr);
} }
// Same usage than paramByte(addresse) for signed parameters
// Declaration in XML file
// <ParameterType Id="M-00FA_A-0066-EA-0001_PT-delta" Name="delta">
// <TypeNumber SizeInBit="8" Type="signedInt" minInclusive="-10" maxInclusive="10"/>
// </ParameterType>
int8_t paramSignedByte(uint32_t addr)
{
if (!_bau.configured())
return 0;
return (int8_t) _bau.parameters().getByte(addr);
}
uint16_t paramWord(uint32_t addr) uint16_t paramWord(uint32_t addr)
{ {
if (!_bau.configured()) if (!_bau.configured())
@ -338,11 +393,11 @@ template <class P, class B> class KnxFacade : private SaveRestore
uint32_t _ledPin = LED_BUILTIN; uint32_t _ledPin = LED_BUILTIN;
uint32_t _buttonPinInterruptOn = RISING; uint32_t _buttonPinInterruptOn = RISING;
uint32_t _buttonPin = 0; uint32_t _buttonPin = 0;
SaveRestoreCallback _saveCallback = 0; SaveCallback _saveCallback = 0;
SaveRestoreCallback _restoreCallback = 0; RestoreCallback _restoreCallback = 0;
volatile bool _toggleProgMode = false; volatile bool _toggleProgMode = false;
bool _progLedState = false; bool _progLedState = false;
uint16_t _saveSize = 0; uint16_t _saveSize = USERDATA_SAVE_SIZE;
IsrFunctionPtr _progButtonISRFuncPtr = 0; IsrFunctionPtr _progButtonISRFuncPtr = 0;
uint8_t* save(uint8_t* buffer) uint8_t* save(uint8_t* buffer)
@ -353,7 +408,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
return buffer; return buffer;
} }
uint8_t* restore(uint8_t* buffer) const uint8_t* restore(const uint8_t* buffer)
{ {
if (_restoreCallback != 0) if (_restoreCallback != 0)
return _restoreCallback(buffer); return _restoreCallback(buffer);

View File

@ -501,6 +501,7 @@ void LinuxPlatform::setupUart()
} }
} }
#ifndef KNX_NO_PRINT
void printUint64(uint64_t value, int base = DEC) void printUint64(uint64_t value, int base = DEC)
{ {
char buf[8 * sizeof(uint64_t) + 1]; char buf[8 * sizeof(uint64_t) + 1];
@ -707,6 +708,7 @@ void println(void)
{ {
printf("\n"); printf("\n");
} }
#endif // KNX_NO_PRINT
void pinMode(uint32_t dwPin, uint32_t dwMode) void pinMode(uint32_t dwPin, uint32_t dwMode)
{ {

View File

@ -6,7 +6,10 @@
#include <Arduino.h> #include <Arduino.h>
#include <FlashAsEEPROM.h> #include <FlashAsEEPROM.h>
SamdPlatform::SamdPlatform() : ArduinoPlatform(&Serial1) SamdPlatform::SamdPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial1)
#endif
{ {
} }
@ -14,6 +17,26 @@ SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{ {
} }
uint32_t SamdPlatform::uniqueSerialNumber()
{
#if defined (__SAMD51__)
// SAMD51 from section 9.6 of the datasheet
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x008061FC)
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x00806010)
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x00806014)
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x00806018)
#else
//#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__)
// SAMD21 from section 9.3.3 of the datasheet
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C)
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040)
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044)
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048)
#endif
return SERIAL_NUMBER_WORD_0 ^ SERIAL_NUMBER_WORD_1 ^ SERIAL_NUMBER_WORD_2 ^ SERIAL_NUMBER_WORD_3;
}
void SamdPlatform::restart() void SamdPlatform::restart()
{ {
println("restart"); println("restart");

View File

@ -10,6 +10,9 @@ public:
SamdPlatform(); SamdPlatform();
SamdPlatform( HardwareSerial* s); SamdPlatform( HardwareSerial* s);
// unique serial number
uint32_t uniqueSerialNumber() override;
void restart(); void restart();
uint8_t* getEepromBuffer(uint16_t size); uint8_t* getEepromBuffer(uint16_t size);
void commitToEeprom(); void commitToEeprom();

View File

@ -1,10 +1,13 @@
#include "stm32_platform.h" #include "stm32_platform.h"
#ifdef ARDUINO_ARCH_STM32 #ifdef ARDUINO_ARCH_STM32
#include <stm32_eeprom.h> #include <EEPROM.h>
#include "knx/bits.h" #include "knx/bits.h"
Stm32Platform::Stm32Platform() : ArduinoPlatform(&Serial2) Stm32Platform::Stm32Platform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&Serial2)
#endif
{ {
} }
@ -17,6 +20,11 @@ Stm32Platform::~Stm32Platform()
delete [] _eepromPtr; delete [] _eepromPtr;
} }
uint32_t Stm32Platform::uniqueSerialNumber()
{
return HAL_GetUIDw0() ^ HAL_GetUIDw1() ^ HAL_GetUIDw2();
}
void Stm32Platform::restart() void Stm32Platform::restart()
{ {
NVIC_SystemReset(); NVIC_SystemReset();

View File

@ -8,6 +8,9 @@ public:
Stm32Platform( HardwareSerial* s); Stm32Platform( HardwareSerial* s);
~Stm32Platform(); ~Stm32Platform();
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff // basic stuff
void restart(); void restart();