Mercurial > tote
changeset 202:a75e02f9c15a
Add code for version 5.
| author | Radomir Dopieralski <sheep-devel@sheep.art.pl> |
|---|---|
| date | Wed, 07 Dec 2016 21:10:22 +0100 |
| parents | 2e3dc44ed08c |
| children | c4a2d90ad57f |
| files | v5/IRLremote.cpp v5/IRLremote.h v5/Makefile v5/battery.h v5/battery.ino v5/beep.h v5/beep.ino v5/clock.h v5/clock.ino v5/creep.h v5/creep.ino v5/ir.h v5/ir.ino v5/leg.h v5/leg.ino v5/misc.h v5/misc.ino v5/servos.h v5/servos.ino v5/v5.ino |
| diffstat | 20 files changed, 1558 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/IRLremote.cpp Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,50 @@ +/* +Copyright (c) 2014 NicoHood +See the readme for credit to other people. + +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. +*/ + +#include "IRLremote.h" + +//================================================================================ +// User function helper +//================================================================================ + +// ensure available() returns false +uint8_t IRLProtocol = 0; +uint16_t IRLAddress = 0; +uint32_t IRLCommand = 0; + +// Called when directly received correct IR Signal +// Do not use Serial inside, it can crash your Arduino! +void IREvent(uint8_t protocol, uint16_t address, uint32_t command) { + // For no user function set we need to pause IR + // to not overwrite the actual values until they are read. + if (IRLProtocol) return; + IRLAddress = address; + IRLCommand = command; + IRLProtocol = protocol; +} + +void decodeIR(const uint16_t duration){ + // add your own protocols here or create your own decoding function + // you can overwrite this function in your .ino file + decodeAll(duration); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/IRLremote.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,778 @@ +/* +Copyright (c) 2014 NicoHood +See the readme for credit to other people. + +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. +*/ + +#ifndef IRLREMOTE_H +#define IRLREMOTE_H + +#include <Arduino.h> + +//missing 1.0.6 definition workaround +#if ARDUINO == 106 +#define NOT_AN_INTERRUPT -1 +#endif + +// board type detection +#if defined(__MK20DX128__) || defined(__MK20DX256__) +#define IRLREMOTE_ARM +#define IRLREMOTE_TEENSY3 +#elif defined(__SAM3X8E__) +#define IRLREMOTE_ARM +#define IRLREMOTE_DUE +#else +// default to assuming everything else is avr for now +#define IRLREMOTE_AVR +#endif + +// delay_basic is only for avrs. with ARM sending is currently not possible +#ifdef IRLREMOTE_AVR +#include <util/delay_basic.h> +#endif + +//================================================================================ +// Definitions +//================================================================================ + +//NEC +//IRP notation: {38.4k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,-78,(16,-4,1,-173)*) +// Lead + Space logic +#define NEC_HZ 38400 +#define NEC_PULSE 564UL +#define NEC_BLOCKS 4 +#define NEC_ADDRESS_LENGTH 16 +#define NEC_COMMAND_LENGTH 16 +#define NEC_LENGTH 2 + NEC_BLOCKS*8*2 // 2 for lead + space, each block has 8bits: mark and space +#define NEC_TIMEOUT NEC_PULSE*173 +#define NEC_MARK_LEAD NEC_PULSE*16 +#define NEC_SPACE_LEAD NEC_PULSE*8 +#define NEC_SPACE_HOLDING NEC_PULSE*4 +#define NEC_MARK_ZERO NEC_PULSE*1 +#define NEC_MARK_ONE NEC_PULSE*1 +#define NEC_SPACE_ZERO NEC_PULSE*1 +#define NEC_SPACE_ONE NEC_PULSE*3 + +//PANASONIC +//IRP notation: {37k,432}<1,-1|1,-3>(8,-4,3:8,1:8,D:8,S:8,F:8,(D^S^F):8,1,-173)+ +// Lead + Space logic +#define PANASONIC_HZ 37000 +#define PANASONIC_PULSE 432UL +#define PANASONIC_BLOCKS 6 +#define PANASONIC_ADDRESS_LENGTH 16 +#define PANASONIC_COMMAND_LENGTH 32 +#define PANASONIC_LENGTH 2 + PANASONIC_BLOCKS*8*2 // 2 for lead + space, each block has 8bits: mark and space +#define PANASONIC_TIMEOUT PANASONIC_PULSE*173 +#define PANASONIC_MARK_LEAD PANASONIC_PULSE*8 +#define PANASONIC_SPACE_LEAD PANASONIC_PULSE*4 +#define PANASONIC_SPACE_HOLDING 0 // no holding function in this protocol +#define PANASONIC_MARK_ZERO PANASONIC_PULSE*1 +#define PANASONIC_MARK_ONE PANASONIC_PULSE*1 +#define PANASONIC_SPACE_ZERO PANASONIC_PULSE*1 +#define PANASONIC_SPACE_ONE PANASONIC_PULSE*3 + +/* +Panasonic pulse demonstration: + +*---------| |------------| |---------| |---| ... -| |--------- +* | | | | | | | ... | | +* | | | | | | | ... | | +* |------------------------| |---| |---| |- ... |---| +* | Lead Mark | Lead Space | Logical 1 | Log 0 | Data |End| + +*/ + +//SONY 8, 12, 15, 20 +//IRP notation: {40k,600}<1,-1|2,-1>(4,-1,F:8,^22200) +//IRP notation: {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,^45m)+ +//IRP notation: {40k,600}<1,-1|2,-1>(4,-1,F:7,D:8,^45m)+ +//IRP notation: {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,S:8,^45m) + +// Lead + Mark logic +#define SONY_HZ 40000 +#define SONY_PULSE 600UL +#define SONY_BLOCKS_8 1 +#define SONY_BLOCKS_12 2 +#define SONY_BLOCKS_15 2 +#define SONY_BLOCKS_20 3 +#define SONY_ADDRESS_LENGTH_8 0 +#define SONY_ADDRESS_LENGTH_12 5 +#define SONY_ADDRESS_LENGTH_15 8 +#define SONY_ADDRESS_LENGTH_20 13 +#define SONY_COMMAND_LENGTH_8 8 +#define SONY_COMMAND_LENGTH_12 7 +#define SONY_COMMAND_LENGTH_15 7 +#define SONY_COMMAND_LENGTH_20 7 +#define SONY_LENGTH_8 2 + (8-1)*2 // 2 for lead + space, -1 for mark end, 8 bit +#define SONY_LENGTH_12 2 + (7+5-1)*2 // 2 for lead + space, -1 for mark end, 12 bit +#define SONY_LENGTH_15 2 + (7+8-1)*2 // 2 for lead + space, -1 for mark end, 15 bit +#define SONY_LENGTH_20 2 + (7+5+8-1)*2 // 2 for lead + space, -1 for mark end, 20 bit +#define SONY_TIMEOUT_8 22200 +#define SONY_TIMEOUT 45000 // 12, 15, 20 have the same timeout +#define SONY_MARK_LEAD SONY_PULSE*4 +#define SONY_SPACE_LEAD SONY_PULSE*1 +#define SONY_SPACE_HOLDING 0 // no holding function in this protocol +#define SONY_MARK_ZERO SONY_PULSE*1 +#define SONY_MARK_ONE SONY_PULSE*2 +#define SONY_SPACE_ZERO SONY_PULSE*1 +#define SONY_SPACE_ONE SONY_PULSE*1 + +typedef enum IRType{ + IR_NO_PROTOCOL, // 0 + IR_USER, // 1 + IR_ALL, // 2 + IR_NEC, // ... + IR_PANASONIC, + IR_SONY8, + IR_SONY12, + IR_SONY15, + IR_SONY20, + // add new protocols here + IR_RAW, +}; + +// definitions to use decoding functions with extra accuracy +#define IR_EXTRA_ACCURACY true +#define IR_NO_EXTRA_ACCURACY false + +// definition for sending to determine what should be send first +#define IR_ADDRESS_FIRST true +#define IR_COMMAND_FIRST false + +// definition to convert an uint8_t array to an uint16_t/uint32_t at any position (thx timeage!) +#define UINT16_AT_OFFSET(p_to_8, offset) ((uint16_t)*((const uint16_t *)((p_to_8)+(offset)))) +#define UINT32_AT_OFFSET(p_to_8, offset) ((uint32_t)*((const uint32_t *)((p_to_8)+(offset)))) + +// definition to get the higher value +#define MAX(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +// definition to get the lower value +#define MIN(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +//================================================================================ +// Prototypes +//================================================================================ + +// attach the interrupt function +template <IRType irType> +inline void IRLbegin(const uint8_t interrupt) __attribute__((always_inline)); + +// dettach the interrupt function +inline void IRLend(const uint8_t interrupt) __attribute__((always_inline)); + +// variables for IR processing if no user function was set +extern uint8_t IRLProtocol; +extern uint16_t IRLAddress; +extern uint32_t IRLCommand; + +// functions to use if no user function was set +inline bool IRLavailable(void) __attribute__((always_inline)); +inline void IRLreset(void) __attribute__((always_inline)); +inline uint8_t IRLgetProtocol(void) __attribute__((always_inline)); +inline uint16_t IRLgetAddress(void) __attribute__((always_inline)); +inline uint32_t IRLgetCommand(void) __attribute__((always_inline)); + +// function called on a valid IR event, must be overwritten by the user +void __attribute__((weak)) IREvent(uint8_t protocol, uint16_t address, uint32_t command); + +// optinal user function called when the user wants to implement its own decoding function +void __attribute__((weak)) decodeIR(const uint16_t duration); + +// called by interrupt CHANGE +template <IRType irType> +inline void IRLinterrupt(void) __attribute__((always_inline)); + +// special decode function for each protocol +inline void decodeAll(const uint16_t duration) __attribute__((always_inline)); +template <bool extraAccuracy> inline void decodeNec(const uint16_t duration); +template <bool extraAccuracy> inline void decodePanasonic(const uint16_t duration) __attribute__((always_inline)); +template <bool extraAccuracy> inline void decodeSony8(const uint16_t duration); +template <bool extraAccuracy> inline void decodeSony12(const uint16_t duration); +template <bool extraAccuracy> inline void decodeSony15(const uint16_t duration); +template <bool extraAccuracy> inline void decodeSony20(const uint16_t duration); +inline void decodeRaw(const uint16_t duration) __attribute__((always_inline)); + +// functions to check if the received data is valid with the protocol checksums +inline bool IRLcheckInverse0(uint8_t data[]) __attribute__((always_inline)); +inline bool IRLcheckInverse1(uint8_t data[]) __attribute__((always_inline)); +inline bool IRLcheckHolding(uint8_t data[]) __attribute__((always_inline)); +inline bool IRLcheckXOR0(uint8_t data[]) __attribute__((always_inline)); + +// default decoder helper function +template <uint8_t irLength, uint16_t timeoutThreshold, uint16_t markLeadThreshold, uint16_t spaceLeadThreshold, + uint16_t spaceLeadHoldingThreshold, uint16_t markThreshold, uint16_t spaceThreshold, + uint16_t markTimeout, uint16_t spaceTimeout> + inline bool IRLdecode(uint16_t duration, uint8_t data[]) __attribute__((always_inline)); + +// functions to send the protocol +template <IRType irType> +inline void IRLwrite(const uint8_t pin, uint16_t address, uint32_t command) __attribute__((always_inline)); + +template <uint8_t addressLength, uint8_t commandLength, + uint16_t Hz, bool addressFirst, + uint16_t markLead, uint16_t spaceLead, + uint16_t markZero, uint16_t markOne, + uint16_t spaceZero, uint16_t spaceOne> + inline void IRLsend(volatile uint8_t * outPort, uint8_t bitmask, uint16_t address, uint32_t command) __attribute__((always_inline)); + +// functions to set the pin high or low (with bitbang pwm) +inline void IRLmark(const uint16_t Hz, volatile uint8_t * outPort, uint8_t bitMask, uint16_t time) __attribute__((always_inline)); +inline void IRLspace(volatile uint8_t * outPort, uint8_t bitMask, uint16_t time) __attribute__((always_inline)); + +//================================================================================ +// Inline Implementations (receive) +//================================================================================ + +template <IRType irType> +void IRLbegin(const uint8_t interrupt){ + // attach the function that decodes the signals + attachInterrupt(interrupt, IRLinterrupt<irType>, CHANGE); +} + +void IRLend(const uint8_t interrupt){ + // release the interrupt, if its NOT_AN_INTERRUPT the detach function does nothing + detachInterrupt(interrupt); + // also make sure to call reset() after end() if you use the intern reading functions +} + +bool IRLavailable(void){ + return IRLProtocol; +} + +void IRLreset(void){ + IRLProtocol = 0; +} + +uint8_t IRLgetProtocol(void){ + return IRLProtocol; +} + +uint16_t IRLgetAddress(void){ + return IRLAddress; +} + +uint32_t IRLgetCommand(void){ + return IRLCommand; +} + +template <IRType irType> +void IRLinterrupt(void){ + //save the duration between the last reading + static uint32_t lastTime = 0; + uint32_t time = micros(); + uint32_t duration_32 = time - lastTime; + lastTime = time; + + // calculate 16 bit duration. On overflow set duration to a clear timeout + uint16_t duration = 0xFFFF; + if (duration_32 <= 0xFFFF) + duration = duration_32; + + // determinate which decode function must be called + switch (irType){ + case IR_USER: + // userfunction that can be overwritten + decodeIR(duration); + break; + case IR_ALL: + decodeAll(duration); + break; + case IR_NEC: + decodeNec<IR_NO_EXTRA_ACCURACY>(duration); + break; + case IR_PANASONIC: + decodePanasonic<IR_NO_EXTRA_ACCURACY>(duration); + break; + //case IR_SONY8: + // decodeSony8<IR_NO_EXTRA_ACCURACY>(duration); + // break; + case IR_SONY12: + decodeSony12<IR_NO_EXTRA_ACCURACY>(duration); + break; + //case IR_SONY15: + // decodeSony15<IR_NO_EXTRA_ACCURACY>(duration); + // break; + //case IR_SONY20: + // decodeSony20<IR_NO_EXTRA_ACCURACY>(duration); + // break; + case IR_RAW: + decodeRaw(duration); + break; + } +} + +void decodeAll(const uint16_t duration){ + // go through all known protocols and decode with more (resource unfriendly) accuration + decodeNec<IR_EXTRA_ACCURACY>(duration); + decodePanasonic<IR_EXTRA_ACCURACY>(duration); + decodeSony12<IR_EXTRA_ACCURACY>(duration); +} + +template <bool extraAccuracy> void decodeNec(const uint16_t duration){ + // temporary buffer to hold bytes for decoding this protocol + static uint8_t data[NEC_BLOCKS]; + + // pass the duration to the decoding function + bool newInput; + if (extraAccuracy) // no accuracy set at the moment, no conflict detected yet + newInput = IRLdecode <NEC_LENGTH, (NEC_TIMEOUT + NEC_MARK_LEAD) / 2, // irLength, timeoutThreshold + (NEC_MARK_LEAD + NEC_SPACE_ONE) / 2, (NEC_SPACE_LEAD + NEC_SPACE_HOLDING) / 2, // markLeadThreshold, spaceLeadThreshold + (NEC_SPACE_HOLDING + NEC_SPACE_ONE) / 2, 0, // spaceLeadHoldingThreshold, markThreshold + (NEC_SPACE_ONE + NEC_SPACE_ZERO) / 2, // spaceThreshold + 0, 0>// markTimeout, spaceTimeout + (duration, data); + else + newInput = IRLdecode <NEC_LENGTH, (NEC_TIMEOUT + NEC_MARK_LEAD) / 2, // irLength, timeoutThreshold + (NEC_MARK_LEAD + NEC_SPACE_ONE) / 2, (NEC_SPACE_LEAD + NEC_SPACE_HOLDING) / 2, // markLeadThreshold, spaceLeadThreshold + (NEC_SPACE_HOLDING + NEC_SPACE_ONE) / 2, 0, // spaceLeadHoldingThreshold, markThreshold + (NEC_SPACE_ONE + NEC_SPACE_ZERO) / 2, // spaceThreshold + 0, 0>// markTimeout, spaceTimeout + (duration, data); + + if (newInput){ + // Check if the protcol's checksum is correct + bool holding; + if (IRLcheckInverse1(data) || (holding = IRLcheckHolding(data))){ + // normally NEC also check for the inverse of the address. + // newer remotes dont have this because of the wide used protocol all addresses were already used + // to make it less complicated its left out and the user can check the command inverse himself if needed + //if (!holding && !extended && !IRLcheckInverse0(data)) + // return; + + // save address + command and trigger event + uint16_t address = UINT16_AT_OFFSET(data, 0); + uint32_t command = UINT16_AT_OFFSET(data, 2); + IREvent(IR_NEC, address, command); + } + } +} + +template <bool extraAccuracy> void decodePanasonic(const uint16_t duration){ + // temporary buffer to hold bytes for decoding this protocol + static uint8_t data[PANASONIC_BLOCKS]; + + // pass the duration to the decoding function + bool newInput; + if (extraAccuracy) // no accuracy set at the moment, no conflict detected yet + newInput = IRLdecode <PANASONIC_LENGTH, (PANASONIC_TIMEOUT + PANASONIC_MARK_LEAD) / 2, // irLength, timeoutThreshold + (PANASONIC_MARK_LEAD + PANASONIC_SPACE_ONE) / 2, (PANASONIC_SPACE_LEAD + PANASONIC_SPACE_ONE) / 2, // markLeadThreshold, spaceLeadThreshold + 0, 0, // spaceLeadHoldingThreshold, markThreshold + (PANASONIC_SPACE_ONE + PANASONIC_SPACE_ZERO) / 2, // spaceThreshold + 0, 0>// markTimeout, spaceTimeout + (duration, data); + else + newInput = IRLdecode <PANASONIC_LENGTH, (PANASONIC_TIMEOUT + PANASONIC_MARK_LEAD) / 2, // irLength, timeoutThreshold + (PANASONIC_MARK_LEAD + PANASONIC_SPACE_ONE) / 2, (PANASONIC_SPACE_LEAD + PANASONIC_SPACE_ONE) / 2, // markLeadThreshold, spaceLeadThreshold + 0, 0, // spaceLeadHoldingThreshold, markThreshold + (PANASONIC_SPACE_ONE + PANASONIC_SPACE_ZERO) / 2, // spaceThreshold + 0, 0>// markTimeout, spaceTimeout + (duration, data); + + if (newInput){ + // Check if the protcol's checksum is correct + if (IRLcheckXOR0(data)){ + uint16_t address = UINT16_AT_OFFSET(data, 0); + uint32_t command = UINT32_AT_OFFSET(data, 2); IREvent(IR_PANASONIC, address, command); + return; + } + } +} + +template <bool extraAccuracy> void decodeSony12(const uint16_t duration){ + // temporary buffer to hold bytes for decoding this protocol + static uint8_t data[SONY_BLOCKS_12]; + + // pass the duration to the decoding function + bool newInput; + if (extraAccuracy) + newInput = IRLdecode <SONY_LENGTH_12, (SONY_TIMEOUT + SONY_MARK_LEAD) / 2, // irLength, timeoutThreshold + (SONY_MARK_LEAD + SONY_MARK_ONE) / 2, 0, // markLeadThreshold, spaceLeadThreshold + 0, (SONY_MARK_ONE + SONY_MARK_ZERO) / 2, // spaceLeadHoldingThreshold, markThreshold + 0, // spaceThreshold + (SONY_MARK_LEAD + SONY_MARK_ONE) / 2, SONY_MARK_ONE>// markTimeout, spaceTimeout + (duration, data); + else + newInput = IRLdecode <SONY_LENGTH_12, (SONY_TIMEOUT + SONY_MARK_LEAD) / 2, // irLength, timeoutThreshold + (SONY_MARK_LEAD + SONY_MARK_ONE) / 2, 0, // markLeadThreshold, spaceLeadThreshold + 0, (SONY_MARK_ONE + SONY_MARK_ZERO) / 2, // spaceLeadHoldingThreshold, markThreshold + 0, // spaceThreshold + 0, 0>// markTimeout, spaceTimeout + (duration, data); + + if (newInput){ + // protocol has no checksum + uint16_t address = UINT16_AT_OFFSET(data, 0) >> 7; + uint32_t command = data[0] & 0x7F; + IREvent(IR_SONY12, address, command); + return; + } +} + +void decodeRaw(const uint16_t duration){ + //TODO, use advanced raw example instead +} + +bool IRLcheckInverse0(uint8_t data[]){ + // check if byte 2 and is the inverse of byte 3 + if (uint8_t((data[0] ^ (~data[1]))) == 0) + return true; + else return false; +} + +bool IRLcheckInverse1(uint8_t data[]){ + // check if byte 0 and is the inverse of byte 1 + if (uint8_t((data[2] ^ (~data[3]))) == 0) + return true; + else return false; +} + +bool IRLcheckHolding(uint8_t data[]){ + // check if at least the command is always 1 + if (data[2] == 0xFF && data[3] == 0xFF) + return true; + else return false; +} + +bool IRLcheckXOR0(uint8_t data[]){ + // this function is used for panasonic checksum + if (uint8_t(data[2] ^ data[3] ^ data[4]) == data[5]) + return true; + else return false; +} + +// multifunctional template for receiving +template <uint8_t irLength, uint16_t timeoutThreshold, uint16_t markLeadThreshold, uint16_t spaceLeadThreshold, + uint16_t spaceLeadHoldingThreshold, uint16_t markThreshold, uint16_t spaceThreshold, + uint16_t markTimeout, uint16_t spaceTimeout> + bool IRLdecode(uint16_t duration, uint8_t data[]){ + + // variables for ir processing + static uint8_t count = 0; + + // if timeout always start next possible reading and abort any pending readings + if (duration >= timeoutThreshold) + count = 1; + + // on a reset (error in decoding) we are waiting for a timeout to start a new reading again + // this is to not conflict with other protocols while they are sending 0/1 which might be similar to a lead in another protocol + else if (count == 0) + return false; + + // check pulses for mark/space and lead + logical 0/1 seperate + else{ + // Mark pulses (odd numbers) + if (count % 2 == 1){ + // check Mark Lead (needs a timeout or a correct signal) + if (markLeadThreshold && count == 1){ + // lead is okay// wrong lead + if (duration <= markLeadThreshold){ + count = 0; + return false; + } + } + + else{ + // check for timeout if needed (might be a different protocol) + if (markTimeout && duration > markTimeout){ + count = 0; + return false; + } + + // only check values if the protocol has different logical space pulses + else if (markThreshold){ + + // get number of the Mark Bits (starting from zero) + uint8_t length; + // only save every 2nd value, substract the first two lead pulses + if (!spaceThreshold) + length = (count / 2) - 1; + // special case: spaces and marks both have data in the pulse + else length = count - 2; + + // move bits and write 1 or 0 depending on the duration + // 1.7: changed from MSB to LSB. somehow takes a bit more flash but is correct and easier to handle. + data[length / 8] >>= 1; + if (duration > markThreshold) + data[length / 8] |= 0x80; + //else // normally not needed through the bitshift + // data[length / 8] &= ~0x80; + } + + // check last input (always a mark) + if (count > irLength){ + count = 0; + return true; + } + } + } + + // Space pulses (even numbers) + else{ + //check Space Lead/Space Holding + if (spaceLeadThreshold && count == 2){ + // normal Space, next reading + if (duration > spaceLeadThreshold); + + // Button holding (if supported by protocol) + else if (spaceLeadHoldingThreshold && duration > spaceLeadHoldingThreshold){ + // set command to 0xFF if button is held down + if (irLength == (4 * 8 * 2 + 2)){ + data[0] = data[1] = 0x00; + data[2] = data[3] = 0xFF; + } + count = 0; + return true; + } + // wrong space + else { + count = 0; + return false; + } + } + else{ + // check for timeout if needed (might be a different protocol) + if (spaceTimeout && duration > spaceTimeout){ + count = 0; + return false; + } + + // only check values if the protocol has different logical space pulses + else if (spaceThreshold){ + + // get number of the Space Bits (starting from zero) + uint8_t length; + // only save every 2nd value, substract the first two lead pulses + if (!markThreshold) + length = (count / 2) - 2; + // special case: spaces and marks both have data in the pulse + else length = count - 2; + + // move bits and write 1 or 0 depending on the duration + // 1.7: changed from MSB to LSB. somehow takes a bit more flash but is correct and easier to handle. + data[length / 8] >>= 1; + if (duration > spaceThreshold) + data[length / 8] |= 0x80; + //else // normally not needed through the bitshift + // data[length / 8] &= ~0x80; + } + } + } + + // next reading, no errors + count++; + } + + // no valid input (yet) + return false; +} + +//================================================================================ +// Inline Implementations (send) +//================================================================================ + +template <IRType irType> +void IRLwrite(const uint8_t pin, uint16_t address, uint32_t command) +{ + // get the port mask and the pointers to the out/mode registers for faster access + uint8_t bitMask = digitalPinToBitMask(pin); + uint8_t port = digitalPinToPort(pin); + volatile uint8_t * outPort = portOutputRegister(port); + volatile uint8_t * modePort = portModeRegister(port); + + // set pin to OUTPUT and LOW + *modePort |= bitMask; + *outPort &= ~bitMask; + + // disable interrupts + uint8_t oldSREG = SREG; + cli(); + + switch (irType){ + + case IR_NEC: + // NEC only sends the data once + if (command == 0xFFF) + // send holding indicator + IRLsend<0, 0, NEC_HZ, 0, NEC_MARK_LEAD, NEC_SPACE_HOLDING, + 0, 0, 0, 0> + (outPort, bitMask, address, command); + else + // send data + IRLsend<NEC_ADDRESS_LENGTH, NEC_COMMAND_LENGTH, NEC_HZ, IR_ADDRESS_FIRST, NEC_MARK_LEAD, NEC_SPACE_LEAD, + NEC_MARK_ZERO, NEC_MARK_ONE, NEC_SPACE_ZERO, NEC_SPACE_ONE> + (outPort, bitMask, address, command); + break; + + case IR_PANASONIC: //TODO test + // send data + IRLsend<PANASONIC_ADDRESS_LENGTH, PANASONIC_COMMAND_LENGTH, PANASONIC_HZ, IR_ADDRESS_FIRST, PANASONIC_MARK_LEAD, PANASONIC_SPACE_LEAD, + PANASONIC_MARK_ZERO, PANASONIC_MARK_ONE, PANASONIC_SPACE_ZERO, PANASONIC_SPACE_ONE> + (outPort, bitMask, address, command); + break; + + case IR_SONY12: //TODO test, address -1? + // repeat 3 times + for (uint8_t i = 0; i < 3; i++) + // send data + IRLsend<SONY_ADDRESS_LENGTH_12, SONY_COMMAND_LENGTH_12, SONY_HZ, IR_COMMAND_FIRST, SONY_MARK_LEAD, SONY_SPACE_LEAD, + SONY_MARK_ZERO, SONY_MARK_ONE, SONY_SPACE_ZERO, SONY_SPACE_ONE> + (outPort, bitMask, address, command); + break; + } + + // enable interrupts + SREG = oldSREG; + + // set pin to INPUT again to be save + *modePort &= ~bitMask; +} + +// multifunctional template for sending +template <uint8_t addressLength, uint8_t commandLength, + uint16_t Hz, bool addressFirst, + uint16_t markLead, uint16_t spaceLead, + uint16_t markZero, uint16_t markOne, + uint16_t spaceZero, uint16_t spaceOne> + void IRLsend(volatile uint8_t * outPort, uint8_t bitmask, uint16_t address, uint32_t command){ + + // send lead mark + if (markLead) + IRLmark(Hz, outPort, bitmask, markLead); + + // send lead space + if (spaceLead) + IRLspace(outPort, bitmask, spaceLead); + + // abort if input values are both the same (sending a holding signal for example) + if (markOne != markZero || spaceOne != spaceZero) + { + // go through all bits + for (uint8_t i = 0; i < (addressLength + commandLength); i++){ + // determine if its a logical one or zero, starting with address or command + bool bitToSend; + if (addressFirst && i < addressLength || !addressFirst && i >= commandLength){ + bitToSend = address & 0x01; + address >>= 1; + } + else{ + bitToSend = command & 0x01; + command >>= 1; + } + + // send logic mark bits if needed + if (markOne != markZero){ + // modulate if spaces dont have logic, else only every even number + if (spaceOne == spaceZero || spaceOne != spaceZero && i % 2 == 0) + { + if (bitToSend) + IRLmark(Hz, outPort, bitmask, markOne); + else + IRLmark(Hz, outPort, bitmask, markZero); + } + } + else + IRLmark(Hz, outPort, bitmask, markOne); + + // send logic space bits if needed + if (spaceOne != spaceZero){ + // modulate if marks dont have logic, else only every odd number + if (markOne == markZero || markOne != markZero && i % 2 == 1){ + if (bitToSend) + IRLspace(outPort, bitmask, spaceOne); + else + IRLspace(outPort, bitmask, spaceZero); + } + } + else + IRLspace(outPort, bitmask, spaceOne); + } + } + + // finish mark + IRLmark(Hz, outPort, bitmask, markZero); //TODO zero or one for sony? + IRLspace(outPort, bitmask, 0); +} + +void IRLmark(const uint16_t Hz, volatile uint8_t * outPort, uint8_t bitMask, uint16_t time) { + /* + Bitbangs PWM in the given Hz number for the given time + ________________________________________________________________________________ + Delay calculation: + F_CPU/1.000.000 to get number of cycles/uS + /3 to get the number of loops needed for 1ms (1loop = 3 cycles) + + Multiply with the number of ms delay: + 1/kHz to get the seconds + * 1.000.000 to get it in uS + /2 to get half of a full pulse + + Substract the while, portmanipulation, loop overhead /3 loop cycles + + F_CPU(16.000.000) 1 * 1.000.000(pulse in uS) 12(overhead) + ========================== * ========================== - ============== + 1.000.000 * 3(loop cycles) Hz * 2(half of a pulse) 3(loop cycles) + + <==> + + F_CPU(16.000.000) - (12(overhead) * Hz * 2(half of a pulse)) + =========================================================== + Hz * 2(half of a on/off pulse) * 3(loop cycles) + + ________________________________________________________________________________ + Iterations calculation: + Devide time with cycles in while loop + Multiply this with the cycles per uS + cycles per while loop: 3(loop cycles) * delay + overhead + + time * (F_CPU(16.000.000) / 1.000.000) + ====================================== + delay*3(loop cycles) + overhead + */ + + const uint32_t loopCycles = 3; + const uint32_t overHead = 12; // just a guess from try + error + uint8_t delay = (F_CPU - (overHead * Hz * 2UL)) / (Hz * 2UL * loopCycles); + uint16_t iterations = (time*(F_CPU / 1000000UL)) / (delay * loopCycles + overHead); + + while (iterations--){ + // flip pin state and wait for the calculated time + *outPort ^= bitMask; +#ifdef IRLREMOTE_AVR + _delay_loop_1(delay); +#else + // ARM TODO? +#endif + } +} + +void IRLspace(volatile uint8_t * outPort, uint8_t bitMask, uint16_t time) { + // Sends an IRLspace for the specified number of microseconds. + // A IRLspace is no output, so the PWM output is disabled. + *outPort &= ~bitMask; // write pin LOW + delayMicroseconds(time); + //_delay_loop_2(time*(F_CPU/1000000UL)/4UL); +} + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/Makefile Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,11 @@ +.phony: verify upload + +ARDUINO=arduino-1.6.4 +ARDUINO_OPTS=--board arduino:avr:pro:cpu=8MHzatmega328 --port /dev/ttyUSB0 +SKETCH=start.ino + +verify: start.ino + $(ARDUINO) --verify $(ARDUINO_OPTS) $(SKETCH) + +upload: start.ino + $(ARDUINO) --upload $(ARDUINO_OPTS) $(SKETCH)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/battery.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,8 @@ +#ifndef BATTERY_H +#define BATTERY_H + +void battery_setup(void); +int get_battery(void); +void battery_loop(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/battery.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,31 @@ +#include "beep.h" + +#define BATTERY_PIN A3 +// #define MIN_BATTERY 557 // About 3.59V. +#define MIN_BATTERY 367 // About 3.59V. + +extern int robot_mode; + +void battery_setup() { + pinMode(BATTERY_PIN, INPUT); +} + +int get_battery() { + return analogRead(BATTERY_PIN); +} + +void battery_loop() { + static int battery_fail = 0; + int reading = get_battery(); + + if (reading < MIN_BATTERY) { + battery_fail += 1; + beep(reading, 200); + } else { + battery_fail = 0; + } + if (battery_fail > 40) { + /* If it stays bad for 4s, shut down. */ + robot_mode = 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/beep.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,9 @@ +#ifndef BEEP_H +#define BEEP_H + +extern bool beep_mute; + +void beep_setup(void); +void beep(int frequency, int duration); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/beep.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,20 @@ +#define BEEP_PIN 13 + +bool beep_mute = false; + + +void beep_setup() { + pinMode(BEEP_PIN, OUTPUT); + beep(1760, 50); + delay(100); + beep(1975, 50); + delay(50); + beep(1760, 50); +} + +void beep(int frequency, int duration) { + if (beep_mute) { + return; + } + tone(BEEP_PIN, frequency, duration); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/clock.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,9 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#define FPS 25 + +const unsigned char TICK = 1000 / FPS; +void tick(unsigned int ms); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/clock.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,14 @@ +#include "clock.h" + +void tick(unsigned int ms) { + // Wait remaining time to delay ms milliseconds since last call. + static unsigned long last = 0; + unsigned long now = millis(); + long wait = last + ms - now; + if (wait > 0) { + delay(wait); + } else { + // Somehow engage a time machine here to go back in time. + } + last = now; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/creep.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,14 @@ +#ifndef CREEP_H +#define CREEP_H + +/* Walking speed. */ +extern double creep_dx; // Sideways. +extern double creep_dy; // Forward. +extern double creep_spread; +extern double creep_rotation; +extern double creep_height; + +/* Perform one step of the creep gait with the current speed. */ +void walk(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/creep.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,206 @@ +#include "creep.h" +#include "misc.h" +#include "leg.h" +#include "clock.h" + + +/* Creep gait parameters. */ +#ifdef TOTE2 +#define SPEED 1 +/* How far forward to move them when making a step? */ +#define STRIDE 14.0 +/* How much to shift the body? */ +#define SHIFT 8.0 +/* In how many steps should the body be shifted? */ +#define SHIFT_STEPS 2 +/* How fast to raise the leg. */ +#define RAISE 10.0 +/* In how many steps. */ +#define RAISE_STEPS 3 +#else +#define SPEED 0.5 +/* How far forward to move them when making a step? */ +#define STRIDE 12.0 +/* How much to shift the body? */ +#define SHIFT 8.0 +/* In how many steps should the body be shifted? */ +#define SHIFT_STEPS 4 +/* How fast to raise the leg. */ +#define RAISE 10.0 +/* In how many steps. */ +#define RAISE_STEPS 3 +#endif + +/* Given current leg, which leg to move next? */ +const unsigned char _NEXT_LEG[6][4] = { + {2, 0, 3, 1}, // forward + {1, 3, 0, 2}, // backward + {1, 2, 3, 0}, // clockwise + {3, 0, 1, 2}, // counter-clockwise + {2, 3, 1, 0}, // left + {3, 2, 0, 1}, // right +}; + +/* Body position. */ +static double _shift_x = 0.0; +static double _shift_y = 0.0; + +/* Walking speed. */ +double creep_dx = 0.0; // Sideways. +double creep_dy = 1.0; // Forward. +double creep_rotation = 0.0; // Rotation. +double creep_height = TIBIA * 0.75; // Body height. +double creep_spread = 0.0; // Leg spread. + +/* Which legs are touching the ground? */ +static bool _on_ground[4] = {true, true, true, true}; + +/* +Move all legs that are on the ground backwards, pushing the robot forward with +the current walking speed. This is called every tick, to make the robot move +continuously. +*/ +void _creep_move() { + for (unsigned char leg=0; leg < 4; ++leg) { + if (_on_ground[leg]) { + move_leg( + leg, + leg_position[leg][0] - creep_dx * LEG_X[leg] * SPEED, + leg_position[leg][1] - creep_dy * LEG_Y[leg] * SPEED, + -creep_height + ); + rotate_leg_by(leg, -creep_rotation * LEG_X[leg] * LEG_Y[leg] / 2); + } + } +} + +/* Called every frame. */ +void _creep_tick() { + _creep_move(); + tick(TICK); +} + +/* Shifts the whole body by defined amount. */ +void _shift_body_by(double dx, double dy) { + for (unsigned char leg=0; leg < 4; ++leg) { + move_leg_by(leg, dx * LEG_X[leg], dy * LEG_Y[leg], 0); + } + _shift_x += dx; + _shift_y += dy; +} + +/* Shifts the body away from the given leg, for balance. */ +void _shift_body(unsigned char leg) { + double dx = (LEG_X[leg] * SHIFT - _shift_x) / SHIFT_STEPS; + double dy = (LEG_Y[leg] * SHIFT - _shift_y) / SHIFT_STEPS; + for (unsigned char i = 0; i < SHIFT_STEPS; ++i) { + _shift_body_by(dx, dy); + _creep_tick(); + } +} + +/* Perform a full step with a single leg. */ +void _creep_step(unsigned char leg) { + _shift_body(leg); + _on_ground[leg] = false; + for (unsigned char step = 0; step < RAISE_STEPS; ++step) { + move_leg_by(leg, 0, 0, RAISE); + _creep_tick(); + } + move_leg( + leg, + HOME + (_shift_x + creep_dx * STRIDE) * LEG_X[leg] + creep_spread, + HOME + (_shift_y + creep_dy * STRIDE) * LEG_Y[leg] + creep_spread, + RAISE * RAISE_STEPS - creep_height + ); + _creep_tick(); + _creep_tick(); + for (unsigned char step = 0; step < RAISE_STEPS - 1; ++step) { + move_leg_by(leg, 0, 0, -RAISE); + _creep_tick(); + } + _on_ground[leg] = true; + _creep_tick(); +} + + +/* Called every frame. */ +void _trot_tick() { + _creep_move(); + _creep_move(); + tick(TICK); +} + +/* Perform a full trot step. */ +void _trot_step(unsigned char leg) { + unsigned char other_leg = (leg + 2) % 4; + _on_ground[leg] = false; + _on_ground[other_leg] = false; + move_leg( + leg, + HOME + creep_dx * STRIDE * LEG_X[leg] + creep_spread, + HOME + creep_dy * STRIDE * LEG_Y[leg] + creep_spread, + RAISE * RAISE_STEPS - creep_height + ); + move_leg( + other_leg, + HOME + creep_dx * STRIDE * LEG_X[other_leg] + creep_spread, + HOME + creep_dy * STRIDE * LEG_Y[other_leg] + creep_spread, + RAISE * RAISE_STEPS - creep_height + ); + _trot_tick(); + _trot_tick(); + _trot_tick(); + _trot_tick(); + _on_ground[leg] = true; + _on_ground[other_leg] = true; + _trot_tick(); + _trot_tick(); + _trot_tick(); + _trot_tick(); +} + + +unsigned char step_order() { + // Decide on the best leg order. + if (abs(creep_dy) > abs(creep_dx)) { + // forward/backward + if (creep_dy > 0) { + return 0; // forward + } else { + return 1; // backward + } + } else if (abs(creep_dx) > abs(creep_dy)) { + // sideways + if (creep_dx < 0) { + return 4; // left + } else { + return 5; // right + } + } else if (abs(creep_rotation) > 0.001) { + // rotate + if (creep_rotation > 0) { + return 2; // clockwise + } else { + return 3; // counter-clockwise + } + } + return 0; // default to forward +} + +/* Perform one step of a walk, selected depending on the speed. */ +void walk() { + static unsigned char leg = 0; + double max_speed = max(abs(creep_rotation), + max(abs(creep_dx), abs(creep_dy))); + + if (max_speed > 1.5) { + _trot_step(leg); + leg = (leg + 1) % 2; + } else if (max_speed > 0.01) { + _creep_step(leg); + leg = _NEXT_LEG[step_order()][leg]; + } else { + _creep_tick(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/ir.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,7 @@ +#ifndef IR_H +#define IR_H + +void ir_setup(); +void ir_loop(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/ir.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,116 @@ +#include "creep.h" +#include "leg.h" +#include "servos.h" +#include "misc.h" +#include "beep.h" +#include "IRLremote.h" + + +#define IR_INTERRUPT 0 // Sensor on PIN 2 +#define IR_PROTOCOL IR_NEC +#define IR_ADDRESS 0xFF00 + +#define KEY_UP 0xB946 // Up arrow. +#define KEY_DOWN 0xEA15 // Down arrow. +#define KEY_LEFT 0xBB44 // Left arrow. +#define KEY_RIGHT 0xBC43 // Right arrow. +#define KEY_OK 0xBF40 // OK. + +#define KEY_0 0xF30C // Rewind. +#define KEY_1 0xE718 // Play/pause. +#define KEY_2 0xA15E // Fast forward. +#define KEY_3 0xF708 // Channel minus. +#define KEY_4 0xA55A // Channel plus. +#define KEY_5 0xE31C +#define KEY_6 0xBD42 // Volume down. +#define KEY_7 0xAD52 // Mute. +#define KEY_8 0xB54A // Volume up. +#define KEY_9 0xBA45 // Power. +#define KEY_AST 0xAF50 // Asterisk. +#define KEY_HASH 0xBB44 // Hash. + + +extern int robot_mode; + + +void ir_setup() { + IRLbegin<IR_ALL>(IR_INTERRUPT); + pinMode(3, OUTPUT); +} + +void ir_loop() { + static uint32_t last=0; + uint32_t IRcommand = 0; + + if (!IRLavailable()) { + return; + } + + IRcommand = IRLgetCommand(); + + if (IRcommand == 0xFFFF) { + IRcommand = last; + } else if (IRLgetAddress() != IR_ADDRESS) { + IRLreset(); + return; // React only to my remote. + } + last = IRcommand; + + beep(1319, 50); + + switch (IRcommand) { + case KEY_UP: // Up arrow. + creep_dy = min(3, creep_dy + 0.25); + break; + case KEY_DOWN: // Down arrow. + creep_dy = max(-3, creep_dy - 0.25); + break; + case KEY_LEFT: // Left arrow. + creep_rotation = min(PI/90, creep_rotation + PI/360); + break; + case KEY_RIGHT: // Right arrow. + creep_rotation = max(-PI/90, creep_rotation - PI/360); + break; + case KEY_1: // Spread -. + creep_spread = max(-10.0, creep_spread - 1); + break; + case KEY_2: // Spread default. + creep_spread = 0.0; + break; + case KEY_3: // Spread +. + creep_spread = min(10.0, creep_spread + 1); + break; + case KEY_4: // Height -. + creep_height = max(TIBIA * 0.25, creep_height - 1); + break; + case KEY_6: // Height +. + creep_height = min(TIBIA * 1.25, creep_height + 1); + break; + case KEY_5: // Height default. + creep_height = TIBIA * 0.80; + break; + case KEY_7: // Strafe left. + creep_dx = max(-1.0, creep_dx - 0.25); + break; + case KEY_0: // Mute. + beep_mute = !beep_mute; + break; + case KEY_9: // Strafe right. + creep_dx = min(1.0, creep_dx + 0.25); + break; + case KEY_AST: // Power. + robot_mode = 0; + break; + case KEY_HASH: // Home. + for (unsigned char leg=0; leg < 4; ++leg) { + move_leg(leg, HOME, HOME, TIBIA); + } + break; + case KEY_OK: // OK. + creep_dx = 0.0; + creep_dy = 0.0; + creep_rotation = 0.0; + break; + } + IRLreset(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/leg.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,27 @@ +#ifndef LEG_H +#define LEG_H + +#ifdef TOTE2 +// For version 2: +#define COXA 12.0 +#define FEMUR 42.0 +#define TIBIA 38.5 +#define BASE 21.0 +#else +// For version 1: +#define COXA 8.0 +#define FEMUR 20.5 +#define TIBIA 28.5 +#define BASE 18.5 +#endif + +const signed char LEG_X[4] = {-1, -1, 1, 1}; +const signed char LEG_Y[4] = {1, -1, -1, 1}; +extern double leg_position[4][3]; +const double HOME = (COXA + FEMUR) / SQRT2; + +bool move_leg(unsigned char leg, double x, double y, double z); +bool move_leg_by(unsigned char leg, double dx, double dy, double dz); +bool rotate_leg_by(unsigned char leg, double angle); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/leg.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,84 @@ +#include "misc.h" +#include "leg.h" + +const unsigned char LEG_JOINT[4][3] = { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}, + {9, 10, 11}, +}; + +double leg_position[4][3] = { + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, + {HOME, LEG_Y[0] * HOME, -TIBIA}, +}; + + +double _solve_triangle(double a, double b, double c) { + // Calculate the angle between a and b, opposite to c. + a = abs(a); + b = abs(b); + c = abs(c); + c = min(c, a + b); + b = min(b, a + c); + a = min(a, b + c); + return acos((a * a + b * b - c * c) / (2 * a * b)); +} + +double _norm(double a, double b) { + // Calculate the norm of a vector. + return sqrt(a * a + b * b); +} + +bool _inverse_kinematics(double x, double y, double z, + double *ankle, double *knee, double *hip) { + // Calculate angles for knee and ankle, and put them in those variables. + // Return true on success, and false if x and y are out of range. + double f = _norm(x, y) - COXA; + double d = _norm(f, z); + d = min(d, FEMUR + TIBIA); + *hip = atan2(y, x); + if (isnan(*hip)) { return false; } + *knee = _solve_triangle(FEMUR, d, TIBIA) - atan2(-z, f); + if (isnan(*knee)) { return false; } + *ankle = _solve_triangle(FEMUR, TIBIA, d) - PI2; + if (isnan(*ankle)) { return false; } + return true; +} + +bool move_leg(unsigned char leg, double x, double y, double z) { + // Move the tip of the leg to x, y. Return false when out of range. + double ankle = NAN; + double knee = NAN; + double hip = NAN; + leg_position[leg][0] = x; + leg_position[leg][1] = y; + leg_position[leg][2] = z; + if (!_inverse_kinematics(x, y, z, &ankle, &knee, &hip)) { + return false; + } + hip -= PI4; + servo_move(LEG_JOINT[leg][0], ankle); + servo_move(LEG_JOINT[leg][1], knee); + servo_move(LEG_JOINT[leg][2], hip); + return true; +} + +bool move_leg_by(unsigned char leg, double dx, double dy, double dz) { + // Move the tip of the leg by dx, dy. Return false when out of range. + return move_leg(leg, + leg_position[leg][0] + dx, + leg_position[leg][1] + dy, + leg_position[leg][2] + dz); +} + +bool rotate_leg_by(unsigned char leg, double angle) { + // Rotate the tip of the leg around the center of robot's body. + double x = leg_position[leg][0] + BASE; + double y = leg_position[leg][1] + BASE; + double nx = x * cos(angle) - y * sin(angle) - BASE; + double ny = x * sin(angle) + y * cos(angle) - BASE; + return move_leg(leg, nx, ny, leg_position[leg][2]); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/misc.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,12 @@ +#ifndef MISC_H +#define MISC_H + +extern const double PI2; +extern const double PI4; +extern const double TAU; +extern const double SQRT2; + +// Use the larger SG90 servos +#define TOTE2 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/misc.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,6 @@ +#include "misc.h" + +const double PI2 = PI / 2; +const double PI4 = PI / 4; +const double TAU = PI * 2; +const double SQRT2 = 1.4142135623730951;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/servos.h Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,11 @@ +#ifndef SERVOS_H +#define SERVOS_H + +// Number of servos. +#define SERVOS 12 + +void servo_move(unsigned char servo, double rads); +void servo_setup(void); +void servo_shutdown(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/servos.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,102 @@ +#include "misc.h" +#include "servos.h" +#include <Servo.h> + +// Range of the servos. +#define SERVO_FREQ_MIN 600 +#define SERVO_FREQ_MAX 2400 + +static const unsigned char SERVO_PINS[SERVOS] = { +// ankle, knee, hip + 7, 8, 9 // front left + 6, 5, 4, // hind left + 3, 1, 0, // hind right + 12, 11, 10, // front right +}; +static const signed char SERVO_REVERSE[SERVOS] = { +// ankle, knee, hip + 1, -1, -1 // front left + -1, 1, 1, // hind left + 1, -1, -1, // hind right + -1, 1, 1, // front right +}; +static const signed char SERVO_TRIM[SERVOS] = { +// ankle, knee, hip + 0, 0, 10 // front left + 0, 0, 10, // hind left + 10, -5, -10, // hind right + 0, 5, -10, // front right +}; +static Servo servos[SERVOS]; + +static const double HOME_POSITION[SERVOS] = { +// ankle, knee, hip + 0, 0, 0 // front right + 0, 0, 0, // hind right + 0, 0, 0, // hind left + 0, 0, 0, // front left +}; + +static const double FOLD_POSITION[SERVOS] = { +// ankle, knee, hip + -PI2, PI2, -PI4 // front right + -PI2, PI2, -PI4, // hind right + -PI2, PI2, -PI4, // hind left + -PI2, PI2, -PI4, // front left +}; + +void servo_move(unsigned char servo, double rads) { + // Move a servo to a position in radians between -PI/2 and PI/2. + if (servo >= SERVOS) { + return; + } + rads = rads * SERVO_REVERSE[servo] + PI2; + while (rads > TAU) { + rads -= TAU; + } + while (rads < 0) { + rads += TAU; + } + int freq = (SERVO_FREQ_MAX - + SERVO_FREQ_MIN) * rads / PI + SERVO_FREQ_MIN; + freq = min(SERVO_FREQ_MAX, + max(SERVO_FREQ_MIN, + freq + SERVO_TRIM[servo] * 10)); + servos[servo].writeMicroseconds(freq); +} + +void servo_setup() { + // Initialize all servos. + // Start with hips. + for (unsigned char servo = 2; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } + // Then ankles. + for (unsigned char servo = 0; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } + // Finally knees. + for (unsigned char servo = 1; servo < SERVOS; servo += 3) { + servos[servo].attach(SERVO_PINS[servo], + SERVO_FREQ_MIN, SERVO_FREQ_MAX); + servo_move(servo, HOME_POSITION[servo]); + delay(150); + } +} + +void servo_shutdown() { + // Power down all servos. + for (unsigned char servo = 0; servo < SERVOS; ++servo) { + servo_move(servo, FOLD_POSITION[servo]); + } + delay(500); + for (unsigned char servo = 0; servo < SERVOS; ++servo) { + servos[servo].detach(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/v5/v5.ino Wed Dec 07 21:10:22 2016 +0100 @@ -0,0 +1,43 @@ +#include "misc.h" +#include "servos.h" +#include "beep.h" +#include "battery.h" +#include "creep.h" +#include "leg.h" + +#include <avr/sleep.h> + +// 0 -- Power off +// 1 -- Walk +// 2 -- Nothing +int robot_mode = 1; + + +void setup() { + // battery_setup(); + servo_setup(); + beep_setup(); + ir_setup(); +} + +void loop() { + switch (robot_mode) { + case 0: // Power off + servo_shutdown(); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + sleep_mode(); + while (1) { + delay(100); + } + break; + case 1: + walk(); + break; + case 2: // Nothing + delay(100); + break; + } + ir_loop(); + // battery_loop(); +}
