├── .gitignore ├── README.md ├── build.sh ├── firmware ├── common │ └── .gitkeep └── hekapoo │ ├── bin │ └── .gitkeep │ ├── out │ └── .gitkeep │ └── src │ ├── hekapoo.c │ └── hekapoo.h └── utilities.js /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore the Si4010 example folders 2 | /firmware/* 3 | # but keep our code 4 | !/firmware/hekapoo 5 | # and skip build artifacts 6 | /firmware/hekapoo/out/* 7 | 8 | # keep the common folder 9 | !/firmware/common 10 | # but none of its contents 11 | /firmware/common/* 12 | # except for a README explaining what should be there 13 | !/firmware/common/README.md 14 | 15 | # keep placeholders 16 | !**/.gitkeep 17 | 18 | # drop common garbage files 19 | *.swp 20 | *.hex 21 | *.out 22 | *.LST 23 | *.#* 24 | *.OBJ 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Hekapoo 2 | So a couple years ago I started porting [samyk/opensesame](https://github.com/samyk/opensesame) to the Si4010. 3 | I think all of this worked but I got distracted with other projects and haven't 4 | gotten around to my eventual goal of making this into a badge shaped like the 5 | those really cool scissors from `Star vs. the Forces of Evil`. 6 | 7 | ### Building 8 | Place the Si4010 examples/common folders contents into firmware/common and copy 9 | over the demo's keyfob_startup.a51 to firmware/hekapoo/src/keyfob_startup.a51. 10 | You can download them here: 11 | https://www.silabs.com/documents/public/example-code/Si4010_example_programs.zip 12 | 13 | You'll also need the Keil C51 compiler, free version is available here: 14 | https://www.keil.com/demo/eval/c51.htm 15 | 16 | Then just run build.sh. IIRC, you'll need some other stuff to actually run this 17 | on the Si4010, but I can't remember. /shrug 18 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run from powershell in the root of the repo with: 4 | # nodemon -e "c,h,a51,sh" -x bash ./build.sh 5 | 6 | KEIL_PATH="C:\Keil_v5" 7 | # Idk if this is needed tbh but w/e 8 | SILABS_PATH="C:\SiLabs\MCU\INC" 9 | 10 | # http://www.keil.com/support/man/docs/c51/c51_cm_directives.htm 11 | COMPILER_FLAGS="DEBUG OBJECTEXTEND BROWSE NOAREGS NOINTPROMOTE CODE SYMBOLS INCDIR(..\src;..\..\common\src;$SILABS_PATH)" 12 | ASSEMBLER_FLAGS="NOMOD51 DEBUG XREF EP INCDIR(..\src;..\..\common\src;$SILABS_PATH)" 13 | LINKER_FLAGS="PL (68) PW (78) IXREF RS (256) CODE (0x800-0X107F ) XDATA (0X80-0X800) STACK (?STACK (0x90))" 14 | 15 | echo ">>> Cleaning up any old build artifacts" 16 | find firmware -type f | grep -E '(OBJ|LST|M51|cyglink.txt|tmp.out)' | xargs rm 17 | 18 | echo ">>> Compiling..." 19 | cd firmware/hekapoo/src 20 | printf "hekapoo.c\t\t" && cmd.exe /C "$KEIL_PATH\C51\BIN\C51.exe hekapoo.c $COMPILER_FLAGS" | tail -n+5 | sed -r 's|.*COMPLETE\.\s+||g' 21 | printf "keyfob_startup.a51\t" && cmd.exe /C "$KEIL_PATH\C51\BIN\A51.EXE keyfob_startup.a51 $ASSEMBLER_FLAGS" | tail -n+5 | sed -r 's|.*COMPLETE\.\s+||g' 22 | 23 | cd ../../common/src 24 | printf "si4010_link.c\t\t" && cmd.exe /C "$KEIL_PATH\C51\BIN\C51.exe si4010_link.c $COMPILER_FLAGS" | tail -n+5 | sed -r 's|.*COMPLETE\.\s+||g' 25 | printf "si4010_data.c\t\t" && cmd.exe /C "$KEIL_PATH\C51\BIN\C51.exe si4010_data.c $COMPILER_FLAGS" | tail -n+5 | sed -r 's|.*COMPLETE\.\s+||g' 26 | printf "si4010_rom_keil.a51\t" && cmd.exe /C "$KEIL_PATH\C51\BIN\A51.EXE si4010_rom_keil.a51 $ASSEMBLER_FLAGS" | tail -n+5 | sed -r 's|.*COMPLETE\.\s+||g' 27 | cd ../../../ 28 | 29 | echo && echo ">>> Linking..." 30 | WIN_DIR=$(cmd.exe /C "echo %cd%" | sed -r "s|\x0d$||") # CRLF :| 31 | cat << HERE > firmware/hekapoo/out/cyglink.txt 32 | "$WIN_DIR\firmware\common\src\si4010_link.OBJ", 33 | "$WIN_DIR\firmware\common\src\si4010_rom_keil.OBJ", 34 | "$WIN_DIR\firmware\common\src\si4010_data.OBJ", 35 | "$WIN_DIR\firmware\hekapoo\src\keyfob_startup.OBJ", 36 | "$WIN_DIR\firmware\hekapoo\src\hekapoo.OBJ" TO "$WIN_DIR\firmware\hekapoo\out\hekapoo.omf" $LINKER_FLAGS 37 | HERE 38 | cmd.exe /C "$KEIL_PATH\C51\BIN\BL51.EXE @$WIN_DIR\firmware\hekapoo\out\cyglink.txt" 39 | cmd.exe /C "$KEIL_PATH\C51\BIN\OH51.EXE $WIN_DIR\firmware\hekapoo\out\hekapoo.omf" 40 | -------------------------------------------------------------------------------- /firmware/common/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liath/hekapoo/c42006ce0859c0e8766a7a6e2d45d26de95e8072/firmware/common/.gitkeep -------------------------------------------------------------------------------- /firmware/hekapoo/bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liath/hekapoo/c42006ce0859c0e8766a7a6e2d45d26de95e8072/firmware/hekapoo/bin/.gitkeep -------------------------------------------------------------------------------- /firmware/hekapoo/out/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liath/hekapoo/c42006ce0859c0e8766a7a6e2d45d26de95e8072/firmware/hekapoo/out/.gitkeep -------------------------------------------------------------------------------- /firmware/hekapoo/src/hekapoo.c: -------------------------------------------------------------------------------- 1 | // normal stuffs 2 | #include "stdlib.h" 3 | // si4010 stuffs 4 | #include "si4010.h" 5 | #include "si4010_api_rom.h" 6 | // our stuffs 7 | #include "hekapoo.h" 8 | 9 | void main(void) { 10 | PDMD = 1; 11 | // Disable the Matrix and Roff modes on GPIO[3:1] 12 | PORT_CTRL &= ~(M_PORT_MATRIX | M_PORT_ROFF | M_PORT_STROBE); 13 | PORT_CTRL |= M_PORT_STROBE; 14 | PORT_CTRL &= (~M_PORT_STROBE); 15 | // Turn LED control off 16 | GPIO_LED = 0; 17 | vSys_Setup(10); // 80ms battery insert time? 18 | vSys_SetMasterTime(0); 19 | // Setup the bandgap 20 | vSys_BandGapLdo(1); 21 | 22 | // if part is burned to user or run mode. 23 | if ((PROT0_CTRL & M_NVM_BLOWN) > 1) { 24 | // Check the first power up bit meaning keyfob is powered on by battery 25 | // insertion 26 | if (0 != (SYSGEN & M_POWER_1ST_TIME)) { 27 | vSys_FirstPowerUp(); // Function will shutdown. 28 | } 29 | } 30 | // Set LED intensity. Valid values are 0 (off), 1, 2 and 3 31 | vSys_LedIntensity(3); 32 | lLEDOnTime = 20; 33 | // Initialize isr call counter 34 | bIsr_DebounceCount = 0; 35 | 36 | // BSR parameters initialization 37 | rBsrSetup.bButtonMask = bButtonMask_c; 38 | rBsrSetup.pbPtsReserveHead = abBsrPtsPlaceHolder; 39 | rBsrSetup.bPtsSize = 3; 40 | rBsrSetup.bPushQualThresh = 3; 41 | // Setup the BSR 42 | vBsr_Setup(&rBsrSetup); 43 | 44 | // Setup the RTC to tick every 5ms and clear it. Keep it disabled. 45 | RTC_CTRL = (0x07 << B_RTC_DIV) | M_RTC_CLR; 46 | // Enable the RTC 47 | RTC_CTRL |= M_RTC_ENA; 48 | // Enable the RTC interrupt and global interrupt enable 49 | ERTC = 1; 50 | EA = 1; 51 | 52 | // Setup the PA. 53 | rPaSetup.bLevel = b_PaLevel_c; 54 | rPaSetup.wNominalCap = b_PaNominalCap_c; 55 | rPaSetup.bMaxDrv = b_PaMaxDrv_c; 56 | rPaSetup.fAlpha = 0.0; 57 | rPaSetup.fBeta = 0.3462; 58 | vPa_Setup(&rPaSetup); 59 | 60 | rOdsSetup.bModulationType = bModOOK_c; 61 | // Setup the STL encoding for Manchester. No user encoding function therefore 62 | // the pointer is NULL. 63 | vStl_EncodeSetup(3, &bEnc_CustomEncode); 64 | fDesiredFreq = f_RkeFreqOOK_c; 65 | 66 | // Setup the ODS 67 | // Set group width to 7, which means 8 bits/encoded byte to be transmitted. 68 | // The value must match the output width of the data encoding function 69 | // set by vStl_EncodeSetup()! 70 | rOdsSetup.bGroupWidth = 7; 71 | rOdsSetup.bClkDiv = 5; 72 | rOdsSetup.bEdgeRate = 0; 73 | // Configure the warm up intervals 74 | rOdsSetup.bLcWarmInt = 8; 75 | rOdsSetup.bDivWarmInt = 5; 76 | rOdsSetup.bPaWarmInt = 4; 77 | // Should be 2000 but this yieled the closet signal to the original 78 | rOdsSetup.wBitRate = 1959; 79 | vOds_Setup(&rOdsSetup); 80 | 81 | // Setup frequency casting .. needed to be called once per boot 82 | vFCast_Setup(); 83 | 84 | // Measure the battery voltage in mV, only once per boot to save power 85 | // Make loaded measurement .. bBatteryWait_c * 17ms wait after loading 86 | iBatteryMv = iMVdd_Measure(bBatteryWait_c); 87 | if (iBatteryMv < iLowBatMv_c) { 88 | bBatStatus = 0; 89 | } else { 90 | bBatStatus = 1; 91 | } 92 | 93 | pbFrameHead = abFrame; 94 | bFrameSize = bFrameSize_c; 95 | 96 | // Setup the DMD temperature sensor for temperature mode 97 | vDmdTs_RunForTemp(3); // Skip first 3 samples 98 | // Wait until there is a valid DMD temp sensor sample 99 | while (0 == bDmdTs_GetSamplesTaken()); 100 | 101 | /*------------------------------------------------------------------------------ 102 | * TRANSMISSION LOOP PHASE 103 | *------------------------------------------------------------------------------ 104 | */ 105 | 106 | // Application loop, including push button state analyzation and transmission. 107 | while (1) { 108 | // Buttons analyzation 109 | vButtonCheck(); 110 | if (bButtonState) { 111 | // Packet transmit repeat counter 112 | bRepeatCount = bRepeatCount_c; 113 | // Transmit. 114 | vRepeatTxLoop(); 115 | // Sync counter increment 116 | } else if ((lSys_GetMasterTime() >> 5) > bMaxWaitForPush_c) { 117 | // if part is burned to user or run mode. 118 | if ((PROT0_CTRL & M_NVM_BLOWN) > 1) { 119 | // Disable all interrupts 120 | EA = 0; 121 | // Shutdown 122 | vSys_Shutdown(); 123 | } 124 | } 125 | } 126 | } 127 | 128 | /*------------------------------------------------------------------------------ 129 | * 130 | * FUNCTION DESCRIPTION: 131 | * This function contains the loop which consists of three procedures, 132 | * tx setup, tx loop and tx shutdown in the application. 133 | * During waiting for repeat transmission, check button state. 134 | * Once any new push button is detected, then transmit the new packet 135 | * instead of the current packet. 136 | * 137 | *------------------------------------------------------------------------------ 138 | */ 139 | void vRepeatTxLoop(void) { 140 | vFCast_Tune(fDesiredFreq); 141 | 142 | // Wait until there is a temperature sample available 143 | while (0 == bDmdTs_GetSamplesTaken()); 144 | // Tune the PA with the temperature as an argument 145 | vPa_Tune(iDmdTs_GetLatestTemp()); 146 | 147 | // Convert whole frame before transmission 148 | vStl_PreLoop(); 149 | do { 150 | // get current timestamp 151 | lTimestamp = lSys_GetMasterTime(); 152 | // if part is burned to user or run mode 153 | if ((PROT0_CTRL & M_NVM_BLOWN) > 1) { 154 | // turn LED on 155 | GPIO_LED = 1; 156 | } 157 | // wait for LED ON time to expire 158 | while ((lSys_GetMasterTime() - lTimestamp) < lLEDOnTime); 159 | GPIO_LED = 0; // turn LED off 160 | // Transmit packet 161 | vStl_SingleTxLoop(pbFrameHead, bFrameSize); 162 | // Wait repeat interval. 163 | while ((lSys_GetMasterTime() - lTimestamp) < wRepeatInterval_c); 164 | } while (--bRepeatCount); 165 | 166 | vStl_PostLoop(); 167 | 168 | // Clear time value for next button push detecting. 169 | vSys_SetMasterTime(0); 170 | 171 | return; 172 | } 173 | 174 | void isr_rtc(void) interrupt INTERRUPT_RTC using 1 { 175 | // Update the master time by 5 every time this isr is run. 176 | // clear the RTC_INT 177 | RTC_CTRL &= ~M_RTC_INT; 178 | vSys_IncMasterTime(5); 179 | bIsr_DebounceCount++; 180 | if ((bIsr_DebounceCount % bDebounceInterval_c) == 0) { 181 | vBsr_Service(); 182 | } 183 | 184 | return; 185 | } 186 | 187 | /*------------------------------------------------------------------------------ 188 | * Update bAp_ButtonState which indicates what to be transmitted. 189 | * Check the elements on PTS (push tracking strcture) to see if any GPIO 190 | * has been pressed or released. 191 | * If any new pressed button has detected, the corresponding flag will be 192 | * set and the associated information will be transmitted in application loop 193 | * procedure. 194 | *----------------------------------------------------------------------------*/ 195 | void vButtonCheck(void) { 196 | // Disable RTC interrupt, or button state might be updated. 197 | ERTC = 0; 198 | bButtonState = 0; // comment this line out if autorepeat needed 199 | // Some buttons were pressed or released 200 | if (bBsr_GetPtsItemCnt()) { 201 | bButtonState = wBsr_Pop() & 0xFF; 202 | if (bPrevBtn) { 203 | bPrevBtn = bButtonState; 204 | bButtonState = 0; 205 | } else { 206 | bPrevBtn = bButtonState; 207 | } 208 | } 209 | // Enable RTC interrupt 210 | ERTC = 1; 211 | 212 | return; 213 | } 214 | 215 | BYTE bEnc_CustomEncode(BYTE xdata *pboEncodedBytes, BYTE biByteToEncode) { 216 | BYTE bitPosition, out; 217 | 218 | bitPosition = 8; 219 | do { 220 | bitPosition--; 221 | out = 0; 222 | 223 | if ((biByteToEncode >> bitPosition) & 1U) { 224 | out |= 0x07; // xxxx0111 225 | } else { 226 | out |= 0x01; // xxxx0001 227 | } 228 | bitPosition--; 229 | 230 | if ((biByteToEncode >> bitPosition) & 1U) { 231 | out |= 0x70; // 0111xxxx 232 | } else { 233 | out |= 0x10; // 0001xxxx 234 | } 235 | 236 | *pboEncodedBytes = out; 237 | pboEncodedBytes++; 238 | } while (bitPosition > 0); 239 | 240 | return 4; 241 | } 242 | 243 | void vIsr_Dmd(void) interrupt INTERRUPT_DMD using 2 { 244 | vDmdTs_ClearDmdIntFlag(); 245 | vDmdTs_IsrCall(); 246 | } 247 | -------------------------------------------------------------------------------- /firmware/hekapoo/src/hekapoo.h: -------------------------------------------------------------------------------- 1 | #ifndef _HEKAPOO_H 2 | #define _HEKAPOO_H 3 | 4 | #include "si4010_types.h" 5 | 6 | // defines GPIO pins used for button input (see button vector mapping in AN370) 7 | #define bButtonMask_c 0x1F; // GPIO0, GPIO1, GPIO2, GPIO3, GPIO4 8 | #define bModOOK_c 0 9 | 10 | #define f_RkeFreqOOK_c 3E+8 // for RKEdemo OOK 11 | #define b_PaLevel_c 37 // PA level 12 | #define b_PaMaxDrv_c 0 // PA level 13 | #define b_PaNominalCap_c 10 // PA level 14 | 15 | #define iLowBatMv_c 2500 16 | 17 | #define bButton1_c 0x01 18 | #define bButton2_c 0x02 19 | #define bButton3_c 0x04 20 | #define bButton4_c 0x08 21 | #define bButton5_c 0x10 22 | // defines position of button bits in status byte of the transmitted packets 23 | #define M_ButtonBits_c 0x1F 24 | 25 | // Amount of time, the application will wait after boot without 26 | // getting a qualified button push before giving up and shutting the chip down 27 | #define bMaxWaitForPush_c 50 28 | #define wRepeatInterval_c 100 // ms 29 | #define bRepeatCount_c 1 30 | #define bBatteryWait_c 100 31 | // This specifies the number of times that the RTC ISR is called between 32 | // each call to the button service routine. The actual time between 33 | // calls is dependent on the setting of RTC_CTRL.RTC_DIV which dictates how 34 | // often the RTC ISR is called (5ms in this demo).It means that the interval 35 | // between calls to the vBsr_Service() function is 5*2=10ms. 36 | #define bDebounceInterval_c 2 37 | // Size of FIFO of captured buttons .. max number of unserviced button changes 38 | #define bPtsSize_c 3 39 | //--------------------------------------------------------------------------- 40 | // PROTOTYPES OF STATIC FUNCTIONS: 41 | //------------------------------------------------------------------------- 42 | // Assemble packet for Rke demo receiver 43 | void vPacketAssemble(void); 44 | // Buttoons checking 45 | void vButtonCheck(void); 46 | 47 | void vRepeatTxLoop(void); 48 | 49 | BYTE bEnc_CustomEncode(BYTE xdata *pboEncodedBytes, BYTE biByteToEncode); 50 | 51 | //--------------------------------------------------------------------------- 52 | // VARIABLES: 53 | BYTE bIsr_DebounceCount; 54 | LWORD xdata lLEDOnTime; 55 | BYTE xdata bRepeatCount; 56 | LWORD lTimestamp; 57 | BYTE bdata bPrevBtn = 0; 58 | // Pointer to the head of the frame to be sent out. 59 | BYTE xdata *pbFrameHead; 60 | BYTE xdata bFrameSize; 61 | BYTE bBatStatus; 62 | 63 | // de bruijn, precomputed for 10bit sweeping using utilities.js 64 | code BYTE abFrame[128] = {0x00, 0x20, 0x18, 0x0a, 0x03, 0x81, 0x20, 0x58, 0x1a, 65 | 0x07, 0x82, 0x20, 0x98, 0x2a, 0x0b, 0x83, 0x20, 0xd8, 0x3a, 0x0f, 0x84, 0x23, 66 | 0x09, 0x42, 0x70, 0xa4, 0x2b, 0x0b, 0x42, 0xf0, 0xc4, 0x33, 0x0d, 0x43, 0x70, 67 | 0xe4, 0x3b, 0x0f, 0x43, 0xf1, 0x14, 0x47, 0x12, 0x44, 0xb1, 0x34, 0x4f, 0x14, 68 | 0xc5, 0x51, 0x5c, 0x59, 0x16, 0xc5, 0xd1, 0x7c, 0x63, 0x28, 0xce, 0x34, 0x8d, 69 | 0x63, 0x68, 0xde, 0x39, 0x8e, 0xa3, 0xb8, 0xf2, 0x3d, 0x8f, 0xa3, 0xf9, 0x26, 70 | 0x4a, 0x92, 0xe4, 0xd9, 0x3a, 0x4f, 0x94, 0xa7, 0x2a, 0xca, 0xd2, 0xbc, 0xb3, 71 | 0x2d, 0x4b, 0x72, 0xec, 0xbd, 0x2f, 0xcc, 0xd3, 0x3c, 0xd5, 0x35, 0xcd, 0xb3, 72 | 0x74, 0xdf, 0x39, 0xd6, 0x76, 0x9d, 0xe7, 0xa9, 0xee, 0x7d, 0x9f, 0xa7, 0xfa, 73 | 0xab, 0xab, 0x6a, 0xfa, 0xd6, 0xf5, 0xdd, 0x7b, 0x5f, 0xdb, 0x76, 0xfd, 0xdf, 74 | 0x7b, 0xff}; 75 | 76 | #define bFrameSize_c sizeof(abFrame) 77 | 78 | int iBatteryMv; 79 | 80 | BYTE xdata bButtonState; 81 | 82 | float xdata fDesiredFreq; 83 | /* Structure for setting up the ODS .. must be in XDATA */ 84 | tOds_Setup xdata rOdsSetup; 85 | 86 | /* Structure for setting up the XO .. must be in XDATA */ 87 | tFCast_XoSetup xdata rXoSetup; 88 | 89 | /* Structure for setting up the PA .. must be in XDATA */ 90 | tPa_Setup xdata rPaSetup; 91 | 92 | /* BSR control structure */ 93 | tBsr_Setup xdata rBsrSetup; 94 | BYTE xdata abBsrPtsPlaceHolder[bPtsSize_c * 2] = {0}; 95 | WORD wPacketCount; 96 | #endif /* _HEKAPOO_H */ 97 | -------------------------------------------------------------------------------- /utilities.js: -------------------------------------------------------------------------------- 1 | const debruijn = (k, n) => { 2 | const a = [...Array(k * n)].fill(0); 3 | let sequence = []; 4 | 5 | const db = (t, p) => { 6 | if (t > n) { 7 | if (n % p === 0) { 8 | sequence = sequence.concat(a.slice(1, p + 1)); 9 | } 10 | } else { 11 | a[t] = a[t - p]; 12 | db(t + 1, p); 13 | for (let j = a[t - p] + 1; j <= k; j++) { 14 | a[t] = j; 15 | db(t + 1, t); 16 | } 17 | } 18 | }; 19 | 20 | db(1, 1); 21 | return sequence.join(''); 22 | }; 23 | 24 | const binaryStreamStringToBytes = (string) => { 25 | // 1100110001011001 -> 11001100 01011001 26 | let chunks = string.split(/(.{8})/).filter((x) => x); 27 | // 11001100 01011001 -> 00110011 10011010 28 | // chunks = chunks.map((x) => [...x].reverse().join('')); 29 | // 00110011 10011010 -> 0x33 0x9a 30 | chunks = chunks.map((x) => 31 | `0x${parseInt(x, 2).toString(16).padStart(2, 0)}`); 32 | 33 | return chunks.join(', '); 34 | }; 35 | 36 | const printChunks = (byteString) => 37 | byteString.split(' ').reduce((s, x, i) => { 38 | const p = Math.floor(i / 9); 39 | s[p] = (s[p] || []).concat(x); 40 | return s; 41 | }, []).forEach((x) => console.log(x.join(' '))); 42 | 43 | module.exports = { 44 | binaryStreamStringToBytes, 45 | debruijn, 46 | printChunks, 47 | }; 48 | 49 | if (require.main === module) { 50 | // de bruijn for hekapoo.h 51 | console.log(printChunks( 52 | binaryStreamStringToBytes( 53 | debruijn(1, 10)))); 54 | } 55 | --------------------------------------------------------------------------------