From 33c1f968f2b1e1faea506b82160bc47fbd6f72e8 Mon Sep 17 00:00:00 2001 From: Bob Date: Sun, 4 Oct 2015 17:27:19 +0200 Subject: [PATCH] first checkin --- bitlair_doorduino/.bitlair_doorduino.ino.swp | Bin 0 -> 28672 bytes bitlair_doorduino/Entropy.cpp | 344 +++++++++++ bitlair_doorduino/Entropy.h | 73 +++ bitlair_doorduino/OneWire.cpp | 563 ++++++++++++++++++ bitlair_doorduino/OneWire.h | 250 ++++++++ bitlair_doorduino/bitlair_doorduino.ino | 423 +++++++++++++ bitlair_doorduino/ds1961.cpp | 325 ++++++++++ bitlair_doorduino/ds1961.h | 23 + bitlair_doorduino/sha1.cpp | 156 +++++ bitlair_doorduino/sha1.h | 69 +++ bitlair_writesecretduino/OneWire.cpp | 563 ++++++++++++++++++ bitlair_writesecretduino/OneWire.h | 250 ++++++++ .../bitlair_writesecretduino.ino | 187 ++++++ bitlair_writesecretduino/ds1961.cpp | 325 ++++++++++ bitlair_writesecretduino/ds1961.h | 23 + 15 files changed, 3574 insertions(+) create mode 100644 bitlair_doorduino/.bitlair_doorduino.ino.swp create mode 100644 bitlair_doorduino/Entropy.cpp create mode 100644 bitlair_doorduino/Entropy.h create mode 100644 bitlair_doorduino/OneWire.cpp create mode 100644 bitlair_doorduino/OneWire.h create mode 100644 bitlair_doorduino/bitlair_doorduino.ino create mode 100644 bitlair_doorduino/ds1961.cpp create mode 100644 bitlair_doorduino/ds1961.h create mode 100644 bitlair_doorduino/sha1.cpp create mode 100644 bitlair_doorduino/sha1.h create mode 100644 bitlair_writesecretduino/OneWire.cpp create mode 100644 bitlair_writesecretduino/OneWire.h create mode 100644 bitlair_writesecretduino/bitlair_writesecretduino.ino create mode 100644 bitlair_writesecretduino/ds1961.cpp create mode 100644 bitlair_writesecretduino/ds1961.h diff --git a/bitlair_doorduino/.bitlair_doorduino.ino.swp b/bitlair_doorduino/.bitlair_doorduino.ino.swp new file mode 100644 index 0000000000000000000000000000000000000000..d3cfa55c17df88f832da3cb140b809bf3311f431 GIT binary patch literal 28672 zcmeI43vgW3d4QK!Q$oO|DO1Q}dMwBGuDq7CYuO0Dii{=O)W)tJcELDerML?|;wZKmU3B|2ga7j$K=~tBcz@6I=@uiF13--}Um*%M%Zsmq-kk zhUM;@7ji3#CEpt^mG-ajS9NwwHGbs?g6%uYrEzb>ue1*r{E1wlJd`h$%2j{lrwheW z{hc=MHcmIb8*3oez#JNw_D5HAots#h&2)(5;+93~tv@q3$4l|!SOc*JVhzL^h&2#v zAl5*vfmj2v2L3N;z@IuR@m?x)x~8+z3bDN>~k9I2+D_X9z^x4_|m*F9J0B(VsVH_@npM~?_I02I1 zhhuOUiZBY-zz}SMUbqmxPcY^=_%k>HhoKuTg)F2Y3CrL)0z2P@XW=1u0B(VsVGne} z1+V~~B#85QxCd^7_rnpG0S^XY02V_Fyo60X3SWZ<;Q{z{5L^2o)Lz$d;-oa8HhccY zY2Pmu`@NB}=O;N1;&%=Cttyw#m(wcvv!WlTs9!=UL9U*joqapDw;e1Od@ot?b7f!N zQp?qNT6N6y_NLY*L~OKFRyyI#>LFi^tM#g5Eg$Pt|GZMqRBipiFq zzP=rOYgDnM%B9kzDip1nlwVR6zf|_rur|XM)1;=ATw7t7)8%5&)(!1LCz7|RNcv3- zWj3qDa-G7{%2>R#ud|VAoki`q=d!+@fk-={U~|pZL&P@cmAosH?IV)1F&k}?lT-d7 zsjKZgPG|13&dxpe!u$JX>u3O++y;XPi`*m=L_Z|m9A@8=jm-It*qqod~ORH3-Nl=qT5w_Y*C)4rZfX|-c; zVCUd~w#k~uoBMitu80UE+(;VsLOYgjEal31x*Y?ymzN$FNDh1Z3dLk5vnt!s!F}7? zEA+pqWYyrGFYGJ$xryC+=#Q3f%ht_Xbc#wflCgyhq@*o8Mq4GeJmKXj6oc#f^(xTD z#_7r-8Dfn=H%e3MW!sM3r!?E{X@9I#rls=p$ULadtd({}oV(6_OczXc^T^qyIq!^{ z>RGJLHF(r4?Giw_#%VCXCx@r4#U98FdBuD(SKe3At#P1IwDUM3jH&3eG^)kZYIM?1 zt57mGqzzKPs~?&Kazhh^im&DL1TZp|D>KTE?#ZrNy;m%hNv~?fppvWwctZqRO(tz^ zZIpVRf@)|e=lkUX^N}|+q>@R7{mGoKlBObQl}W2?Dn;oMoBA`Ctj;JUvMiyJOY>6e z9Yt?KOQ~6;RaYS0+uPA0($}l2o29;PT_7*gp}Z@nE1pWCgqhU3b+$A{I+U-fSjAOL zz64JsDgDGH)g?tsno4%1mg>bKRSRCI>iteRE9)PJy;P}e{bSpYbOM?B$C$iJTX9g+ zRebBl!GVDtR}A&{boX7hWoTgQ_8vxEwOW*<+S~oYq*t2uSs>)fBV)2Am4$(|fiRW+ zE!`Qtj0&>QU$PxRK9QwXR3_7r?F?ihOXR3bvOidIL>4Wf!rhV~`VQZ_!gRy;qcUA# z`7hhPDX4lxBxAKOQktC174ynzgB90gVS>Ku(f#t3AR|d;<6v*CCRTF>-swA>J|g)j z6)M3B=(!^z654|Z=zbP?%GWG;6pAAg(|J#|RK{|dwy~DT9XBgSZ?3Z|wy_PN;JOOK zL2+MO%{@kFU+#s1E%}NJ2-UgR;XrPR|DVNITZ%6z{=eIwKY>3lzW-xz1ZH3Zw8Fb! zF+7Q1|5dmj-Um}K4x`|~E*OA*NW9_y+tnd=6%y441e+Q|Jyv=k}^$R?oN6fxHR%*R2Wd{M~}b9Y}=F=8_D2CV~q1qT2NrH`83^ zsr@7LNh7lQS))Xv3D6NEuY}1rOi><(qvu-V)|%8d&z@+rw`xKtx%??g#-?-9weWmX zfO);BMH?x&PESp0W%G)J6l&TcP+Nlp8s*+VEMho8YJ`k*M8b&I#<9e?c0PPywr(kF zUHdR87Lo~*PBdbVgpKvPOm7uwhpYmqfPc;%=};#lOl|+RUOQsf!^ly`7!btQjef zbm`k^b#Y1@FkP{WZFGw_%N}mvkbR$|b*c?Z;Jg=_xkOuBBuu)&71gbHPqY@DQU8Znx(S-4D;>JCCHl6IsICv+Z8Upkj$lUb>% zhuYpgk3Q;jH+TQ+^G3YF0goVxj*II)Y0IaS3s;)*iV-hjc-rF4>@Jn_y#$b>_8eMl zS}i?@z*MQ?DxHo@RB2YQIsMe>$@W;&=d9QRolk__bOk1j|TR>1Tnk=aw&D{*)2Eha*N zX0um==G4LSXm&Q#uTzWapxUyk4ZGpUe-eeL&c}9>z@nsdqH^hlx>(f)^_t8{(o9EU z+buQ|XG_kKWW$*H6a zzI{F0ckJrHDHJC=ZHHw{-Y-vh#d?X9o{c)BFg8_6S(7A1o-MubagZ3PMVo<9&~!vS z0!N<&8&E+5A!sIH8yyYjG97U;=>(BrTR%Osg)VQM9#o6b-jEq3aZ_!yNF>AZD=ulg zKrA7h%5-Jhb@<)y1d31Z2-gLE!SBE?!$Nokzxvzo6g&ubz_lQ9e+(5Ef-6CM_kQSubKx9#9@+ch3n1?v zgX`gH*ah2RGc1S2a6X(3XTS;k^;h9X@B;i0rl1I`VHGHN2Yd&={jcC*_z?UyTm@TT z6UgOycYUv(rBBb&Gh`eOHeig=%UGpWgnf3M?xSMJ>?LQ#=uHQmXA#)*Wn>vO9!;>w ztnZ_4MhW^Q-LWGjH4g8nE4&$hH{EJysbagRn?a=1=|GebD?H&)yY!l4oDRnBR$Xzu zAhWZexuZRn=)5Spp&UA0>GXQcG-oBOl9;~&y}9AdeFg(~K_)r5L=|$$1ZxK(v-FYS zDIrgcMBU<9`~p13gJYb?sEC|IJ=d&}e~Y6sI_5I@oS$R*)P7GQ$eLx>{Dd1z%_pb& zvNTSsN%6e3t?iW8DXqP|J^1>1(wXDkrjMIGXr$VzvY$kgRK2IzdkE{?-u~K4pi_FC zuLsJPg48!{)XZjoJo{5U;@Li=U4y1Zpvxgkp;MdS>{XLaJy+>YNqhg#N#yk_1bg9{ zZ;j?BDN^&3v@aD=4>WNDw(K#Cg&T$~c z&M%atqy(U$afJ1XtOU|( zGB+|bN>FOZzD#&2A;s515^>=WqdmS@k7iIPz{=>yd!%#B*5+o%j)>@r_* zY$J&DNu8~a&Ki?9O_y@hVRSMudQ@CCLN~6JX%5Zmc&|S3B0(42%5iX^7wd0F_EhSQ z&IbGU(UY^T9yKA@eV)(ZSkBIxV5PKn-SJ2%rdd>qCAssZ))rjt+-F?`&&gvfkuKy? zGz;0tx!5=C^q@AHNMXZhqZs0U_u*OD|Nj&Gya9Yl@&AX#q2u~T{QOVBe%J^X!3+5E z55pbsZdeTefRFwR+zp=q@!{Lxas2Tacn5q1zxyxXLy&}J@G8FcPvA@N3HSq$Gx@i| zZ^3VZ_~`G3HLwic2^YaDXX9(bw?NM5e+F)aBX9t=z@_kZI30c>{yMw_a!&ux;Uki5csu+D{`}YB5%@IR4M$-D#NXcwyJ0P?ghg->ycHIJoY_AeWZ(V-F@PK3 z2*?@z9_WBH{0n;jHXMh$;7%9^i4*8H(XA2x6Kf#W!2cEvxCyv=Mwi*lOzCbatcr2R z@i2*@1zVB-PvT)yxe<<*SM4xX2iR=k@7yTovJ*Z_BV5OWrB7xNW#1ty-4Vu6AwbaA!HodUsn^j8!KBqDnDm zJ(DTo#MD)^0*Nl&veA7im23?)lv=)gtrine8|{odmNm0JE8&Dp z%S58q5n)s>*}DU$E8Ol>ScYKjA1?ix=c{)e<+00C?4{wU4t>bv6dmax;2mm*+Urt? zd92KGh^|WA>+PXi@yBeuTYNuskTlU~lTgGB3RAY$QtPp}MyelBrSf$?nTbowZES_( zE?z!x)3A*UaPT5wG}z2vbbg)NClifVkKVH7uH+xRZIBFpa+azsPYRDU&B=1xn_9Ji zD_N>R(Zs6eUoMldv_oymonoiv$hS=?o2)gJBtjA>XnAHe`_L;m$1t)opY<}M z@>3i7MDRjw+AB{cZGM}e)6R6P%&zLZc=a4{+;zzurS6zD>8P@eGFD&QxhlJ|BcqdbBW**YCAL6YIP;O%U(jyUCJb~9 zaY$o&!cQ)hTN1q>X. + +#include +#include "Entropy.h" + +const uint8_t WDT_MAX_8INT=0xFF; +const uint16_t WDT_MAX_16INT=0xFFFF; +const uint32_t WDT_MAX_32INT=0xFFFFFFFF; +// Since the Due TRNG is so fast we don't need a circular buffer for it +#ifndef ARDUINO_SAM_DUE + const uint8_t gWDT_buffer_SIZE=32; + const uint8_t WDT_POOL_SIZE=8; + uint8_t gWDT_buffer[gWDT_buffer_SIZE]; + uint8_t gWDT_buffer_position; + uint8_t gWDT_loop_counter; + volatile uint8_t gWDT_pool_start; + volatile uint8_t gWDT_pool_end; + volatile uint8_t gWDT_pool_count; + volatile uint32_t gWDT_entropy_pool[WDT_POOL_SIZE]; +#endif + +// This function initializes the global variables needed to implement the circular entropy pool and +// the buffer that holds the raw Timer 1 values that are used to create the entropy pool. It then +// Initializes the Watch Dog Timer (WDT) to perform an interrupt every 2048 clock cycles, (about +// 16 ms) which is as fast as it can be set. +void EntropyClass::initialize(void) +{ +#ifndef ARDUINO_SAM_DUE + gWDT_buffer_position=0; + gWDT_pool_start = 0; + gWDT_pool_end = 0; + gWDT_pool_count = 0; +#endif +#if defined(__AVR__) + cli(); // Temporarily turn off interrupts, until WDT configured + MCUSR = 0; // Use the MCU status register to reset flags for WDR, BOR, EXTR, and POWR + _WD_CONTROL_REG |= (1<<_WD_CHANGE_BIT) | (1<TRNG_IDR = 0xFFFFFFFF; + TRNG->TRNG_CR = TRNG_CR_KEY(0x524e47) | TRNG_CR_ENABLE; +#elif defined(__arm__) && defined(TEENSYDUINO) + SIM_SCGC5 |= SIM_SCGC5_LPTIMER; + LPTMR0_CSR = 0b10000100; + LPTMR0_PSR = 0b00000101; // PCS=01 : 1 kHz clock + LPTMR0_CMR = 0x0006; // smaller number = faster random numbers... + LPTMR0_CSR = 0b01000101; + NVIC_ENABLE_IRQ(IRQ_LPTMR); +#endif +} + +// This function returns a uniformly distributed random integer in the range +// of [0,0xFFFFFFFF] as long as some entropy exists in the pool and a 0 +// otherwise. To ensure a proper random return the available() function +// should be called first to ensure that entropy exists. +// +// The pool is implemented as an 8 value circular buffer +uint32_t EntropyClass::random(void) +{ +#ifdef ARDUINO_SAM_DUE + while (! (TRNG->TRNG_ISR & TRNG_ISR_DATRDY)) + ; + retVal = TRNG->TRNG_ODATA; +#else + uint8_t waiting; + while (gWDT_pool_count < 1) + waiting += 1; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + retVal = gWDT_entropy_pool[gWDT_pool_start]; + gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; + --gWDT_pool_count; + } +#endif + return(retVal); +} + +// This function returns one byte of a single 32-bit entropy value, while preserving the remaining bytes to +// be returned upon successive calls to the method. This makes best use of the available entropy pool when +// only bytes size chunks of entropy are needed. Not available to public use since there is a method of using +// the default random method for the end-user to achieve the same results. This internal method is for providing +// that capability to the random method, shown below +uint8_t EntropyClass::random8(void) +{ + static uint8_t byte_position=0; + uint8_t retVal8; + + if (byte_position == 0) + share_entropy.int32 = random(); + retVal8 = share_entropy.int8[byte_position++]; + byte_position = byte_position % 4; + return(retVal8); +} + +// This function returns one word of a single 32-bit entropy value, while preserving the remaining word to +// be returned upon successive calls to the method. This makes best use of the available entropy pool when +// only word sized chunks of entropy are needed. Not available to public use since there is a method of using +// the default random method for the end-user to achieve the same results. This internal method is for providing +// that capability to the random method, shown below +uint16_t EntropyClass::random16(void) +{ + static uint8_t word_position=0; + uint16_t retVal16; + + if (word_position == 0) + share_entropy.int32 = random(); + retVal16 = share_entropy.int16[word_position++]; + word_position = word_position % 2; + return(retVal16); +} + +uint8_t EntropyClass::randomByte(void) +{ + return random8(); +} + +uint16_t EntropyClass::randomWord(void) +{ + return random16(); +} + +// This function returns a uniformly distributed integer in the range of +// of [0,max). The added complexity of this function is required to ensure +// a uniform distribution since the naive modulus max (% max) introduces +// bias for all values of max that are not powers of two. +// +// The loops below are needed, because there is a small and non-uniform chance +// That the division below will yield an answer = max, so we just get +// the next random value until answer < max. Which prevents the introduction +// of bias caused by the division process. This is why we can't use the +// simpler modulus operation which introduces significant bias for divisors +// that aren't a power of two +uint32_t EntropyClass::random(uint32_t max) +{ + uint32_t slice; + + if (max < 2) + retVal=0; + else + { + retVal = WDT_MAX_32INT; + if (max <= WDT_MAX_8INT) // If only byte values are needed, make best use of entropy + { // by diving the long into four bytes and using individually + slice = WDT_MAX_8INT / max; + while (retVal >= max) + retVal = random8() / slice; + } + else if (max <= WDT_MAX_16INT) // If only word values are need, make best use of entropy + { // by diving the long into two words and using individually + slice = WDT_MAX_16INT / max; + while (retVal >= max) + retVal = random16() / slice; + } + else + { + slice = WDT_MAX_32INT / max; + while (retVal >= max) + retVal = random() / slice; + } + } + return(retVal); +} + +// This function returns a uniformly distributed integer in the range of +// of [min,max). +uint32_t EntropyClass::random(uint32_t min, uint32_t max) +{ + uint32_t tmp_random, tmax; + + tmax = max - min; + if (tmax < 1) + retVal=min; + else + { + tmp_random = random(tmax); + retVal = min + tmp_random; + } + return(retVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [0.0,1.0) +float EntropyClass::randomf(void) +{ + float fRetVal; + + // Since c++ doesn't allow bit manipulations of floating point types, we are + // using integer type and arrange its bit pattern to follow the IEEE754 bit + // pattern for single precision floating point value in the range of 1.0 - 2.0 + uint32_t tmp_random = random(); + tmp_random = (tmp_random & 0x007FFFFF) | 0x3F800000; + // We then copy that binary representation from the temporary integer to the + // returned floating point value + memcpy((void *) &fRetVal, (void *) &tmp_random, sizeof(fRetVal)); + // Now translate the value back to its intended range by subtracting 1.0 + fRetVal = fRetVal - 1.0; + return (fRetVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [0.0, max) +float EntropyClass::randomf(float max) +{ + float fRetVal; + fRetVal = randomf() * max; + return(fRetVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [min, max) +float EntropyClass::randomf(float min,float max) +{ + float fRetVal; + float tmax; + tmax = max - min; + fRetVal = (randomf() * tmax) + min; + return(fRetVal); +} + +// This function implements the Marsaglia polar method of converting a uniformly +// distributed random numbers to a normaly distributed (bell curve) with the +// mean and standard deviation specified. This type of random number is useful +// for a variety of purposes, like Monte Carlo simulations. +float EntropyClass::rnorm(float mean, float stdDev) +{ + static float spare; + static float u1; + static float u2; + static float s; + static bool isSpareReady = false; + + if (isSpareReady) + { + isSpareReady = false; + return ((spare * stdDev) + mean); + } else { + do { + u1 = (randomf() * 2) - 1; + u2 = (randomf() * 2) - 1; + s = (u1 * u1) + (u2 * u2); + } while (s >= 1.0); + s = sqrt(-2.0 * log(s) / s); + spare = u2 * s; + isSpareReady = true; + return(mean + (stdDev * u1 * s)); + } +} + +// This function returns a unsigned char (8-bit) with the number of unsigned long values +// in the entropy pool +uint8_t EntropyClass::available(void) +{ +#ifdef ARDUINO_SAM_DUE + return(TRNG->TRNG_ISR & TRNG_ISR_DATRDY); +#else + return(gWDT_pool_count); +#endif +} + +// Circular buffer is not needed with the speed of the Arduino Due trng hardware generator +#ifndef ARDUINO_SAM_DUE +// This interrupt service routine is called every time the WDT interrupt is triggered. +// With the default configuration that is approximately once every 16ms, producing +// approximately two 32-bit integer values every second. +// +// The pool is implemented as an 8 value circular buffer +static void isr_hardware_neutral(uint8_t val) +{ + gWDT_buffer[gWDT_buffer_position] = val; + gWDT_buffer_position++; // every time the WDT interrupt is triggered + if (gWDT_buffer_position >= gWDT_buffer_SIZE) + { + gWDT_pool_end = (gWDT_pool_start + gWDT_pool_count) % WDT_POOL_SIZE; + // The following code is an implementation of Jenkin's one at a time hash + // This hash function has had preliminary testing to verify that it + // produces reasonably uniform random results when using WDT jitter + // on a variety of Arduino platforms + for(gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter) + { + gWDT_entropy_pool[gWDT_pool_end] += gWDT_buffer[gWDT_loop_counter]; + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 10); + gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 6); + } + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 3); + gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 11); + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 15); + gWDT_entropy_pool[gWDT_pool_end] = gWDT_entropy_pool[gWDT_pool_end]; + gWDT_buffer_position = 0; // Start collecting the next 32 bytes of Timer 1 counts + if (gWDT_pool_count == WDT_POOL_SIZE) // The entropy pool is full + gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; + else // Add another unsigned long (32 bits) to the entropy pool + ++gWDT_pool_count; + } +} +#endif + +#if defined( __AVR_ATtiny25__ ) || defined( __AVR_ATtiny45__ ) || defined( __AVR_ATtiny85__ ) +ISR(WDT_vect) +{ + isr_hardware_neutral(TCNT0); +} + +#elif defined(__AVR__) +ISR(WDT_vect) +{ + isr_hardware_neutral(TCNT1L); // Record the Timer 1 low byte (only one needed) +} + +#elif defined(__arm__) && defined(TEENSYDUINO) +void lptmr_isr(void) +{ + LPTMR0_CSR = 0b10000100; + LPTMR0_CSR = 0b01000101; + isr_hardware_neutral(SYST_CVR); +} +#endif + +// The library implements a single global instance. There is no need, nor will the library +// work properly if multiple instances are created. +EntropyClass Entropy; diff --git a/bitlair_doorduino/Entropy.h b/bitlair_doorduino/Entropy.h new file mode 100644 index 0000000..0d7d7a6 --- /dev/null +++ b/bitlair_doorduino/Entropy.h @@ -0,0 +1,73 @@ +// Entropy - A entropy (random number) generator for the Arduino +// +// Copyright 2014 by Walter Anderson +// +// This file is part of Entropy, an Arduino library. +// Entropy is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Entropy is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Entropy. If not, see . + +#ifndef Entropy_h +#define Entropy_h + +#include + +// Separate the ARM Due headers we use +#ifdef ARDUINO_SAM_DUE +#include +#include +#endif + +// Teensy required headers +#ifdef TEENSYDUINO +#include +#endif + +// Separate AVR headers from ARM headers +#ifdef __AVR__ +#include +#include +#include +#endif + +const uint32_t WDT_RETURN_BYTE=256; +const uint32_t WDT_RETURN_WORD=65536; + +union ENTROPY_LONG_WORD +{ + uint32_t int32; + uint16_t int16[2]; + uint8_t int8[4]; +}; + +class EntropyClass +{ +public: + void initialize(void); + uint32_t random(void); + uint32_t random(uint32_t max); + uint32_t random(uint32_t min, uint32_t max); + uint8_t randomByte(void); + uint16_t randomWord(void); + float randomf(void); + float randomf(float max); + float randomf(float min, float max); + float rnorm(float mean, float stdDev); + uint8_t available(void); + private: + ENTROPY_LONG_WORD share_entropy; + uint32_t retVal; + uint8_t random8(void); + uint16_t random16(void); +}; +extern EntropyClass Entropy; +#endif diff --git a/bitlair_doorduino/OneWire.cpp b/bitlair_doorduino/OneWire.cpp new file mode 100644 index 0000000..6d55de0 --- /dev/null +++ b/bitlair_doorduino/OneWire.cpp @@ -0,0 +1,563 @@ +/* +Copyright (c) 2007, Jim Studt (original old version - many contributors since) + +The latest version of this library may be found at: + http://www.pjrc.com/teensy/td_libs_OneWire.html + +OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since +January 2010. At the time, it was in need of many bug fixes, but had +been abandoned the original author (Jim Studt). None of the known +contributors were interested in maintaining OneWire. Paul typically +works on OneWire every 6 to 12 months. Patches usually wait that +long. If anyone is interested in more actively maintaining OneWire, +please contact Paul. + +Version 2.3: + Unknonw chip fallback mode, Roger Clark + Teensy-LC compatibility, Paul Stoffregen + Search bug fix, Love Nystrom + +Version 2.2: + Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com + Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 + Fix DS18B20 example negative temperature + Fix DS18B20 example's low res modes, Ken Butcher + Improve reset timing, Mark Tillotson + Add const qualifiers, Bertrik Sikken + Add initial value input to crc16, Bertrik Sikken + Add target_search() function, Scott Roberts + +Version 2.1: + Arduino 1.0 compatibility, Paul Stoffregen + Improve temperature example, Paul Stoffregen + DS250x_PROM example, Guillermo Lovato + PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com + Improvements from Glenn Trewitt: + - crc16() now works + - check_crc16() does all of calculation/checking work. + - Added read_bytes() and write_bytes(), to reduce tedious loops. + - Added ds2408 example. + Delete very old, out-of-date readme file (info is here) + +Version 2.0: Modifications by Paul Stoffregen, January 2010: +http://www.pjrc.com/teensy/td_libs_OneWire.html + Search fix from Robin James + http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + Use direct optimized I/O in all cases + Disable interrupts during timing critical sections + (this solves many random communication errors) + Disable interrupts during read-modify-write I/O + Reduce RAM consumption by eliminating unnecessary + variables and trimming many to 8 bits + Optimize both crc8 - table version moved to flash + +Modified to work with larger numbers of devices - avoids loop. +Tested in Arduino 11 alpha with 12 sensors. +26 Sept 2008 -- Robin James +http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + +Updated to work with arduino-0008 and to include skip() as of +2007/07/06. --RJL20 + +Modified to calculate the 8-bit CRC directly, avoiding the need for +the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 +-- Tom Pollard, Jan 23, 2008 + +Jim Studt's original library was modified by Josh Larios. + +Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Much of the code was inspired by Derek Yerger's code, though I don't +think much of that remains. In any event that was.. + (copyleft) 2006 by Derek Yerger - Free to distribute freely. + +The CRC code was excerpted and inspired by the Dallas Semiconductor +sample code bearing this copyright. +//--------------------------------------------------------------------------- +// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//-------------------------------------------------------------------------- +*/ + +#include "OneWire.h" + + +OneWire::OneWire(uint8_t pin) +{ + pinMode(pin, INPUT); + bitmask = PIN_TO_BITMASK(pin); + baseReg = PIN_TO_BASEREG(pin); +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return a 0; +// +// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// +uint8_t OneWire::reset(void) +{ + IO_REG_TYPE mask = bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + uint8_t retries = 125; + + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); + interrupts(); + // wait until the wire is high... just in case + do { + if (--retries == 0) return 0; + delayMicroseconds(2); + } while ( !DIRECT_READ(reg, mask)); + + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + interrupts(); + delayMicroseconds(480); + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); // allow it to float + delayMicroseconds(70); + r = !DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(410); + return r; +} + +// +// Write a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +void OneWire::write_bit(uint8_t v) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + + if (v & 1) { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(10); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(55); + } else { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(65); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(5); + } +} + +// +// Read a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +uint8_t OneWire::read_bit(void) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + + noInterrupts(); + DIRECT_MODE_OUTPUT(reg, mask); + DIRECT_WRITE_LOW(reg, mask); + delayMicroseconds(3); + DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise + delayMicroseconds(10); + r = DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(53); + return r; +} + +// +// Write a byte. The writing code uses the active drivers to raise the +// pin high, if you need power after the write (e.g. DS18S20 in +// parasite power mode) then set 'power' to 1, otherwise the pin will +// go tri-state at the end of the write to avoid heating in a short or +// other mishap. +// +void OneWire::write(uint8_t v, uint8_t power /* = 0 */) { + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + OneWire::write_bit( (bitMask & v)?1:0); + } + if ( !power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) { + for (uint16_t i = 0 ; i < count ; i++) + write(buf[i]); + if (!power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +// +// Read a byte +// +uint8_t OneWire::read() { + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if ( OneWire::read_bit()) r |= bitMask; + } + return r; +} + +void OneWire::read_bytes(uint8_t *buf, uint16_t count) { + for (uint16_t i = 0 ; i < count ; i++) + buf[i] = read(); +} + +// +// Do a ROM select +// +void OneWire::select(const uint8_t rom[8]) +{ + uint8_t i; + + write(0x55); // Choose ROM + + for (i = 0; i < 8; i++) write(rom[i]); +} + +// +// Do a ROM skip +// +void OneWire::skip() +{ + write(0xCC); // Skip ROM +} + +void OneWire::depower() +{ + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + interrupts(); +} + +#if ONEWIRE_SEARCH + +// +// You need to use this function to start a search again from the beginning. +// You do not need to do it for the first search, though you could. +// +void OneWire::reset_search() +{ + // reset the search state + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + for(int i = 7; ; i--) { + ROM_NO[i] = 0; + if ( i == 0) break; + } +} + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// +void OneWire::target_search(uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + ROM_NO[0] = family_code; + for (uint8_t i = 1; i < 8; i++) + ROM_NO[i] = 0; + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = FALSE; +} + +// +// Perform a search. If this function returns a '1' then it has +// enumerated the next device and you may retrieve the ROM from the +// OneWire::address variable. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then a 0 is returned. If a new device is found then +// its address is copied to newAddr. Use OneWire::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +uint8_t OneWire::search(uint8_t *newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag) + { + // 1-Wire reset + if (!reset()) + { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + + // issue the search command + write(0xF0); + + // loop to do the search + do + { + // read a bit and its complement + id_bit = read_bit(); + cmp_id_bit = read_bit(); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else + { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy) + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + + // if 0 was picked then record its position in LastZero + if (search_direction == 0) + { + last_zero = id_bit_number; + + // check for Last discrepancy in family + if (last_zero < 9) + LastFamilyDiscrepancy = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[rom_byte_number] |= rom_byte_mask; + else + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + write_bit(search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) + { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + + // check for last device + if (LastDiscrepancy == 0) + LastDeviceFlag = TRUE; + + search_result = TRUE; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[0]) + { + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + search_result = FALSE; + } else { + for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i]; + } + return search_result; + } + +#endif + +#if ONEWIRE_CRC +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#if ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (C) 2000 Dallas Semiconductor Corporation +static const uint8_t PROGMEM dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif + +#if ONEWIRE_CRC16 +bool OneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) +{ + crc = ~crc16(input, len, crc); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc) +{ + static const uint8_t oddparity[16] = + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + for (uint16_t i = 0 ; i < len ; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +#endif + +#endif diff --git a/bitlair_doorduino/OneWire.h b/bitlair_doorduino/OneWire.h new file mode 100644 index 0000000..be62737 --- /dev/null +++ b/bitlair_doorduino/OneWire.h @@ -0,0 +1,250 @@ +#ifndef OneWire_h +#define OneWire_h + +#include + +#if ARDUINO >= 100 +#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc +#else +#include "WProgram.h" // for delayMicroseconds +#include "pins_arduino.h" // for digitalPinToBitMask, etc +#endif + +// You can exclude certain features from OneWire. In theory, this +// might save some space. In practice, the compiler automatically +// removes unused code (technically, the linker, using -fdata-sections +// and -ffunction-sections when compiling, and Wl,--gc-sections +// when linking), so most of these will not result in any code size +// reduction. Well, unless you try to use the missing features +// and redesign your program to not need them! ONEWIRE_CRC8_TABLE +// is the exception, because it selects a fast but large algorithm +// or a small but slow algorithm. + +// you can exclude onewire_search by defining that to 0 +#ifndef ONEWIRE_SEARCH +#define ONEWIRE_SEARCH 1 +#endif + +// You can exclude CRC checks altogether by defining this to 0 +#ifndef ONEWIRE_CRC +#define ONEWIRE_CRC 1 +#endif + +// Select the table-lookup method of computing the 8-bit CRC +// by setting this to 1. The lookup table enlarges code size by +// about 250 bytes. It does NOT consume RAM (but did in very +// old versions of OneWire). If you disable this, a slower +// but very compact algorithm is used. +#ifndef ONEWIRE_CRC8_TABLE +#define ONEWIRE_CRC8_TABLE 1 +#endif + +// You can allow 16-bit CRC checks by defining this to 1 +// (Note that ONEWIRE_CRC must also be 1.) +#ifndef ONEWIRE_CRC16 +#define ONEWIRE_CRC16 1 +#endif + +#define FALSE 0 +#define TRUE 1 + +// Platform specific I/O definitions + +#if defined(__AVR__) +#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM asm("r30") +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) + +#elif defined(__MK20DX128__) || defined(__MK20DX256__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (1) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (*((base)+512)) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) + +#elif defined(__MKL26Z64__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) + +#elif defined(__SAM3X8E__) +// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due. +// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268 +// If you have trouble with OneWire on Arduino Due, please check the +// status of delayMicroseconds() before reporting a bug in OneWire! +#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) +#ifndef PROGMEM +#define PROGMEM +#endif +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) +#endif + +#elif defined(__PIC32MX__) +#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 +#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 +#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 +#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 + +#else +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (pin) +#define IO_REG_TYPE unsigned int +#define IO_REG_ASM +#define DIRECT_READ(base, pin) digitalRead(pin) +#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) +#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) +#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) +#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) +#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture." + +#endif + + +class OneWire +{ + private: + IO_REG_TYPE bitmask; + volatile IO_REG_TYPE *baseReg; + +#if ONEWIRE_SEARCH + // global search state + unsigned char ROM_NO[8]; + uint8_t LastDiscrepancy; + uint8_t LastFamilyDiscrepancy; + uint8_t LastDeviceFlag; +#endif + + public: + OneWire( uint8_t pin); + + // Perform a 1-Wire reset cycle. Returns 1 if a device responds + // with a presence pulse. Returns 0 if there is no device or the + // bus is shorted or otherwise held low for more than 250uS + uint8_t reset(void); + + // Issue a 1-Wire rom select command, you do the reset first. + void select(const uint8_t rom[8]); + + // Issue a 1-Wire rom skip command, to address all on bus. + void skip(void); + + // Write a byte. If 'power' is one then the wire is held high at + // the end for parasitically powered devices. You are responsible + // for eventually depowering it by calling depower() or doing + // another read or write. + void write(uint8_t v, uint8_t power = 0); + + void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0); + + // Read a byte. + uint8_t read(void); + + void read_bytes(uint8_t *buf, uint16_t count); + + // Write a bit. The bus is always left powered at the end, see + // note in write() about that. + void write_bit(uint8_t v); + + // Read a bit. + uint8_t read_bit(void); + + // Stop forcing power onto the bus. You only need to do this if + // you used the 'power' flag to write() or used a write_bit() call + // and aren't about to do another read or write. You would rather + // not leave this powered if you don't have to, just in case + // someone shorts your bus. + void depower(void); + +#if ONEWIRE_SEARCH + // Clear the search state so that if will start from the beginning again. + void reset_search(); + + // Setup the search to find the device type 'family_code' on the next call + // to search(*newAddr) if it is present. + void target_search(uint8_t family_code); + + // Look for the next device. Returns 1 if a new address has been + // returned. A zero might mean that the bus is shorted, there are + // no devices, or you have already retrieved all of them. It + // might be a good idea to check the CRC to make sure you didn't + // get garbage. The order is deterministic. You will always get + // the same devices in the same order. + uint8_t search(uint8_t *newAddr); +#endif + +#if ONEWIRE_CRC + // Compute a Dallas Semiconductor 8 bit CRC, these are used in the + // ROM and scratchpad registers. + static uint8_t crc8(const uint8_t *addr, uint8_t len); + +#if ONEWIRE_CRC16 + // Compute the 1-Wire CRC16 and compare it against the received CRC. + // Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. + // uint8_t buf[13]; + // buf[0] = 0xF0; // Read PIO Registers + // buf[1] = 0x88; // LSB address + // buf[2] = 0x00; // MSB address + // WriteBytes(net, buf, 3); // Write 3 cmd bytes + // ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + // if (!CheckCRC16(buf, 11, &buf[11])) { + // // Handle error. + // } + // + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param inverted_crc - The two CRC16 bytes in the received data. + // This should just point into the received data, + // *not* at a 16-bit integer. + // @param crc - The crc starting value (optional) + // @return True, iff the CRC matches. + static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0); + + // Compute a Dallas Semiconductor 16 bit CRC. This is required to check + // the integrity of data received from many 1-Wire devices. Note that the + // CRC computed here is *not* what you'll get from the 1-Wire network, + // for two reasons: + // 1) The CRC is transmitted bitwise inverted. + // 2) Depending on the endian-ness of your processor, the binary + // representation of the two-byte return value may have a different + // byte order than the two bytes you get from 1-Wire. + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param crc - The crc starting value (optional) + // @return The CRC16, as defined by Dallas Semiconductor. + static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0); +#endif +#endif +}; + +#endif diff --git a/bitlair_doorduino/bitlair_doorduino.ino b/bitlair_doorduino/bitlair_doorduino.ino new file mode 100644 index 0000000..32ff18a --- /dev/null +++ b/bitlair_doorduino/bitlair_doorduino.ino @@ -0,0 +1,423 @@ +#include "OneWire.h" +#include "ds1961.h" + +#include +#include +#include +#include "Entropy.h" +#include "sha1.h" + +#define PIN_1WIRE 2 +#define PIN_LEDGREEN 3 +#define PIN_LEDRED 4 + +#define CMD_BUFSIZE 64 +#define CMD_TIMEOUT 10000 //command timeout in milliseconds + +#define SECRETSIZE 8 +#define ADDRSIZE 8 +#define STORAGESIZE (SECRETSIZE + ADDRSIZE) +#define EEPROMSIZE 1024 +#define SHA1SIZE 20 + +#define IBUTTON_SEARCH_TIMEOUT 60000 //timeout searching for ibutton + +#define LEDState_Off 0 +#define LEDState_Reading 1 +#define LEDState_Authorized 2 +#define LEDState_Busy 3 + +#define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +OneWire ds(PIN_1WIRE); +DS1961 ibutton(&ds); + +int Serialprintf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + +int Serialprintf (const char* fmt, ...) +{ + char buf[256]; + + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + Serial.print(buf); +} + +void SetLEDState(uint8_t ledstate) +{ + if (ledstate == LEDState_Off) + { + digitalWrite(PIN_LEDGREEN, LOW); + digitalWrite(PIN_LEDRED, LOW); + } + else if (ledstate == LEDState_Reading) + { + digitalWrite(PIN_LEDGREEN, LOW); + digitalWrite(PIN_LEDRED, HIGH); + } + else if (ledstate == LEDState_Authorized) + { + digitalWrite(PIN_LEDGREEN, HIGH); + digitalWrite(PIN_LEDRED, LOW); + } + else if (ledstate == LEDState_Busy) + { + digitalWrite(PIN_LEDGREEN, HIGH); + digitalWrite(PIN_LEDRED, HIGH); + } +} + +void setup() +{ + Serial.begin(115200); + Serial.println("DEBUG: Board started"); + + pinMode(PIN_LEDGREEN, OUTPUT); + pinMode(PIN_LEDRED, OUTPUT); + + SetLEDState(LEDState_Off); + + Entropy.initialize(); +} + +void AddButton(uint8_t* addr, uint8_t* secret) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + bool emptyslot = true; + uint16_t startaddr = i * STORAGESIZE; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = EEPROM.read(startaddr + j); + if (eeprombyte != 0xFF && eeprombyte != addr[j]) + { + emptyslot = false; + break; + } + } + + if (emptyslot) + { + for (uint16_t j = 0; j < ADDRSIZE; j++) + EEPROM.write(startaddr + j, addr[j]); + + for (uint16_t j = 0; j < SECRETSIZE; j++) + EEPROM.write(startaddr + j + ADDRSIZE, secret[j]); + + Serialprintf("DEBUG: stored button in slot %i\n", i); + + return; + } + } + + Serial.println("ERROR: no room in eeprom to store button"); +} + +void RemoveButton(uint8_t* addr) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + uint16_t startaddr = i * STORAGESIZE; + bool sameaddr = true; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = EEPROM.read(startaddr + j); + if (eeprombyte != addr[j]) + { + sameaddr = false; + break; + } + } + if (!sameaddr) + continue; + + Serialprintf("DEBUG: erasing slot %i\n", i); + + for (uint16_t j = 0; j < STORAGESIZE; j++) + EEPROM.write(startaddr + j, 0xFF); + } +} + +bool GetButtonSecret(uint8_t* addr, uint8_t* secret) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + uint16_t startaddr = i * STORAGESIZE; + bool sameaddr = true; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = EEPROM.read(startaddr + j); + if (eeprombyte != addr[j]) + { + sameaddr = false; + break; + } + } + + if (sameaddr) + { + Serialprintf("DEBUG: getting secret from slot %i\n", i); + + for (uint16_t j = 0; j < SECRETSIZE; j++) + secret[j] = EEPROM.read(startaddr + j + ADDRSIZE); + + return true; + } + } + + Serial.println("DEBUG: can't find secret for button"); + + return false; +} + +bool AuthenticateButton(uint8_t* addr, uint8_t* secret) +{ + uint8_t mac_from_ibutton[SHA1SIZE]; + uint8_t data[32]; + uint8_t nonce[3]; + + for (uint8_t i = 0; i < sizeof(nonce); i++) + nonce[i] = Entropy.randomByte(); + + if (!ibutton.ReadAuthWithChallenge(addr, 0, nonce, data, mac_from_ibutton)) + { + Serial.println("Button authentication failed"); + return false; + } + + sha1::sha1nfo sha1data = {}; + sha1::sha1_init(&sha1data); + sha1::sha1_write(&sha1data, (const char*)secret, 4); + sha1::sha1_write(&sha1data, (const char*)data, sizeof(data)); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0x40); + sha1::sha1_write(&sha1data, (const char*)addr, ADDRSIZE - 1); + sha1::sha1_write(&sha1data, (const char*)secret + 4, 4); + sha1::sha1_write(&sha1data, (const char*)nonce, sizeof(nonce)); + + uint8_t* sha_computed = sha1::sha1_result(&sha1data); + + uint8_t mac_computed[SHA1SIZE]; + ((uint32_t*)mac_computed)[0] = htonl(ntohl(*(uint32_t *)sha_computed) - 0x67452301); + ((uint32_t*)mac_computed)[1] = htonl(ntohl(*(uint32_t *)(sha_computed+4)) - 0xefcdab89); + ((uint32_t*)mac_computed)[2] = htonl(ntohl(*(uint32_t *)(sha_computed+8)) - 0x98badcfe); + ((uint32_t*)mac_computed)[3] = htonl(ntohl(*(uint32_t *)(sha_computed+12)) - 0x10325476); + ((uint32_t*)mac_computed)[4] = htonl(ntohl(*(uint32_t *)(sha_computed+16)) - 0xc3d2e1f0); + + for (uint8_t i = 0; i < SHA1SIZE; i++) + Serialprintf("%02x %02x\n", ((uint8_t*)mac_from_ibutton)[i], ((uint8_t*)mac_computed)[SHA1SIZE - 1 - i]); + + for (uint8_t i = 0; i < SHA1SIZE; i++) + { + if (mac_from_ibutton[i] != mac_computed[SHA1SIZE - 1 - i]) + return false; + } + + return true; +} + +bool ReadCMD(char* cmdbuf, uint8_t* cmdbuffill) +{ + uint32_t cmdstarttime = millis(); + *cmdbuffill = 0; + for(;;) + { + if (Serial.available()) + { + char input = Serial.read(); + if (input == '\n') + { + cmdbuf[*cmdbuffill] = 0; + return true; + } + else if (*cmdbuffill < CMD_BUFSIZE - 1) + { + cmdbuf[*cmdbuffill] = input; + (*cmdbuffill)++; + } + } + + if (millis() - cmdstarttime >= CMD_TIMEOUT) + { + Serial.println("ERROR: timeout receiving command"); + return false; + } + } +} + +uint8_t NextWordPos(char* cmdbuf, uint8_t cmdbuffill, uint8_t index) +{ + bool foundwhitespace = false; + for (uint8_t i = index; i < cmdbuffill; i++) + { + if (!foundwhitespace) + { + if (cmdbuf[i] == ' ') + foundwhitespace = true; + } + else + { + if (cmdbuf[i] != ' ') + { + return i; + } + } + } + + return 0; +} + +bool GetHexWordFromCMD(char* cmdbuf, uint8_t cmdbuffill, uint8_t* wordpos, uint8_t* wordbuf, uint8_t wordsize, char* wordname) +{ + *wordpos = NextWordPos(cmdbuf, cmdbuffill, *wordpos); + + if (*wordpos == 0) + { + Serialprintf("ERROR: no %s found in command\n", wordname); + return false; + } + else if (cmdbuffill - *wordpos < wordsize * 2) + { + Serialprintf("ERROR: %s is too short\n", wordname); + return false; + } + + for (uint8_t i = 0; i < wordsize; i++) + { + if ((cmdbuf[*wordpos + i * 2] == ' ') || (cmdbuf[*wordpos + i * 2 + 1] == ' ')) + { + Serialprintf("ERROR: %s is too short\n", wordname); + return false; + } + + int numread = sscanf(cmdbuf + *wordpos + i * 2, "%2hhx", wordbuf + i); + if (numread != 1) + { + Serialprintf("ERROR: %s is invalid\n", wordname); + return false; + } + } + + return true; +} + +#define CMD_ADD_BUTTON "add_button" +#define CMD_REMOVE_BUTTON "remove_button" + +void ParseCMD(char* cmdbuf, uint8_t cmdbuffill) +{ + Serial.print("DEBUG: Received cmd: "); + Serial.println(cmdbuf); + + bool isadd = strncmp(CMD_ADD_BUTTON, cmdbuf, strlen(CMD_ADD_BUTTON)) == 0; + bool isremove = strncmp(CMD_REMOVE_BUTTON, cmdbuf, strlen(CMD_REMOVE_BUTTON)) == 0; + + if (isadd || isremove) + { + uint8_t wordpos = 0; + uint8_t addr[ADDRSIZE]; + if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, addr, ADDRSIZE, "address")) + return; + + Serial.print("DEBUG: Received address "); + for (uint8_t i = 0; i < ADDRSIZE; i++) + Serialprintf("%02x", addr[i]); + Serial.print("\n"); + + bool addrvalid = false; + for (uint8_t i = 0; i < ADDRSIZE; i++) + { + if (addr[i] != 0xFF) + { + addrvalid = true; + break; + } + } + if (!addrvalid) + { + Serial.println("ERROR: address FFFFFFFFFFFFFFFF is invalid"); + return; + } + + if (isadd) + { + uint8_t secret[SECRETSIZE]; + if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, secret, SECRETSIZE, "secret")) + return; + + Serial.print("DEBUG: Received secret "); + for (uint8_t i = 0; i < ADDRSIZE; i++) + Serialprintf("%02x", secret[i]); + Serial.print("\n"); + + AddButton(addr, secret); + } + else + { + Serialprintf("DEBUG: removing button\n"); + RemoveButton(addr); + } + } + else + { + Serial.println("Unknown command"); + } +} + +void loop() +{ + uint8_t addr[ADDRSIZE]; + + for(;;) + { + if (Serial.available()) + { + uint8_t input = Serial.read(); + if (input == '\n') + { + SetLEDState(LEDState_Busy); + Serial.println("ready"); + + char cmdbuf[CMD_BUFSIZE] = {}; + uint8_t cmdbuffill; + if (ReadCMD(cmdbuf, &cmdbuffill)) + ParseCMD(cmdbuf, cmdbuffill); + } + } + + SetLEDState(LEDState_Reading); + + ds.reset_search(); + if (ds.search(addr) && OneWire::crc8(addr, 7) == addr[7]) + { + SetLEDState(LEDState_Authorized); + Serial.print("DEBUG: Found iButton with address: "); + for (uint8_t i = 0; i < sizeof(addr); i++) + Serialprintf("%02x", addr[i]); + Serial.print('\n'); + + uint8_t secret[SECRETSIZE]; + GetButtonSecret(addr, secret); + for (uint8_t i = 0; i < SECRETSIZE; i++) + + if (AuthenticateButton(addr, secret)) + delay(1000); + else + SetLEDState(LEDState_Busy); + } + } +} + diff --git a/bitlair_doorduino/ds1961.cpp b/bitlair_doorduino/ds1961.cpp new file mode 100644 index 0000000..37f521e --- /dev/null +++ b/bitlair_doorduino/ds1961.cpp @@ -0,0 +1,325 @@ +#include +#include +#include + +#include "OneWire.h" +#include "ds1961.h" + +// commands used in the DS1961 standard +#define CMD_WRITE_SCRATCHPAD 0x0F +#define CMD_COMPUTE_NEXT_SECRET 0x33 +#define CMD_COPY_SCRATCHPAD 0x55 +#define CMD_LOAD_FIRST_SECRET 0x5A +#define CMD_REFRESH_SCRATCHPAD 0xA3 +#define CMD_READ_AUTH_PAGE 0xA5 +#define CMD_READ_SCRATCHPAD 0xAA +#define CMD_READ_MEMORY 0xF0 + +// memory ranges +#define MEM_DATA_PAGE_0 0x00 +#define MEM_DATA_PAGE_1 0x20 +#define MEM_DATA_PAGE_2 0x40 +#define MEM_DATA_PAGE_3 0x60 +#define MEM_SECRET 0x80 +#define MEM_IDENTITY 0x90 + +// timing (ms) +#define T_CSHA 2 // actually 1.5 +#define T_PROG 10 + + +DS1961::DS1961(OneWire *oneWire) +{ + ow = oneWire; +} + +static bool ResetAndSelect(OneWire *ow, const uint8_t id[8]) +{ + if (!ow->reset()) { + return false; + } + ow->select((uint8_t *) id); + + return true; +} + +static bool WriteScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform write scratchpad command + buf[len++] = CMD_WRITE_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool RefreshScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform refresh scratchpad command + buf[len++] = CMD_REFRESH_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool ReadScratchPad(OneWire *ow, const uint8_t id[8], uint16_t *addr, uint8_t *es, uint8_t data[8]) +{ + uint8_t buf[12]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send read scratchpad command + buf[len++] = CMD_READ_SCRATCHPAD; + ow->write_bytes(buf, len); + + // get TA0/1 and ES + ow->read_bytes(buf + len, 3); + len += 3; + *addr = (buf[2] << 8) | buf[1]; + *es = buf[3]; + + // get data + ow->read_bytes(buf + len, 8); + len += 8; + memcpy(data, buf + 4, 8); + + // check CRC + ow->read_bytes(crc, 2); + return ow->check_crc16(buf, len, crc); +} + +static bool CopyScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es, const uint8_t mac[20]) +{ + uint8_t buf[4]; + int len = 0; + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send copy scratchpad command + arguments + buf[len++] = CMD_COPY_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + buf[len++] = es; // es + ow->write_bytes(buf, len, 1); // write and keep powered + + // wait while MAC is calculated + delay(T_CSHA); + + // send MAC + ow->write_bytes(mac, 20); + + // wait 10 ms + delay(T_PROG); + ow->depower(); + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadAuthPage(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t data[32], uint8_t mac[20]) +{ + uint8_t buf[36]; + uint8_t crc[2]; + uint8_t status; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send command + buf[len++] = CMD_READ_AUTH_PAGE; + buf[len++] = (addr >> 0) & 0xFF; + buf[len++] = (addr >> 8) & 0xFF; + ow->write_bytes(buf, len); + + // read data part + 0xFF + ow->read_bytes(buf + len, 33); + len += 33; + if (buf[35] != 0xFF) { + return false; + } + ow->read_bytes(crc, 2); + if (!ow->check_crc16(buf, len, crc)) { + return false; + } + memcpy(data, buf + 3, 32); + + // read mac part + delay(T_CSHA); + ow->read_bytes(mac, 20); + ow->read_bytes(crc, 2); + if (!ow->check_crc16(mac, 20, crc)) { + return false; + } + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool LoadFirstSecret(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es) +{ + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write auth code + ow->write(CMD_LOAD_FIRST_SECRET); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + ow->write(es, 1); + delay(T_PROG); + ow->depower(); + + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadMemory(OneWire *ow, const uint8_t id[8], int addr, int len, uint8_t data[]) +{ + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write command/addr + ow->write(CMD_READ_MEMORY); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + + // read data + ow->read_bytes(data, len); + + return true; +} + +bool DS1961::ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]) +{ + uint8_t scratchpad[8]; + + // put the challenge in the scratchpad + memset(scratchpad, 0, sizeof(scratchpad)); + memcpy(scratchpad + 4, challenge, 3); + if (!WriteScratchPad(ow, id, addr, scratchpad)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // perform the authenticated read + if (!ReadAuthPage(ow, id, addr, data, mac)) { +// Serial.println("ReadAuthPage failed!"); + return false; + } + + return true; +} + +bool DS1961::WriteSecret(const uint8_t id[8], const uint8_t secret[8]) +{ + uint16_t addr; + uint8_t es; + uint8_t data[8]; + + // write secret to scratch pad + if (!WriteScratchPad(ow, id, MEM_SECRET, secret)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &addr, &es, data)) { +// Serial.println("ReadScratchPad failed!"); + return false; + } + if (!LoadFirstSecret(ow, id, addr, es)) { +// Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + +/* + * Writes 8 bytes of data to specified address + */ +bool DS1961::WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]) +{ + uint8_t spad[8]; + uint16_t ad; + uint8_t es; + + // write data into scratchpad + if (!WriteScratchPad(ow, id, addr, data)) { + Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &ad, &es, spad)) { + Serial.println("ReadScratchPad failed!"); + return false; + } + + // copy scratchpad to EEPROM + if (!CopyScratchPad(ow, id, ad, es, mac)) { + Serial.println("CopyScratchPad failed!"); + return false; + } + + // refresh scratchpad + if (!RefreshScratchPad(ow, id, addr, data)) { + Serial.println("RefreshScratchPad failed!"); + return false; + } + + // re-write with load first secret + if (!LoadFirstSecret(ow, id, addr, es)) { + Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + diff --git a/bitlair_doorduino/ds1961.h b/bitlair_doorduino/ds1961.h new file mode 100644 index 0000000..27179df --- /dev/null +++ b/bitlair_doorduino/ds1961.h @@ -0,0 +1,23 @@ +#ifndef _DS1961_H_ +#define _DS1961_H_ + +#include +#include + +#include "OneWire.h" + +class DS1961 { + +public: + DS1961(OneWire *oneWire); + + bool WriteSecret(const uint8_t id[8], const uint8_t secret[8]); + bool ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]); + bool WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]); + +private: + OneWire *ow; + +}; + +#endif /* _DS1961_H_ */ diff --git a/bitlair_doorduino/sha1.cpp b/bitlair_doorduino/sha1.cpp new file mode 100644 index 0000000..e6dace5 --- /dev/null +++ b/bitlair_doorduino/sha1.cpp @@ -0,0 +1,156 @@ +#include "sha1.h" +#include + +namespace sha1 +{ + /* code */ + #define SHA1_K0 0x5a827999 + #define SHA1_K20 0x6ed9eba1 + #define SHA1_K40 0x8f1bbcdc + #define SHA1_K60 0xca62c1d6 + + void sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; + } + + uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); + } + + void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; + } + + void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; + #ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; + #else + b[s->bufferOffset ^ 3] = data; + #endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } + } + + void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); + } + + void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); + } + + void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); + } + + uint8_t* sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + + #ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } + #endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; + } + + #define HMAC_IPAD 0x36 + #define HMAC_OPAD 0x5c + + void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) { + uint8_t i; + memset(s->keyBuffer, 0, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + sha1_init(s); + for (;keyLength--;) sha1_writebyte(s, *key++); + memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH); + } else { + // Block length keys are used as is + memcpy(s->keyBuffer, key, keyLength); + } + // Start inner hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_IPAD); + } + } + + uint8_t* sha1_resultHmac(sha1nfo *s) { + uint8_t i; + // Complete inner hash + memcpy(s->innerHash,sha1_result(s),HASH_LENGTH); + // Calculate outer hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_OPAD); + for (i=0; iinnerHash[i]); + return sha1_result(s); + } +} diff --git a/bitlair_doorduino/sha1.h b/bitlair_doorduino/sha1.h new file mode 100644 index 0000000..2b1ddf9 --- /dev/null +++ b/bitlair_doorduino/sha1.h @@ -0,0 +1,69 @@ +#ifndef SHA1_H +#define SHA1_H + +#include +#include + +#define __LITTLE_ENDIAN__ +//#define __BIG_ENDIAN__ + +namespace sha1 +{ + /* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + // gcc -Wall -DSHA1TEST -o sha1test sha1.c && ./sha1test + + #ifdef __BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + #elif defined __LITTLE_ENDIAN__ + /* override */ + #elif defined __BYTE_ORDER + # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + # endif + #else // ! defined __LITTLE_ENDIAN__ + # include // machine/endian.h + # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + # endif + #endif + + + /* header */ + + #define HASH_LENGTH 20 + #define BLOCK_LENGTH 64 + + typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; + } sha1nfo; + + /* public API - prototypes - TODO: doxygen*/ + + /** + */ + void sha1_init(sha1nfo *s); + /** + */ + void sha1_writebyte(sha1nfo *s, uint8_t data); + /** + */ + void sha1_write(sha1nfo *s, const char *data, size_t len); + /** + */ + uint8_t* sha1_result(sha1nfo *s); + /** + */ + void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); + /** + */ + uint8_t* sha1_resultHmac(sha1nfo *s); +} + +#endif //SHA1_H diff --git a/bitlair_writesecretduino/OneWire.cpp b/bitlair_writesecretduino/OneWire.cpp new file mode 100644 index 0000000..6d55de0 --- /dev/null +++ b/bitlair_writesecretduino/OneWire.cpp @@ -0,0 +1,563 @@ +/* +Copyright (c) 2007, Jim Studt (original old version - many contributors since) + +The latest version of this library may be found at: + http://www.pjrc.com/teensy/td_libs_OneWire.html + +OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since +January 2010. At the time, it was in need of many bug fixes, but had +been abandoned the original author (Jim Studt). None of the known +contributors were interested in maintaining OneWire. Paul typically +works on OneWire every 6 to 12 months. Patches usually wait that +long. If anyone is interested in more actively maintaining OneWire, +please contact Paul. + +Version 2.3: + Unknonw chip fallback mode, Roger Clark + Teensy-LC compatibility, Paul Stoffregen + Search bug fix, Love Nystrom + +Version 2.2: + Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com + Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 + Fix DS18B20 example negative temperature + Fix DS18B20 example's low res modes, Ken Butcher + Improve reset timing, Mark Tillotson + Add const qualifiers, Bertrik Sikken + Add initial value input to crc16, Bertrik Sikken + Add target_search() function, Scott Roberts + +Version 2.1: + Arduino 1.0 compatibility, Paul Stoffregen + Improve temperature example, Paul Stoffregen + DS250x_PROM example, Guillermo Lovato + PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com + Improvements from Glenn Trewitt: + - crc16() now works + - check_crc16() does all of calculation/checking work. + - Added read_bytes() and write_bytes(), to reduce tedious loops. + - Added ds2408 example. + Delete very old, out-of-date readme file (info is here) + +Version 2.0: Modifications by Paul Stoffregen, January 2010: +http://www.pjrc.com/teensy/td_libs_OneWire.html + Search fix from Robin James + http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + Use direct optimized I/O in all cases + Disable interrupts during timing critical sections + (this solves many random communication errors) + Disable interrupts during read-modify-write I/O + Reduce RAM consumption by eliminating unnecessary + variables and trimming many to 8 bits + Optimize both crc8 - table version moved to flash + +Modified to work with larger numbers of devices - avoids loop. +Tested in Arduino 11 alpha with 12 sensors. +26 Sept 2008 -- Robin James +http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + +Updated to work with arduino-0008 and to include skip() as of +2007/07/06. --RJL20 + +Modified to calculate the 8-bit CRC directly, avoiding the need for +the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 +-- Tom Pollard, Jan 23, 2008 + +Jim Studt's original library was modified by Josh Larios. + +Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Much of the code was inspired by Derek Yerger's code, though I don't +think much of that remains. In any event that was.. + (copyleft) 2006 by Derek Yerger - Free to distribute freely. + +The CRC code was excerpted and inspired by the Dallas Semiconductor +sample code bearing this copyright. +//--------------------------------------------------------------------------- +// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//-------------------------------------------------------------------------- +*/ + +#include "OneWire.h" + + +OneWire::OneWire(uint8_t pin) +{ + pinMode(pin, INPUT); + bitmask = PIN_TO_BITMASK(pin); + baseReg = PIN_TO_BASEREG(pin); +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return a 0; +// +// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// +uint8_t OneWire::reset(void) +{ + IO_REG_TYPE mask = bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + uint8_t retries = 125; + + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); + interrupts(); + // wait until the wire is high... just in case + do { + if (--retries == 0) return 0; + delayMicroseconds(2); + } while ( !DIRECT_READ(reg, mask)); + + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + interrupts(); + delayMicroseconds(480); + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); // allow it to float + delayMicroseconds(70); + r = !DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(410); + return r; +} + +// +// Write a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +void OneWire::write_bit(uint8_t v) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + + if (v & 1) { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(10); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(55); + } else { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(65); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(5); + } +} + +// +// Read a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +uint8_t OneWire::read_bit(void) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + + noInterrupts(); + DIRECT_MODE_OUTPUT(reg, mask); + DIRECT_WRITE_LOW(reg, mask); + delayMicroseconds(3); + DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise + delayMicroseconds(10); + r = DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(53); + return r; +} + +// +// Write a byte. The writing code uses the active drivers to raise the +// pin high, if you need power after the write (e.g. DS18S20 in +// parasite power mode) then set 'power' to 1, otherwise the pin will +// go tri-state at the end of the write to avoid heating in a short or +// other mishap. +// +void OneWire::write(uint8_t v, uint8_t power /* = 0 */) { + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + OneWire::write_bit( (bitMask & v)?1:0); + } + if ( !power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) { + for (uint16_t i = 0 ; i < count ; i++) + write(buf[i]); + if (!power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +// +// Read a byte +// +uint8_t OneWire::read() { + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if ( OneWire::read_bit()) r |= bitMask; + } + return r; +} + +void OneWire::read_bytes(uint8_t *buf, uint16_t count) { + for (uint16_t i = 0 ; i < count ; i++) + buf[i] = read(); +} + +// +// Do a ROM select +// +void OneWire::select(const uint8_t rom[8]) +{ + uint8_t i; + + write(0x55); // Choose ROM + + for (i = 0; i < 8; i++) write(rom[i]); +} + +// +// Do a ROM skip +// +void OneWire::skip() +{ + write(0xCC); // Skip ROM +} + +void OneWire::depower() +{ + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + interrupts(); +} + +#if ONEWIRE_SEARCH + +// +// You need to use this function to start a search again from the beginning. +// You do not need to do it for the first search, though you could. +// +void OneWire::reset_search() +{ + // reset the search state + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + for(int i = 7; ; i--) { + ROM_NO[i] = 0; + if ( i == 0) break; + } +} + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// +void OneWire::target_search(uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + ROM_NO[0] = family_code; + for (uint8_t i = 1; i < 8; i++) + ROM_NO[i] = 0; + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = FALSE; +} + +// +// Perform a search. If this function returns a '1' then it has +// enumerated the next device and you may retrieve the ROM from the +// OneWire::address variable. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then a 0 is returned. If a new device is found then +// its address is copied to newAddr. Use OneWire::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +uint8_t OneWire::search(uint8_t *newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag) + { + // 1-Wire reset + if (!reset()) + { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + + // issue the search command + write(0xF0); + + // loop to do the search + do + { + // read a bit and its complement + id_bit = read_bit(); + cmp_id_bit = read_bit(); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else + { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy) + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + + // if 0 was picked then record its position in LastZero + if (search_direction == 0) + { + last_zero = id_bit_number; + + // check for Last discrepancy in family + if (last_zero < 9) + LastFamilyDiscrepancy = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[rom_byte_number] |= rom_byte_mask; + else + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + write_bit(search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) + { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + + // check for last device + if (LastDiscrepancy == 0) + LastDeviceFlag = TRUE; + + search_result = TRUE; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[0]) + { + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + search_result = FALSE; + } else { + for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i]; + } + return search_result; + } + +#endif + +#if ONEWIRE_CRC +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#if ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (C) 2000 Dallas Semiconductor Corporation +static const uint8_t PROGMEM dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif + +#if ONEWIRE_CRC16 +bool OneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) +{ + crc = ~crc16(input, len, crc); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc) +{ + static const uint8_t oddparity[16] = + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + for (uint16_t i = 0 ; i < len ; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +#endif + +#endif diff --git a/bitlair_writesecretduino/OneWire.h b/bitlair_writesecretduino/OneWire.h new file mode 100644 index 0000000..be62737 --- /dev/null +++ b/bitlair_writesecretduino/OneWire.h @@ -0,0 +1,250 @@ +#ifndef OneWire_h +#define OneWire_h + +#include + +#if ARDUINO >= 100 +#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc +#else +#include "WProgram.h" // for delayMicroseconds +#include "pins_arduino.h" // for digitalPinToBitMask, etc +#endif + +// You can exclude certain features from OneWire. In theory, this +// might save some space. In practice, the compiler automatically +// removes unused code (technically, the linker, using -fdata-sections +// and -ffunction-sections when compiling, and Wl,--gc-sections +// when linking), so most of these will not result in any code size +// reduction. Well, unless you try to use the missing features +// and redesign your program to not need them! ONEWIRE_CRC8_TABLE +// is the exception, because it selects a fast but large algorithm +// or a small but slow algorithm. + +// you can exclude onewire_search by defining that to 0 +#ifndef ONEWIRE_SEARCH +#define ONEWIRE_SEARCH 1 +#endif + +// You can exclude CRC checks altogether by defining this to 0 +#ifndef ONEWIRE_CRC +#define ONEWIRE_CRC 1 +#endif + +// Select the table-lookup method of computing the 8-bit CRC +// by setting this to 1. The lookup table enlarges code size by +// about 250 bytes. It does NOT consume RAM (but did in very +// old versions of OneWire). If you disable this, a slower +// but very compact algorithm is used. +#ifndef ONEWIRE_CRC8_TABLE +#define ONEWIRE_CRC8_TABLE 1 +#endif + +// You can allow 16-bit CRC checks by defining this to 1 +// (Note that ONEWIRE_CRC must also be 1.) +#ifndef ONEWIRE_CRC16 +#define ONEWIRE_CRC16 1 +#endif + +#define FALSE 0 +#define TRUE 1 + +// Platform specific I/O definitions + +#if defined(__AVR__) +#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM asm("r30") +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) + +#elif defined(__MK20DX128__) || defined(__MK20DX256__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (1) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (*((base)+512)) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) + +#elif defined(__MKL26Z64__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) + +#elif defined(__SAM3X8E__) +// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due. +// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268 +// If you have trouble with OneWire on Arduino Due, please check the +// status of delayMicroseconds() before reporting a bug in OneWire! +#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) +#ifndef PROGMEM +#define PROGMEM +#endif +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) +#endif + +#elif defined(__PIC32MX__) +#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 +#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 +#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 +#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 + +#else +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (pin) +#define IO_REG_TYPE unsigned int +#define IO_REG_ASM +#define DIRECT_READ(base, pin) digitalRead(pin) +#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) +#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) +#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) +#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) +#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture." + +#endif + + +class OneWire +{ + private: + IO_REG_TYPE bitmask; + volatile IO_REG_TYPE *baseReg; + +#if ONEWIRE_SEARCH + // global search state + unsigned char ROM_NO[8]; + uint8_t LastDiscrepancy; + uint8_t LastFamilyDiscrepancy; + uint8_t LastDeviceFlag; +#endif + + public: + OneWire( uint8_t pin); + + // Perform a 1-Wire reset cycle. Returns 1 if a device responds + // with a presence pulse. Returns 0 if there is no device or the + // bus is shorted or otherwise held low for more than 250uS + uint8_t reset(void); + + // Issue a 1-Wire rom select command, you do the reset first. + void select(const uint8_t rom[8]); + + // Issue a 1-Wire rom skip command, to address all on bus. + void skip(void); + + // Write a byte. If 'power' is one then the wire is held high at + // the end for parasitically powered devices. You are responsible + // for eventually depowering it by calling depower() or doing + // another read or write. + void write(uint8_t v, uint8_t power = 0); + + void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0); + + // Read a byte. + uint8_t read(void); + + void read_bytes(uint8_t *buf, uint16_t count); + + // Write a bit. The bus is always left powered at the end, see + // note in write() about that. + void write_bit(uint8_t v); + + // Read a bit. + uint8_t read_bit(void); + + // Stop forcing power onto the bus. You only need to do this if + // you used the 'power' flag to write() or used a write_bit() call + // and aren't about to do another read or write. You would rather + // not leave this powered if you don't have to, just in case + // someone shorts your bus. + void depower(void); + +#if ONEWIRE_SEARCH + // Clear the search state so that if will start from the beginning again. + void reset_search(); + + // Setup the search to find the device type 'family_code' on the next call + // to search(*newAddr) if it is present. + void target_search(uint8_t family_code); + + // Look for the next device. Returns 1 if a new address has been + // returned. A zero might mean that the bus is shorted, there are + // no devices, or you have already retrieved all of them. It + // might be a good idea to check the CRC to make sure you didn't + // get garbage. The order is deterministic. You will always get + // the same devices in the same order. + uint8_t search(uint8_t *newAddr); +#endif + +#if ONEWIRE_CRC + // Compute a Dallas Semiconductor 8 bit CRC, these are used in the + // ROM and scratchpad registers. + static uint8_t crc8(const uint8_t *addr, uint8_t len); + +#if ONEWIRE_CRC16 + // Compute the 1-Wire CRC16 and compare it against the received CRC. + // Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. + // uint8_t buf[13]; + // buf[0] = 0xF0; // Read PIO Registers + // buf[1] = 0x88; // LSB address + // buf[2] = 0x00; // MSB address + // WriteBytes(net, buf, 3); // Write 3 cmd bytes + // ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + // if (!CheckCRC16(buf, 11, &buf[11])) { + // // Handle error. + // } + // + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param inverted_crc - The two CRC16 bytes in the received data. + // This should just point into the received data, + // *not* at a 16-bit integer. + // @param crc - The crc starting value (optional) + // @return True, iff the CRC matches. + static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0); + + // Compute a Dallas Semiconductor 16 bit CRC. This is required to check + // the integrity of data received from many 1-Wire devices. Note that the + // CRC computed here is *not* what you'll get from the 1-Wire network, + // for two reasons: + // 1) The CRC is transmitted bitwise inverted. + // 2) Depending on the endian-ness of your processor, the binary + // representation of the two-byte return value may have a different + // byte order than the two bytes you get from 1-Wire. + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param crc - The crc starting value (optional) + // @return The CRC16, as defined by Dallas Semiconductor. + static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0); +#endif +#endif +}; + +#endif diff --git a/bitlair_writesecretduino/bitlair_writesecretduino.ino b/bitlair_writesecretduino/bitlair_writesecretduino.ino new file mode 100644 index 0000000..2456c68 --- /dev/null +++ b/bitlair_writesecretduino/bitlair_writesecretduino.ino @@ -0,0 +1,187 @@ +#include "OneWire.h" +#include "ds1961.h" + +#include + +#define PIN_1WIRE 2 +#define PIN_LEDGREEN 3 +#define PIN_LEDRED 4 + +#define CMD_BUFSIZE 64 +#define CMD_TIMEOUT 10000 //command timeout in milliseconds +#define CMD_SET_SECRET "set_secret" +#define CMD_PING "ping" + +#define SECRETSIZE 8 +#define ADDRSIZE 8 + +#define IBUTTON_SEARCH_TIMEOUT 60000 //timeout searching for ibutton + +OneWire ds(PIN_1WIRE); +DS1961 ibutton(&ds); + +void setup() +{ + Serial.begin(115200); + Serial.println("DEBUG: Board started"); + pinMode(PIN_LEDGREEN, OUTPUT); + pinMode(PIN_LEDRED, OUTPUT); +} + +uint8_t ReadCMD(char* cmdbuf) +{ + uint32_t cmdstarttime; + uint8_t cmdbuffill = 0; + for(;;) + { + if (Serial.available()) + { + char input = Serial.read(); + if (input == '\n') + { + cmdbuf[cmdbuffill] = 0; + return cmdbuffill; + } + else if (cmdbuffill < CMD_BUFSIZE - 1) + { + if (cmdbuffill == 0) + cmdstarttime = millis(); + + cmdbuf[cmdbuffill] = input; + cmdbuffill++; + } + } + else if (cmdbuffill > 0 && millis() - cmdstarttime >= CMD_TIMEOUT) + { + Serial.println("ERROR: timeout receiving command"); + return 0; + } + } +} + +bool GetSecretFromBuf(char* cmdbuf, uint8_t cmdbuffill, uint8_t* secret) +{ + uint8_t secretpos = strlen(CMD_SET_SECRET); + while (cmdbuf[secretpos] == ' ' && secretpos < cmdbuffill) + secretpos++; + + if (secretpos == cmdbuffill) + { + Serial.println("ERROR: no secret received"); + return false; + } + else if (cmdbuffill - secretpos < SECRETSIZE * 2) + { + Serial.println("ERROR: received secret is too short"); + return false; + } + + for (uint8_t i = 0; i < SECRETSIZE; i++) + { + int numread = sscanf(cmdbuf + secretpos + i * 2, "%2hhx", secret + i); + if (numread == 0) + { + Serial.println("ERROR: received secret is invalid"); + return false; + } + } + + Serial.print("INFO: received secret "); + for (uint8_t i = 0; i < SECRETSIZE; i++) + { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", secret[i]); + Serial.print(buf); + } + Serial.print('\n'); + + return true; +} + +void WriteSecretToButton(uint8_t* secret) +{ + Serial.println("INFO: searching for iButton"); + uint32_t searchstart = millis(); + digitalWrite(PIN_LEDRED, HIGH); + do + { + ds.reset_search(); + + uint8_t addr[ADDRSIZE]; + if (ds.search(addr) && OneWire::crc8(addr, 7) == addr[7]) + { + Serial.print("INFO: Found iButton with address: "); + for (uint8_t i = 0; i < sizeof(ADDRSIZE); i++) + { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", addr[i]); + Serial.print(buf); + } + Serial.print('\n'); + + if (ibutton.WriteSecret(addr, secret)) + { + digitalWrite(PIN_LEDRED, LOW); + digitalWrite(PIN_LEDGREEN, HIGH); + + Serial.print("INFO: wrote secret "); + for (uint8_t i = 0; i < SECRETSIZE; i++) + { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", secret[i]); + Serial.print(buf); + } + Serial.print(" to iButton with ID "); + for (uint8_t i = 0; i < ADDRSIZE; i++) + { + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", addr[i]); + Serial.print(buf); + } + Serial.print('\n'); + + delay(2000); + + return; + } + else + { + Serial.println("Writing secret failed"); + } + } + } + while (millis() - searchstart < IBUTTON_SEARCH_TIMEOUT); + + Serial.println("ERROR: timeout writing secret to iButton"); +} + +void WriteSecret(char* cmdbuf, uint8_t cmdbuffill) +{ + Serial.println("DEBUG: received set secret command"); + + uint8_t secret[SECRETSIZE]; + if (!GetSecretFromBuf(cmdbuf, cmdbuffill, secret)) + return; + + WriteSecretToButton(secret); +} + +void loop() +{ + digitalWrite(PIN_LEDGREEN, LOW); + digitalWrite(PIN_LEDRED, LOW); + + char cmdbuf[CMD_BUFSIZE] = {}; + uint8_t cmdbuffill = ReadCMD(cmdbuf); + + if (cmdbuffill == 0) + return; + + if (strncasecmp(CMD_SET_SECRET, cmdbuf, strlen(CMD_SET_SECRET)) == 0) + WriteSecret(cmdbuf, cmdbuffill); + else if (strncasecmp(CMD_PING, cmdbuf, strlen(CMD_PING)) == 0) + Serial.println("pong"); + else + Serial.println("unknown command"); +} + diff --git a/bitlair_writesecretduino/ds1961.cpp b/bitlair_writesecretduino/ds1961.cpp new file mode 100644 index 0000000..37f521e --- /dev/null +++ b/bitlair_writesecretduino/ds1961.cpp @@ -0,0 +1,325 @@ +#include +#include +#include + +#include "OneWire.h" +#include "ds1961.h" + +// commands used in the DS1961 standard +#define CMD_WRITE_SCRATCHPAD 0x0F +#define CMD_COMPUTE_NEXT_SECRET 0x33 +#define CMD_COPY_SCRATCHPAD 0x55 +#define CMD_LOAD_FIRST_SECRET 0x5A +#define CMD_REFRESH_SCRATCHPAD 0xA3 +#define CMD_READ_AUTH_PAGE 0xA5 +#define CMD_READ_SCRATCHPAD 0xAA +#define CMD_READ_MEMORY 0xF0 + +// memory ranges +#define MEM_DATA_PAGE_0 0x00 +#define MEM_DATA_PAGE_1 0x20 +#define MEM_DATA_PAGE_2 0x40 +#define MEM_DATA_PAGE_3 0x60 +#define MEM_SECRET 0x80 +#define MEM_IDENTITY 0x90 + +// timing (ms) +#define T_CSHA 2 // actually 1.5 +#define T_PROG 10 + + +DS1961::DS1961(OneWire *oneWire) +{ + ow = oneWire; +} + +static bool ResetAndSelect(OneWire *ow, const uint8_t id[8]) +{ + if (!ow->reset()) { + return false; + } + ow->select((uint8_t *) id); + + return true; +} + +static bool WriteScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform write scratchpad command + buf[len++] = CMD_WRITE_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool RefreshScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform refresh scratchpad command + buf[len++] = CMD_REFRESH_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool ReadScratchPad(OneWire *ow, const uint8_t id[8], uint16_t *addr, uint8_t *es, uint8_t data[8]) +{ + uint8_t buf[12]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send read scratchpad command + buf[len++] = CMD_READ_SCRATCHPAD; + ow->write_bytes(buf, len); + + // get TA0/1 and ES + ow->read_bytes(buf + len, 3); + len += 3; + *addr = (buf[2] << 8) | buf[1]; + *es = buf[3]; + + // get data + ow->read_bytes(buf + len, 8); + len += 8; + memcpy(data, buf + 4, 8); + + // check CRC + ow->read_bytes(crc, 2); + return ow->check_crc16(buf, len, crc); +} + +static bool CopyScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es, const uint8_t mac[20]) +{ + uint8_t buf[4]; + int len = 0; + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send copy scratchpad command + arguments + buf[len++] = CMD_COPY_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + buf[len++] = es; // es + ow->write_bytes(buf, len, 1); // write and keep powered + + // wait while MAC is calculated + delay(T_CSHA); + + // send MAC + ow->write_bytes(mac, 20); + + // wait 10 ms + delay(T_PROG); + ow->depower(); + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadAuthPage(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t data[32], uint8_t mac[20]) +{ + uint8_t buf[36]; + uint8_t crc[2]; + uint8_t status; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send command + buf[len++] = CMD_READ_AUTH_PAGE; + buf[len++] = (addr >> 0) & 0xFF; + buf[len++] = (addr >> 8) & 0xFF; + ow->write_bytes(buf, len); + + // read data part + 0xFF + ow->read_bytes(buf + len, 33); + len += 33; + if (buf[35] != 0xFF) { + return false; + } + ow->read_bytes(crc, 2); + if (!ow->check_crc16(buf, len, crc)) { + return false; + } + memcpy(data, buf + 3, 32); + + // read mac part + delay(T_CSHA); + ow->read_bytes(mac, 20); + ow->read_bytes(crc, 2); + if (!ow->check_crc16(mac, 20, crc)) { + return false; + } + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool LoadFirstSecret(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es) +{ + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write auth code + ow->write(CMD_LOAD_FIRST_SECRET); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + ow->write(es, 1); + delay(T_PROG); + ow->depower(); + + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadMemory(OneWire *ow, const uint8_t id[8], int addr, int len, uint8_t data[]) +{ + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write command/addr + ow->write(CMD_READ_MEMORY); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + + // read data + ow->read_bytes(data, len); + + return true; +} + +bool DS1961::ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]) +{ + uint8_t scratchpad[8]; + + // put the challenge in the scratchpad + memset(scratchpad, 0, sizeof(scratchpad)); + memcpy(scratchpad + 4, challenge, 3); + if (!WriteScratchPad(ow, id, addr, scratchpad)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // perform the authenticated read + if (!ReadAuthPage(ow, id, addr, data, mac)) { +// Serial.println("ReadAuthPage failed!"); + return false; + } + + return true; +} + +bool DS1961::WriteSecret(const uint8_t id[8], const uint8_t secret[8]) +{ + uint16_t addr; + uint8_t es; + uint8_t data[8]; + + // write secret to scratch pad + if (!WriteScratchPad(ow, id, MEM_SECRET, secret)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &addr, &es, data)) { +// Serial.println("ReadScratchPad failed!"); + return false; + } + if (!LoadFirstSecret(ow, id, addr, es)) { +// Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + +/* + * Writes 8 bytes of data to specified address + */ +bool DS1961::WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]) +{ + uint8_t spad[8]; + uint16_t ad; + uint8_t es; + + // write data into scratchpad + if (!WriteScratchPad(ow, id, addr, data)) { + Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &ad, &es, spad)) { + Serial.println("ReadScratchPad failed!"); + return false; + } + + // copy scratchpad to EEPROM + if (!CopyScratchPad(ow, id, ad, es, mac)) { + Serial.println("CopyScratchPad failed!"); + return false; + } + + // refresh scratchpad + if (!RefreshScratchPad(ow, id, addr, data)) { + Serial.println("RefreshScratchPad failed!"); + return false; + } + + // re-write with load first secret + if (!LoadFirstSecret(ow, id, addr, es)) { + Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + diff --git a/bitlair_writesecretduino/ds1961.h b/bitlair_writesecretduino/ds1961.h new file mode 100644 index 0000000..27179df --- /dev/null +++ b/bitlair_writesecretduino/ds1961.h @@ -0,0 +1,23 @@ +#ifndef _DS1961_H_ +#define _DS1961_H_ + +#include +#include + +#include "OneWire.h" + +class DS1961 { + +public: + DS1961(OneWire *oneWire); + + bool WriteSecret(const uint8_t id[8], const uint8_t secret[8]); + bool ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]); + bool WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]); + +private: + OneWire *ow; + +}; + +#endif /* _DS1961_H_ */