From cd76c5c04ded9407d9b1f3351312d28dee2a608b Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Thu, 23 Jun 2022 00:25:16 +0800 Subject: [PATCH] 230622 addresses updated --- ASV-2/POKB/main code/POKB_Main/XBee.h | 1345 +++++++++++++++++ ASV-2/POKB/main code/POKB_Main/can.h | 119 ++ .../main code/POKB_Main/can_asv_defines.h | 64 + ASV-3/POKB/POKB.ino | 236 +++ ASV-3/POKB/defines.h | 33 + .../main_code/{ => Telemetry}/Telemetry.ino | 0 .../Sensor and Telemetry/stb/stb.ino | 658 ++++++++ AUV-4/STB/main code/STB_4.0/MS5837.cpp | 258 ++++ AUV-4/STB/main code/STB_4.0/MS5837.h | 103 ++ 9 files changed, 2816 insertions(+) create mode 100644 ASV-2/POKB/main code/POKB_Main/XBee.h create mode 100644 ASV-2/POKB/main code/POKB_Main/can.h create mode 100644 ASV-2/POKB/main code/POKB_Main/can_asv_defines.h create mode 100644 ASV-3/POKB/POKB.ino create mode 100644 ASV-3/POKB/defines.h rename ASV-3/Telemetry/main_code/{ => Telemetry}/Telemetry.ino (100%) create mode 100644 AUV-3.99/Sensor and Telemetry/main code/Sensor and Telemetry/stb/stb.ino create mode 100644 AUV-4/STB/main code/STB_4.0/MS5837.cpp create mode 100644 AUV-4/STB/main code/STB_4.0/MS5837.h diff --git a/ASV-2/POKB/main code/POKB_Main/XBee.h b/ASV-2/POKB/main code/POKB_Main/XBee.h new file mode 100644 index 00000000..c37fd0c4 --- /dev/null +++ b/ASV-2/POKB/main code/POKB_Main/XBee.h @@ -0,0 +1,1345 @@ +/** + * Copyright (c) 2009 Andrew Rapp. All rights reserved. + * + * This file is part of XBee-Arduino. + * + * XBee-Arduino 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. + * + * XBee-Arduino 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 XBee-Arduino. If not, see . + */ + +#ifndef XBee_h +#define XBee_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include + +#define SERIES_1 +#define SERIES_2 + +// set to ATAP value of XBee. AP=2 is recommended +#define ATAP 2 + +#define START_BYTE 0x7e +#define ESCAPE 0x7d +#define XON 0x11 +#define XOFF 0x13 + +// This value determines the size of the byte array for receiving RX packets +// Most users won't be dealing with packets this large so you can adjust this +// value to reduce memory consumption. But, remember that +// if a RX packet exceeds this size, it cannot be parsed! + +// This value is determined by the largest packet size (100 byte payload + 64-bit address + option byte and rssi byte) of a series 1 radio +#define MAX_FRAME_DATA_SIZE 110 + +#define BROADCAST_ADDRESS 0xffff +#define ZB_BROADCAST_ADDRESS 0xfffe + +// the non-variable length of the frame data (not including frame id or api id or variable data size (e.g. payload, at command set value) +#define ZB_TX_API_LENGTH 12 +#define ZB_EXPLICIT_TX_API_LENGTH 18 +#define TX_16_API_LENGTH 3 +#define TX_64_API_LENGTH 9 +#define AT_COMMAND_API_LENGTH 2 +#define REMOTE_AT_COMMAND_API_LENGTH 13 +// start/length(2)/api/frameid/checksum bytes +#define PACKET_OVERHEAD_LENGTH 6 +// api is always the third byte in packet +#define API_ID_INDEX 3 + +// frame position of rssi byte +#define RX_16_RSSI_OFFSET 2 +#define RX_64_RSSI_OFFSET 8 + +#define DEFAULT_FRAME_ID 1 +#define NO_RESPONSE_FRAME_ID 0 + +// These are the parameters used by the XBee ZB modules when you do a +// regular "ZB TX request". +#define DEFAULT_ENDPOINT 232 +#define DEFAULT_CLUSTER_ID 0x0011 +#define DEFAULT_PROFILE_ID 0xc105 + +// TODO put in tx16 class +#define ACK_OPTION 0 +#define DISABLE_ACK_OPTION 1 +#define BROADCAST_OPTION 4 + +// RX options +#define ZB_PACKET_ACKNOWLEDGED 0x01 +#define ZB_BROADCAST_PACKET 0x02 + +// not everything is implemented! +/** + * Api Id constants + */ +#define TX_64_REQUEST 0x0 +#define TX_16_REQUEST 0x1 +#define AT_COMMAND_REQUEST 0x08 +#define AT_COMMAND_QUEUE_REQUEST 0x09 +#define REMOTE_AT_REQUEST 0x17 +#define ZB_TX_REQUEST 0x10 +#define ZB_EXPLICIT_TX_REQUEST 0x11 +#define RX_64_RESPONSE 0x80 +#define RX_16_RESPONSE 0x81 +#define RX_64_IO_RESPONSE 0x82 +#define RX_16_IO_RESPONSE 0x83 +#define AT_RESPONSE 0x88 +#define TX_STATUS_RESPONSE 0x89 +#define MODEM_STATUS_RESPONSE 0x8a +#define ZB_RX_RESPONSE 0x90 +#define ZB_EXPLICIT_RX_RESPONSE 0x91 +#define ZB_TX_STATUS_RESPONSE 0x8b +#define ZB_IO_SAMPLE_RESPONSE 0x92 +#define ZB_IO_NODE_IDENTIFIER_RESPONSE 0x95 +#define AT_COMMAND_RESPONSE 0x88 +#define REMOTE_AT_COMMAND_RESPONSE 0x97 + + +/** + * TX STATUS constants + */ +#define SUCCESS 0x0 +#define CCA_FAILURE 0x2 +#define INVALID_DESTINATION_ENDPOINT_SUCCESS 0x15 +#define NETWORK_ACK_FAILURE 0x21 +#define NOT_JOINED_TO_NETWORK 0x22 +#define SELF_ADDRESSED 0x23 +#define ADDRESS_NOT_FOUND 0x24 +#define ROUTE_NOT_FOUND 0x25 +#define PAYLOAD_TOO_LARGE 0x74 +// Returned by XBeeWithCallbacks::waitForStatus on timeout +#define XBEE_WAIT_TIMEOUT 0xff + +// modem status +#define HARDWARE_RESET 0 +#define WATCHDOG_TIMER_RESET 1 +#define ASSOCIATED 2 +#define DISASSOCIATED 3 +#define SYNCHRONIZATION_LOST 4 +#define COORDINATOR_REALIGNMENT 5 +#define COORDINATOR_STARTED 6 + +#define ZB_BROADCAST_RADIUS_MAX_HOPS 0 + +#define ZB_TX_UNICAST 0 +#define ZB_TX_BROADCAST 8 + +#define AT_OK 0 +#define AT_ERROR 1 +#define AT_INVALID_COMMAND 2 +#define AT_INVALID_PARAMETER 3 +#define AT_NO_RESPONSE 4 + +#define NO_ERROR 0 +#define CHECKSUM_FAILURE 1 +#define PACKET_EXCEEDS_BYTE_ARRAY_LENGTH 2 +#define UNEXPECTED_START_BYTE 3 + +/** + * C++11 introduced the constexpr as a hint to the compiler that things + * can be evaluated at compiletime. This can help to remove + * startup code for global objects, or otherwise help the compiler to + * optimize. Since the keyword is introduced in C++11, but supporting + * older compilers is a matter of removing the keyword, we use a macro + * for this. + */ +#if __cplusplus >= 201103L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +/** + * The super class of all XBee responses (RX packets) + * Users should never attempt to create an instance of this class; instead + * create an instance of a subclass + * It is recommend to reuse subclasses to conserve memory + */ +class XBeeResponse { +public: + //static const int MODEM_STATUS = 0x8a; + /** + * Default constructor + */ + XBeeResponse(); + /** + * Returns Api Id of the response + */ + uint8_t getApiId(); + void setApiId(uint8_t apiId); + /** + * Returns the MSB length of the packet + */ + uint8_t getMsbLength(); + void setMsbLength(uint8_t msbLength); + /** + * Returns the LSB length of the packet + */ + uint8_t getLsbLength(); + void setLsbLength(uint8_t lsbLength); + /** + * Returns the packet checksum + */ + uint8_t getChecksum(); + void setChecksum(uint8_t checksum); + /** + * Returns the length of the frame data: all bytes after the api id, and prior to the checksum + * Note up to release 0.1.2, this was incorrectly including the checksum in the length. + */ + uint8_t getFrameDataLength(); + void setFrameData(uint8_t* frameDataPtr); + /** + * Returns the buffer that contains the response. + * Starts with byte that follows API ID and includes all bytes prior to the checksum + * Length is specified by getFrameDataLength() + * Note: Unlike Digi's definition of the frame data, this does not start with the API ID.. + * The reason for this is all responses include an API ID, whereas my frame data + * includes only the API specific data. + */ + uint8_t* getFrameData(); + + void setFrameLength(uint8_t frameLength); + // to support future 65535 byte packets I guess + /** + * Returns the length of the packet + */ + uint16_t getPacketLength(); + /** + * Resets the response to default values + */ + void reset(); + /** + * Initializes the response + */ + void init(); +#ifdef SERIES_2 + /** + * Call with instance of ZBTxStatusResponse class only if getApiId() == ZB_TX_STATUS_RESPONSE + * to populate response + */ + void getZBTxStatusResponse(XBeeResponse &response); + /** + * Call with instance of ZBRxResponse class only if getApiId() == ZB_RX_RESPONSE + * to populate response + */ + void getZBRxResponse(XBeeResponse &response); + /** + * Call with instance of ZBExplicitRxResponse class only if getApiId() == ZB_EXPLICIT_RX_RESPONSE + * to populate response + */ + void getZBExplicitRxResponse(XBeeResponse &response); + /** + * Call with instance of ZBRxIoSampleResponse class only if getApiId() == ZB_IO_SAMPLE_RESPONSE + * to populate response + */ + void getZBRxIoSampleResponse(XBeeResponse &response); +#endif +#ifdef SERIES_1 + /** + * Call with instance of TxStatusResponse only if getApiId() == TX_STATUS_RESPONSE + */ + void getTxStatusResponse(XBeeResponse &response); + /** + * Call with instance of Rx16Response only if getApiId() == RX_16_RESPONSE + */ + void getRx16Response(XBeeResponse &response); + /** + * Call with instance of Rx64Response only if getApiId() == RX_64_RESPONSE + */ + void getRx64Response(XBeeResponse &response); + /** + * Call with instance of Rx16IoSampleResponse only if getApiId() == RX_16_IO_RESPONSE + */ + void getRx16IoSampleResponse(XBeeResponse &response); + /** + * Call with instance of Rx64IoSampleResponse only if getApiId() == RX_64_IO_RESPONSE + */ + void getRx64IoSampleResponse(XBeeResponse &response); +#endif + /** + * Call with instance of AtCommandResponse only if getApiId() == AT_COMMAND_RESPONSE + */ + void getAtCommandResponse(XBeeResponse &responses); + /** + * Call with instance of RemoteAtCommandResponse only if getApiId() == REMOTE_AT_COMMAND_RESPONSE + */ + void getRemoteAtCommandResponse(XBeeResponse &response); + /** + * Call with instance of ModemStatusResponse only if getApiId() == MODEM_STATUS_RESPONSE + */ + void getModemStatusResponse(XBeeResponse &response); + /** + * Returns true if the response has been successfully parsed and is complete and ready for use + */ + bool isAvailable(); + void setAvailable(bool complete); + /** + * Returns true if the response contains errors + */ + bool isError(); + /** + * Returns an error code, or zero, if successful. + * Error codes include: CHECKSUM_FAILURE, PACKET_EXCEEDS_BYTE_ARRAY_LENGTH, UNEXPECTED_START_BYTE + */ + uint8_t getErrorCode(); + void setErrorCode(uint8_t errorCode); +protected: + // pointer to frameData + uint8_t* _frameDataPtr; +private: + void setCommon(XBeeResponse &target); + uint8_t _apiId; + uint8_t _msbLength; + uint8_t _lsbLength; + uint8_t _checksum; + uint8_t _frameLength; + bool _complete; + uint8_t _errorCode; +}; + +class XBeeAddress { +public: + CONSTEXPR XBeeAddress() {}; +}; + +/** + * Represents a 64-bit XBee Address + * + * Note that avr-gcc as of 4.9 doesn't optimize uint64_t very well, so + * for the smallest and fastest code, use msb and lsb separately. See + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66511 + */ +class XBeeAddress64 : public XBeeAddress { +public: + CONSTEXPR XBeeAddress64(uint64_t addr) : _msb(addr >> 32), _lsb(addr) {} + CONSTEXPR XBeeAddress64(uint32_t msb, uint32_t lsb) : _msb(msb), _lsb(lsb) {} + CONSTEXPR XBeeAddress64() : _msb(0), _lsb(0) {} + uint32_t getMsb() {return _msb;} + uint32_t getLsb() {return _lsb;} + uint64_t get() {return (static_cast(_msb) << 32) | _lsb;} + operator uint64_t() {return get();} + void setMsb(uint32_t msb) {_msb = msb;} + void setLsb(uint32_t lsb) {_lsb = lsb;} + void set(uint64_t addr) { + _msb = addr >> 32; + _lsb = addr; + } +private: + // Once https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66511 is + // fixed, it might make sense to merge these into a uint64_t. + uint32_t _msb; + uint32_t _lsb; +}; + +//class XBeeAddress16 : public XBeeAddress { +//public: +// XBeeAddress16(uint16_t addr); +// XBeeAddress16(); +// uint16_t getAddress(); +// void setAddress(uint16_t addr); +//private: +// uint16_t _addr; +//}; + +/** + * This class is extended by all Responses that include a frame id + */ +class FrameIdResponse : public XBeeResponse { +public: + FrameIdResponse(); + uint8_t getFrameId(); +private: + uint8_t _frameId; +}; + +/** + * Common functionality for both Series 1 and 2 data RX data packets + */ +class RxDataResponse : public XBeeResponse { +public: + RxDataResponse(); + /** + * Returns the specified index of the payload. The index may be 0 to getDataLength() - 1 + * This method is deprecated; use uint8_t* getData() + */ + uint8_t getData(int index); + /** + * Returns the payload array. This may be accessed from index 0 to getDataLength() - 1 + */ + uint8_t* getData(); + /** + * Returns the length of the payload + */ + virtual uint8_t getDataLength() = 0; + /** + * Returns the position in the frame data where the data begins + */ + virtual uint8_t getDataOffset() = 0; +}; + +// getResponse to return the proper subclass: +// we maintain a pointer to each type of response, when a response is parsed, it is allocated only if NULL +// can we allocate an object in a function? + +#ifdef SERIES_2 +/** + * Represents a Series 2 TX status packet + */ +class ZBTxStatusResponse : public FrameIdResponse { + public: + ZBTxStatusResponse(); + uint16_t getRemoteAddress(); + uint8_t getTxRetryCount(); + uint8_t getDeliveryStatus(); + uint8_t getDiscoveryStatus(); + bool isSuccess(); + + static const uint8_t API_ID = ZB_TX_STATUS_RESPONSE; +}; + +/** + * Represents a Series 2 RX packet + */ +class ZBRxResponse : public RxDataResponse { +public: + ZBRxResponse(); + XBeeAddress64& getRemoteAddress64(); + uint16_t getRemoteAddress16(); + uint8_t getOption(); + uint8_t getDataLength(); + // frame position where data starts + uint8_t getDataOffset(); + + static const uint8_t API_ID = ZB_RX_RESPONSE; +private: + XBeeAddress64 _remoteAddress64; +}; + +/** + * Represents a Series 2 Explicit RX packet + * + * Note: The receive these responses, set AO=1. With the default AO=0, + * you will receive ZBRxResponses, not knowing exact details. + */ +class ZBExplicitRxResponse : public ZBRxResponse { +public: + ZBExplicitRxResponse(); + uint8_t getSrcEndpoint(); + uint8_t getDstEndpoint(); + uint16_t getClusterId(); + uint16_t getProfileId(); + uint8_t getOption(); + uint8_t getDataLength(); + // frame position where data starts + uint8_t getDataOffset(); + + static const uint8_t API_ID = ZB_EXPLICIT_RX_RESPONSE; +}; + +/** + * Represents a Series 2 RX I/O Sample packet + */ +class ZBRxIoSampleResponse : public ZBRxResponse { +public: + ZBRxIoSampleResponse(); + bool containsAnalog(); + bool containsDigital(); + /** + * Returns true if the pin is enabled + */ + bool isAnalogEnabled(uint8_t pin); + /** + * Returns true if the pin is enabled + */ + bool isDigitalEnabled(uint8_t pin); + /** + * Returns the 10-bit analog reading of the specified pin. + * Valid pins include ADC:xxx. + */ + uint16_t getAnalog(uint8_t pin); + /** + * Returns true if the specified pin is high/on. + * Valid pins include DIO:xxx. + */ + bool isDigitalOn(uint8_t pin); + uint8_t getDigitalMaskMsb(); + uint8_t getDigitalMaskLsb(); + uint8_t getAnalogMask(); + + static const uint8_t API_ID = ZB_IO_SAMPLE_RESPONSE; +}; + +#endif + +#ifdef SERIES_1 +/** + * Represents a Series 1 TX Status packet + */ +class TxStatusResponse : public FrameIdResponse { + public: + TxStatusResponse(); + uint8_t getStatus(); + bool isSuccess(); + + static const uint8_t API_ID = TX_STATUS_RESPONSE; +}; + +/** + * Represents a Series 1 RX packet + */ +class RxResponse : public RxDataResponse { +public: + RxResponse(); + // remember rssi is negative but this is unsigned byte so it's up to you to convert + uint8_t getRssi(); + uint8_t getOption(); + bool isAddressBroadcast(); + bool isPanBroadcast(); + uint8_t getDataLength(); + uint8_t getDataOffset(); + virtual uint8_t getRssiOffset() = 0; +}; + +/** + * Represents a Series 1 16-bit address RX packet + */ +class Rx16Response : public RxResponse { +public: + Rx16Response(); + uint8_t getRssiOffset(); + uint16_t getRemoteAddress16(); + + static const uint8_t API_ID = RX_16_RESPONSE; +protected: + uint16_t _remoteAddress; +}; + +/** + * Represents a Series 1 64-bit address RX packet + */ +class Rx64Response : public RxResponse { +public: + Rx64Response(); + uint8_t getRssiOffset(); + XBeeAddress64& getRemoteAddress64(); + + static const uint8_t API_ID = RX_64_RESPONSE; +private: + XBeeAddress64 _remoteAddress; +}; + +/** + * Represents a Series 1 RX I/O Sample packet + */ +class RxIoSampleBaseResponse : public RxResponse { + public: + RxIoSampleBaseResponse(); + /** + * Returns the number of samples in this packet + */ + uint8_t getSampleSize(); + bool containsAnalog(); + bool containsDigital(); + /** + * Returns true if the specified analog pin is enabled + */ + bool isAnalogEnabled(uint8_t pin); + /** + * Returns true if the specified digital pin is enabled + */ + bool isDigitalEnabled(uint8_t pin); + /** + * Returns the 10-bit analog reading of the specified pin. + * Valid pins include ADC:0-5. Sample index starts at 0 + */ + uint16_t getAnalog(uint8_t pin, uint8_t sample); + /** + * Returns true if the specified pin is high/on. + * Valid pins include DIO:0-8. Sample index starts at 0 + */ + bool isDigitalOn(uint8_t pin, uint8_t sample); + uint8_t getSampleOffset(); + + /** + * Gets the offset of the start of the given sample. + */ + uint8_t getSampleStart(uint8_t sample); + private: +}; + +class Rx16IoSampleResponse : public RxIoSampleBaseResponse { +public: + Rx16IoSampleResponse(); + uint16_t getRemoteAddress16(); + uint8_t getRssiOffset(); + + static const uint8_t API_ID = RX_16_IO_RESPONSE; +}; + +class Rx64IoSampleResponse : public RxIoSampleBaseResponse { +public: + Rx64IoSampleResponse(); + XBeeAddress64& getRemoteAddress64(); + uint8_t getRssiOffset(); + + static const uint8_t API_ID = RX_64_IO_RESPONSE; +private: + XBeeAddress64 _remoteAddress; +}; + +#endif + +/** + * Represents a Modem Status RX packet + */ +class ModemStatusResponse : public XBeeResponse { +public: + ModemStatusResponse(); + uint8_t getStatus(); + + static const uint8_t API_ID = MODEM_STATUS_RESPONSE; +}; + +/** + * Represents an AT Command RX packet + */ +class AtCommandResponse : public FrameIdResponse { + public: + AtCommandResponse(); + /** + * Returns an array containing the two character command + */ + uint8_t* getCommand(); + /** + * Returns the command status code. + * Zero represents a successful command + */ + uint8_t getStatus(); + /** + * Returns an array containing the command value. + * This is only applicable to query commands. + */ + uint8_t* getValue(); + /** + * Returns the length of the command value array. + */ + uint8_t getValueLength(); + /** + * Returns true if status equals AT_OK + */ + bool isOk(); + + static const uint8_t API_ID = AT_COMMAND_RESPONSE; +}; + +/** + * Represents a Remote AT Command RX packet + */ +class RemoteAtCommandResponse : public AtCommandResponse { + public: + RemoteAtCommandResponse(); + /** + * Returns an array containing the two character command + */ + uint8_t* getCommand(); + /** + * Returns the command status code. + * Zero represents a successful command + */ + uint8_t getStatus(); + /** + * Returns an array containing the command value. + * This is only applicable to query commands. + */ + uint8_t* getValue(); + /** + * Returns the length of the command value array. + */ + uint8_t getValueLength(); + /** + * Returns the 16-bit address of the remote radio + */ + uint16_t getRemoteAddress16(); + /** + * Returns the 64-bit address of the remote radio + */ + XBeeAddress64& getRemoteAddress64(); + /** + * Returns true if command was successful + */ + bool isOk(); + + static const uint8_t API_ID = REMOTE_AT_COMMAND_RESPONSE; + private: + XBeeAddress64 _remoteAddress64; +}; + + +/** + * Super class of all XBee requests (TX packets) + * Users should never create an instance of this class; instead use an subclass of this class + * It is recommended to reuse Subclasses of the class to conserve memory + *

+ * This class allocates a buffer to + */ +class XBeeRequest { +public: + /** + * Constructor + * TODO make protected + */ + XBeeRequest(uint8_t apiId, uint8_t frameId); + /** + * Sets the frame id. Must be between 1 and 255 inclusive to get a TX status response. + */ + void setFrameId(uint8_t frameId); + /** + * Returns the frame id + */ + uint8_t getFrameId(); + /** + * Returns the API id + */ + uint8_t getApiId(); + // setting = 0 makes this a pure virtual function, meaning the subclass must implement, like abstract in java + /** + * Starting after the frame id (pos = 0) and up to but not including the checksum + * Note: Unlike Digi's definition of the frame data, this does not start with the API ID. + * The reason for this is the API ID and Frame ID are common to all requests, whereas my definition of + * frame data is only the API specific data. + */ + virtual uint8_t getFrameData(uint8_t pos) = 0; + /** + * Returns the size of the api frame (not including frame id or api id or checksum). + */ + virtual uint8_t getFrameDataLength() = 0; + //void reset(); +protected: + void setApiId(uint8_t apiId); +private: + uint8_t _apiId; + uint8_t _frameId; +}; + +// TODO add reset/clear method since responses are often reused +/** + * Primary interface for communicating with an XBee Radio. + * This class provides methods for sending and receiving packets with an XBee radio via the serial port. + * The XBee radio must be configured in API (packet) mode (AP=2) + * in order to use this software. + *

+ * Since this code is designed to run on a microcontroller, with only one thread, you are responsible for reading the + * data off the serial buffer in a timely manner. This involves a call to a variant of readPacket(...). + * If your serial port is receiving data faster than you are reading, you can expect to lose packets. + * Arduino only has a 128 byte serial buffer so it can easily overflow if two or more packets arrive + * without a call to readPacket(...) + *

+ * In order to conserve resources, this class only supports storing one response packet in memory at a time. + * This means that you must fully consume the packet prior to calling readPacket(...), because calling + * readPacket(...) overwrites the previous response. + *

+ * This class creates an array of size MAX_FRAME_DATA_SIZE for storing the response packet. You may want + * to adjust this value to conserve memory. + * + * \author Andrew Rapp + */ +class XBee { +public: + XBee(); + /** + * Reads all available serial bytes until a packet is parsed, an error occurs, or the buffer is empty. + * You may call xbee.getResponse().isAvailable() after calling this method to determine if + * a packet is ready, or xbee.getResponse().isError() to determine if + * a error occurred. + *

+ * This method should always return quickly since it does not wait for serial data to arrive. + * You will want to use this method if you are doing other timely stuff in your loop, where + * a delay would cause problems. + * NOTE: calling this method resets the current response, so make sure you first consume the + * current response + */ + void readPacket(); + /** + * Waits a maximum of timeout milliseconds for a response packet before timing out; returns true if packet is read. + * Returns false if timeout or error occurs. + */ + bool readPacket(int timeout); + /** + * Reads until a packet is received or an error occurs. + * Caution: use this carefully since if you don't get a response, your Arduino code will hang on this + * call forever!! often it's better to use a timeout: readPacket(int) + */ + void readPacketUntilAvailable(); + /** + * Starts the serial connection on the specified serial port + */ + void begin(Stream &serial); + void getResponse(XBeeResponse &response); + /** + * Returns a reference to the current response + * Note: once readPacket is called again this response will be overwritten! + */ + XBeeResponse& getResponse(); + /** + * Sends a XBeeRequest (TX packet) out the serial port + */ + void send(XBeeRequest &request); + //uint8_t sendAndWaitForResponse(XBeeRequest &request, int timeout); + /** + * Returns a sequential frame id between 1 and 255 + */ + uint8_t getNextFrameId(); + /** + * Specify the serial port. Only relevant for Arduinos that support multiple serial ports (e.g. Mega) + */ + void setSerial(Stream &serial); +private: + bool available(); + uint8_t read(); + void flush(); + void write(uint8_t val); + void sendByte(uint8_t b, bool escape); + void resetResponse(); + XBeeResponse _response; + bool _escape; + // current packet position for response. just a state variable for packet parsing and has no relevance for the response otherwise + uint8_t _pos; + // last byte read + uint8_t b; + uint8_t _checksumTotal; + uint8_t _nextFrameId; + // buffer for incoming RX packets. holds only the api specific frame data, starting after the api id byte and prior to checksum + uint8_t _responseFrameData[MAX_FRAME_DATA_SIZE]; + Stream* _serial; +}; + + +/** + * This class can be used instead of the XBee class and allows + * user-specified callback functions to be called when responses are + * received, simplifying the processing code and reducing boilerplate. + * + * To use it, first register your callback functions using the onXxx + * methods. Each method has a uintptr_t data argument, that can be used to + * pass arbitrary data to the callback (useful when using the same + * function for multiple callbacks, or have a generic function that can + * behave differently in different circumstances). Supplying the data + * parameter is optional, but the callback must always accept it (just + * ignore it if it's unused). The uintptr_t type is an integer type + * guaranteed to be big enough to fit a pointer (it is 16-bit on AVR, + * 32-bit on ARM), so it can also be used to store a pointer to access + * more data if required (using proper casts). + * + * There can be only one callback of each type registered at one time, + * so registering callback overwrites any previously registered one. To + * unregister a callback, pass NULL as the function. + * + * To ensure that the callbacks are actually called, call the loop() + * method regularly (in your loop() function, for example). This takes + * care of calling readPacket() and getResponse() other methods on the + * XBee class, so there is no need to do so directly (though it should + * not mess with this class if you do, it would only mean some callbacks + * aren't called). + * + * Inside callbacks, you should generally not be blocking / waiting. + * Since callbacks can be called from inside waitFor() and friends, a + * callback that doesn't return quickly can mess up the waitFor() + * timeout. + * + * Sending packets is not a problem inside a callback, but avoid + * receiving a packet (e.g. calling readPacket(), loop() or waitFor() + * and friends) inside a callback (since that would overwrite the + * current response, messing up any pending callbacks and waitFor() etc. + * methods already running). + */ +class XBeeWithCallbacks : public XBee { +public: + + /** + * Register a packet error callback. It is called whenever an + * error occurs in the packet reading process. Arguments to the + * callback will be the error code (as returned by + * XBeeResponse::getErrorCode()) and the data parameter. while + * registering the callback. + */ + void onPacketError(void (*func)(uint8_t, uintptr_t), uintptr_t data = 0) { _onPacketError.set(func, data); } + + /** + * Register a response received callback. It is called whenever + * a response was succesfully received, before a response + * specific callback (or onOtherResponse) below is called. + * + * Arguments to the callback will be the received response and + * the data parameter passed while registering the callback. + */ + void onResponse(void (*func)(XBeeResponse&, uintptr_t), uintptr_t data = 0) { _onResponse.set(func, data); } + + /** + * Register an other response received callback. It is called + * whenever a response was succesfully received, but no response + * specific callback was registered using the functions below + * (after the onResponse callback is called). + * + * Arguments to the callback will be the received response and + * the data parameter passed while registering the callback. + */ + void onOtherResponse(void (*func)(XBeeResponse&, uintptr_t), uintptr_t data = 0) { _onOtherResponse.set(func, data); } + + // These functions register a response specific callback. They + // are called whenever a response of the appropriate type was + // succesfully received (after the onResponse callback is + // called). + // + // Arguments to the callback will be the received response + // (already converted to the appropriate type) and the data + // parameter passed while registering the callback. + void onZBTxStatusResponse(void (*func)(ZBTxStatusResponse&, uintptr_t), uintptr_t data = 0) { _onZBTxStatusResponse.set(func, data); } + void onZBRxResponse(void (*func)(ZBRxResponse&, uintptr_t), uintptr_t data = 0) { _onZBRxResponse.set(func, data); } + void onZBExplicitRxResponse(void (*func)(ZBExplicitRxResponse&, uintptr_t), uintptr_t data = 0) { _onZBExplicitRxResponse.set(func, data); } + void onZBRxIoSampleResponse(void (*func)(ZBRxIoSampleResponse&, uintptr_t), uintptr_t data = 0) { _onZBRxIoSampleResponse.set(func, data); } + void onTxStatusResponse(void (*func)(TxStatusResponse&, uintptr_t), uintptr_t data = 0) { _onTxStatusResponse.set(func, data); } + void onRx16Response(void (*func)(Rx16Response&, uintptr_t), uintptr_t data = 0) { _onRx16Response.set(func, data); } + void onRx64Response(void (*func)(Rx64Response&, uintptr_t), uintptr_t data = 0) { _onRx64Response.set(func, data); } + void onRx16IoSampleResponse(void (*func)(Rx16IoSampleResponse&, uintptr_t), uintptr_t data = 0) { _onRx16IoSampleResponse.set(func, data); } + void onRx64IoSampleResponse(void (*func)(Rx64IoSampleResponse&, uintptr_t), uintptr_t data = 0) { _onRx64IoSampleResponse.set(func, data); } + void onModemStatusResponse(void (*func)(ModemStatusResponse&, uintptr_t), uintptr_t data = 0) { _onModemStatusResponse.set(func, data); } + void onAtCommandResponse(void (*func)(AtCommandResponse&, uintptr_t), uintptr_t data = 0) { _onAtCommandResponse.set(func, data); } + void onRemoteAtCommandResponse(void (*func)(RemoteAtCommandResponse&, uintptr_t), uintptr_t data = 0) { _onRemoteAtCommandResponse.set(func, data); } + + /** + * Regularly call this method, which ensures that the serial + * buffer is processed and the appropriate callbacks are called. + */ + void loop(); + + /** + * Wait for a API response of the given type, optionally + * filtered by the given match function. + * + * If a match function is given it is called for every response + * of the right type received, passing the response and the data + * parameter passed to this method. If the function returns true + * (or if no function was passed), waiting stops and this method + * returns 0. If the function returns false, waiting + * continues. After the given timeout passes, this method + * returns XBEE_WAIT_TIMEOUT. + * + * If a valid frameId is passed (e.g. 0-255 inclusive) and a + * status API response frame is received while waiting, that has + * a *non-zero* status, waiting stops and that status is + * received. This is intended for when a TX packet was sent and + * you are waiting for an RX reply, which will most likely never + * arrive when TX failed. However, since the status reply is not + * guaranteed to arrive before the RX reply (a remote module can + * send a reply before the ACK), first calling waitForStatus() + * and then waitFor() can sometimes miss the reply RX packet. + * + * Note that when the intended response is received *before* the + * status reply, the latter will not be processed by this + * method and will be subsequently processed by e.g. loop() + * normally. + * + * While waiting, any other responses received are passed to the + * relevant callbacks, just as if calling loop() continuously + * (except for the response sought, that one is only passed to + * the OnResponse handler and no others). + * + * After this method returns, the response itself can still be + * retrieved using getResponse() as normal. + */ + template + uint8_t waitFor(Response& response, uint16_t timeout, bool (*func)(Response&, uintptr_t) = NULL, uintptr_t data = 0, int16_t frameId = -1) { + return waitForInternal(Response::API_ID, &response, timeout, (void*)func, data, frameId); + } + + /** + * Sends a XBeeRequest (TX packet) out the serial port, and wait + * for a status response API frame (up until the given timeout). + * Essentially this just calls send() and waitForStatus(). + * See waitForStatus for the meaning of the return value and + * more details. + */ + uint8_t sendAndWait(XBeeRequest &request, uint16_t timeout) { + send(request); + return waitForStatus(request.getFrameId(), timeout); + } + + /** + * Wait for a status API response with the given frameId and + * return the status from the packet (for ZB_TX_STATUS_RESPONSE, + * this returns just the delivery status, not the routing + * status). If the timeout is reached before reading the + * response, XBEE_WAIT_TIMEOUT is returned instead. + * + * While waiting, any other responses received are passed to the + * relevant callbacks, just as if calling loop() continuously + * (except for the status response sought, that one is only + * passed to the OnResponse handler and no others). + * + * After this method returns, the response itself can still be + * retrieved using getResponse() as normal. + */ + uint8_t waitForStatus(uint8_t frameId, uint16_t timeout); +private: + /** + * Internal version of waitFor that does not need to be + * templated (to prevent duplication the implementation for + * every response type you might want to wait for). Instead of + * using templates, this accepts the apiId to wait for and will + * cast the given response object and the argument to the given + * function to the corresponding type. This means that the + * void* given must match the api id! + */ + uint8_t waitForInternal(uint8_t apiId, void *response, uint16_t timeout, void *func, uintptr_t data, int16_t frameId); + + /** + * Helper that checks if the current response is a status + * response with the given frame id. If so, returns the status + * byte from the response, otherwise returns 0xff. + */ + uint8_t matchStatus(uint8_t frameId); + + /** + * Top half of a typical loop(). Calls readPacket(), calls + * onPacketError on error, calls onResponse when a response is + * available. Returns in the true in the latter case, after + * which a caller should typically call loopBottom(). + */ + bool loopTop(); + + /** + * Bottom half of a typical loop. Call only when a valid + * response was read, will call all response-specific callbacks. + */ + void loopBottom(); + + template struct Callback { + void (*func)(Arg, uintptr_t); + uintptr_t data; + void set(void (*func)(Arg, uintptr_t), uintptr_t data) { + this->func = func; + this->data = data; + } + bool call(Arg arg) { + if (this->func) { + this->func(arg, this->data); + return true; + } + return false; + } + }; + + Callback _onPacketError; + Callback _onResponse; + Callback _onOtherResponse; + Callback _onZBTxStatusResponse; + Callback _onZBRxResponse; + Callback _onZBExplicitRxResponse; + Callback _onZBRxIoSampleResponse; + Callback _onTxStatusResponse; + Callback _onRx16Response; + Callback _onRx64Response; + Callback _onRx16IoSampleResponse; + Callback _onRx64IoSampleResponse; + Callback _onModemStatusResponse; + Callback _onAtCommandResponse; + Callback _onRemoteAtCommandResponse; +}; + +/** + * All TX packets that support payloads extend this class + */ +class PayloadRequest : public XBeeRequest { +public: + PayloadRequest(uint8_t apiId, uint8_t frameId, uint8_t *payload, uint8_t payloadLength); + /** + * Returns the payload of the packet, if not null + */ + uint8_t* getPayload(); + /** + * Sets the payload array + */ + void setPayload(uint8_t* payloadPtr); + + /* + * Set the payload and its length in one call. + */ + void setPayload(uint8_t* payloadPtr, uint8_t payloadLength) { + setPayload(payloadPtr); + setPayloadLength(payloadLength); + } + + /** + * Returns the length of the payload array, as specified by the user. + */ + uint8_t getPayloadLength(); + /** + * Sets the length of the payload to include in the request. For example if the payload array + * is 50 bytes and you only want the first 10 to be included in the packet, set the length to 10. + * Length must be <= to the array length. + */ + void setPayloadLength(uint8_t payloadLength); +private: + uint8_t* _payloadPtr; + uint8_t _payloadLength; +}; + +#ifdef SERIES_1 + +/** + * Represents a Series 1 TX packet that corresponds to Api Id: TX_16_REQUEST + *

+ * Be careful not to send a data array larger than the max packet size of your radio. + * This class does not perform any validation of packet size and there will be no indication + * if the packet is too large, other than you will not get a TX Status response. + * The datasheet says 100 bytes is the maximum, although that could change in future firmware. + */ +class Tx16Request : public PayloadRequest { +public: + Tx16Request(uint16_t addr16, uint8_t option, uint8_t *payload, uint8_t payloadLength, uint8_t frameId); + /** + * Creates a Unicast Tx16Request with the ACK option and DEFAULT_FRAME_ID + */ + Tx16Request(uint16_t addr16, uint8_t *payload, uint8_t payloadLength); + /** + * Creates a default instance of this class. At a minimum you must specify + * a payload, payload length and a destination address before sending this request. + */ + Tx16Request(); + uint16_t getAddress16(); + void setAddress16(uint16_t addr16); + uint8_t getOption(); + void setOption(uint8_t option); + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); +protected: +private: + uint16_t _addr16; + uint8_t _option; +}; + +/** + * Represents a Series 1 TX packet that corresponds to Api Id: TX_64_REQUEST + * + * Be careful not to send a data array larger than the max packet size of your radio. + * This class does not perform any validation of packet size and there will be no indication + * if the packet is too large, other than you will not get a TX Status response. + * The datasheet says 100 bytes is the maximum, although that could change in future firmware. + */ +class Tx64Request : public PayloadRequest { +public: + Tx64Request(XBeeAddress64 &addr64, uint8_t option, uint8_t *payload, uint8_t payloadLength, uint8_t frameId); + /** + * Creates a unicast Tx64Request with the ACK option and DEFAULT_FRAME_ID + */ + Tx64Request(XBeeAddress64 &addr64, uint8_t *payload, uint8_t payloadLength); + /** + * Creates a default instance of this class. At a minimum you must specify + * a payload, payload length and a destination address before sending this request. + */ + Tx64Request(); + XBeeAddress64& getAddress64(); + void setAddress64(XBeeAddress64& addr64); + // TODO move option to superclass + uint8_t getOption(); + void setOption(uint8_t option); + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); +private: + XBeeAddress64 _addr64; + uint8_t _option; +}; + +#endif + + +#ifdef SERIES_2 + +/** + * Represents a Series 2 TX packet that corresponds to Api Id: ZB_TX_REQUEST + * + * Be careful not to send a data array larger than the max packet size of your radio. + * This class does not perform any validation of packet size and there will be no indication + * if the packet is too large, other than you will not get a TX Status response. + * The datasheet says 72 bytes is the maximum for ZNet firmware and ZB Pro firmware provides + * the ATNP command to get the max supported payload size. This command is useful since the + * maximum payload size varies according to certain settings, such as encryption. + * ZB Pro firmware provides a PAYLOAD_TOO_LARGE that is returned if payload size + * exceeds the maximum. + */ +class ZBTxRequest : public PayloadRequest { +public: + /** + * Creates a unicast ZBTxRequest with the ACK option and DEFAULT_FRAME_ID + */ + ZBTxRequest(const XBeeAddress64 &addr64, uint8_t *payload, uint8_t payloadLength); + ZBTxRequest(const XBeeAddress64 &addr64, uint16_t addr16, uint8_t broadcastRadius, uint8_t option, uint8_t *payload, uint8_t payloadLength, uint8_t frameId); + /** + * Creates a default instance of this class. At a minimum you must specify + * a payload, payload length and a 64-bit destination address before sending + * this request. + */ + ZBTxRequest(); + XBeeAddress64& getAddress64(); + uint16_t getAddress16(); + uint8_t getBroadcastRadius(); + uint8_t getOption(); + void setAddress64(const XBeeAddress64& addr64); + void setAddress16(uint16_t addr16); + void setBroadcastRadius(uint8_t broadcastRadius); + void setOption(uint8_t option); +protected: + // declare virtual functions + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); + XBeeAddress64 _addr64; + uint16_t _addr16; + uint8_t _broadcastRadius; + uint8_t _option; +}; + +/** + * Represents a Series 2 TX packet that corresponds to Api Id: ZB_EXPLICIT_TX_REQUEST + * + * See the warning about maximum packet size for ZBTxRequest above, + * which probably also applies here as well. + * + * Note that to distinguish reply packets from non-XBee devices, set + * AO=1 to enable reception of ZBExplicitRxResponse packets. + */ +class ZBExplicitTxRequest : public ZBTxRequest { +public: + /** + * Creates a unicast ZBExplicitTxRequest with the ACK option and + * DEFAULT_FRAME_ID. + * + * It uses the Maxstream profile (0xc105), both endpoints 232 + * and cluster 0x0011, resulting in the same packet as sent by a + * normal ZBTxRequest. + */ + ZBExplicitTxRequest(XBeeAddress64 &addr64, uint8_t *payload, uint8_t payloadLength); + /** + * Create a ZBExplicitTxRequest, specifying all fields. + */ + ZBExplicitTxRequest(XBeeAddress64 &addr64, uint16_t addr16, uint8_t broadcastRadius, uint8_t option, uint8_t *payload, uint8_t payloadLength, uint8_t frameId, uint8_t srcEndpoint, uint8_t dstEndpoint, uint16_t clusterId, uint16_t profileId); + /** + * Creates a default instance of this class. At a minimum you + * must specify a payload, payload length and a destination + * address before sending this request. + * + * Furthermore, it uses the Maxstream profile (0xc105), both + * endpoints 232 and cluster 0x0011, resulting in the same + * packet as sent by a normal ZBExplicitTxRequest. + */ + ZBExplicitTxRequest(); + uint8_t getSrcEndpoint(); + uint8_t getDstEndpoint(); + uint16_t getClusterId(); + uint16_t getProfileId(); + void setSrcEndpoint(uint8_t endpoint); + void setDstEndpoint(uint8_t endpoint); + void setClusterId(uint16_t clusterId); + void setProfileId(uint16_t profileId); +protected: + // declare virtual functions + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); +private: + uint8_t _srcEndpoint; + uint8_t _dstEndpoint; + uint16_t _profileId; + uint16_t _clusterId; +}; + +#endif + +/** + * Represents an AT Command TX packet + * The command is used to configure the serially connected XBee radio + */ +class AtCommandRequest : public XBeeRequest { +public: + AtCommandRequest(); + AtCommandRequest(uint8_t *command); + AtCommandRequest(uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength); + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); + uint8_t* getCommand(); + void setCommand(uint8_t* command); + uint8_t* getCommandValue(); + void setCommandValue(uint8_t* command); + uint8_t getCommandValueLength(); + void setCommandValueLength(uint8_t length); + /** + * Clears the optional commandValue and commandValueLength so that a query may be sent + */ + void clearCommandValue(); + //void reset(); +private: + uint8_t *_command; + uint8_t *_commandValue; + uint8_t _commandValueLength; +}; + +/** + * Represents an Remote AT Command TX packet + * The command is used to configure a remote XBee radio + */ +class RemoteAtCommandRequest : public AtCommandRequest { +public: + RemoteAtCommandRequest(); + /** + * Creates a RemoteAtCommandRequest with 16-bit address to set a command. + * 64-bit address defaults to broadcast and applyChanges is true. + */ + RemoteAtCommandRequest(uint16_t remoteAddress16, uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength); + /** + * Creates a RemoteAtCommandRequest with 16-bit address to query a command. + * 64-bit address defaults to broadcast and applyChanges is true. + */ + RemoteAtCommandRequest(uint16_t remoteAddress16, uint8_t *command); + /** + * Creates a RemoteAtCommandRequest with 64-bit address to set a command. + * 16-bit address defaults to broadcast and applyChanges is true. + */ + RemoteAtCommandRequest(XBeeAddress64 &remoteAddress64, uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength); + /** + * Creates a RemoteAtCommandRequest with 16-bit address to query a command. + * 16-bit address defaults to broadcast and applyChanges is true. + */ + RemoteAtCommandRequest(XBeeAddress64 &remoteAddress64, uint8_t *command); + uint16_t getRemoteAddress16(); + void setRemoteAddress16(uint16_t remoteAddress16); + XBeeAddress64& getRemoteAddress64(); + void setRemoteAddress64(XBeeAddress64 &remoteAddress64); + bool getApplyChanges(); + void setApplyChanges(bool applyChanges); + uint8_t getFrameData(uint8_t pos); + uint8_t getFrameDataLength(); + static XBeeAddress64 broadcastAddress64; +// static uint16_t broadcast16Address; +private: + XBeeAddress64 _remoteAddress64; + uint16_t _remoteAddress16; + bool _applyChanges; +}; + + + +#endif //XBee_h diff --git a/ASV-2/POKB/main code/POKB_Main/can.h b/ASV-2/POKB/main code/POKB_Main/can.h new file mode 100644 index 00000000..6943bfd7 --- /dev/null +++ b/ASV-2/POKB/main code/POKB_Main/can.h @@ -0,0 +1,119 @@ +/* + can.h + 2012 Copyright (c) Seeed Technology Inc. All right reserved. + + Author:Loovee + Contributor: Cory J. Fowler + 2014-1-16 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110- + 1301 USA +*/ +#ifndef _MCP2515_H_ +#define _MCP2515_H_ + +#include "can_defines.h" + +#define MAX_CHAR_IN_MESSAGE 8 + +class MCP_CAN +{ + private: + + INT8U m_nExtFlg; /* identifier xxxID */ + /* either extended (the 29 LSB) */ + /* or standard (the 11 LSB) */ + INT32U m_nID; /* can id */ + INT8U m_nDlc; /* data length: */ + INT8U m_nDta[MAX_CHAR_IN_MESSAGE]; /* data */ + INT8U m_nRtr; /* rtr */ + INT8U m_nfilhit; + INT8U SPICS; + +/* +* mcp2515 driver function +*/ + // private: +private: + + void mcp2515_reset(void); /* reset mcp2515 */ + + INT8U mcp2515_readRegister(const INT8U address); /* read mcp2515's register */ + + void mcp2515_readRegisterS(const INT8U address, + INT8U values[], + const INT8U n); + void mcp2515_setRegister(const INT8U address, /* set mcp2515's register */ + const INT8U value); + + void mcp2515_setRegisterS(const INT8U address, /* set mcp2515's registers */ + const INT8U values[], + const INT8U n); + + void mcp2515_initCANBuffers(void); + + void mcp2515_modifyRegister(const INT8U address, /* set bit of one register */ + const INT8U mask, + const INT8U data); + + INT8U mcp2515_readStatus(void); /* read mcp2515's Status */ + INT8U mcp2515_setCANCTRL_Mode(const INT8U newmode); /* set mode */ + INT8U mcp2515_configRate(const INT8U canSpeed); /* set boadrate */ + INT8U mcp2515_init(const INT8U canSpeed); /* mcp2515init */ + + void mcp2515_write_id( const INT8U mcp_addr, /* write can id */ + const INT8U ext, + const INT32U id ); + + void mcp2515_read_id( const INT8U mcp_addr, /* read can id */ + INT8U* ext, + INT32U* id ); + + void mcp2515_write_canMsg( const INT8U buffer_sidh_addr ); /* write can msg */ + void mcp2515_read_canMsg( const INT8U buffer_sidh_addr); /* read can msg */ + void mcp2515_start_transmit(const INT8U mcp_addr); /* start transmit */ + INT8U mcp2515_getNextFreeTXBuf(INT8U *txbuf_n); /* get Next free txbuf */ + +/* +* can operator function +*/ + + INT8U setMsg(INT32U id, INT8U ext, INT8U len, INT8U rtr, INT8U *pData); /* set message */ + INT8U setMsg(INT32U id, INT8U ext, INT8U len, INT8U *pData); /* set message */ + INT8U readMsg(); /* read message */ + INT8U sendMsg(); /* send message */ + +public: + MCP_CAN(INT8U _CS); + INT8U begin(INT8U speedset); /* init can */ + INT8U init_Mask(INT8U num, INT8U ext, INT32U ulData); /* init Masks */ + INT8U init_Filt(INT8U num, INT8U ext, INT32U ulData); /* init filters */ + INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U rtr, INT8U len, INT8U *buf); /* send buf */ + INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U len, INT8U *buf); /* send buf */ + INT8U readMsgBuf(INT8U *len, INT8U *buf); /* read buf */ + INT8U readMsgBufID(INT32U *ID, INT8U *len, INT8U *buf); /* read buf with object ID */ + INT8U checkTXStatus(INT8U buf); + INT8U clearMsg(); /* clear all message to zero */ + INT8U checkReceive(void); /* if something received */ + INT8U checkError(void); /* if something error */ + INT32U getCanId(void); /* get can id when receive */ + INT8U isRemoteRequest(void); /* get RR flag when receive */ + INT8U setupCANFrame(INT8U *buf, INT8U pos, INT8U size, INT32U val); + INT32U parseCANFrame(INT8U *buf, INT8U pos, INT8U size); +}; + +#endif +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/ASV-2/POKB/main code/POKB_Main/can_asv_defines.h b/ASV-2/POKB/main code/POKB_Main/can_asv_defines.h new file mode 100644 index 00000000..d4bcf679 --- /dev/null +++ b/ASV-2/POKB/main code/POKB_Main/can_asv_defines.h @@ -0,0 +1,64 @@ +//################################################### +//################################################### + +//___. ___. +//\_ |__\_ |__ _____ _________ __ +// | __ \| __ \\__ \ / ___/\ \/ / +// | \_\ \ \_\ \/ __ \_\___ \ \ / +// |___ /___ (____ /____ > \_/ +// \/ \/ \/ \/ + +// Written by Ng Ren Zhi + +// Change Log for v1.0: +// - Initial commit + +//################################################### +//################################################### + +#ifndef _DEFINE_H_ +#define _DEFINE_H_ + +#define CAN_thruster 101 +#define CAN_manual_thruster 102 +#define CAN_control_link 103 +#define CAN_heartbeat 104 +#define CAN_soft_e_stop 105 +#define CAN_e_stop 106 +#define CAN_LARS 107 +#define CAN_POPB_control 108 +#define CAN_Shooter 109 +#define CAN_LARS_stats 110 +#define CAN_battery1_stats 111 +#define CAN_battery2_stats 112 +#define CAN_esc1_motor_stats 113 +#define CAN_esc2_motor_stats 114 +#define CAN_remote_kill_stats 115 +#define CAN_INS_stats 116 +#define CAN_GPS_stats 117 +#define CAN_cpu_temp 118 +#define CAN_POSB_stats 119 +#define CAN_POPB_stats 120 +#define CAN_POSB_BUS_stats 121 +#define CAN_POKB_BUS_stats 122 +#define CAN_POPB_BUS_stats 123 +#define CAN_Tele_BUS_stats 124 +#define CAN_LARS_BUS_stats 125 +#define CAN_MANI_stats 126 + +//CAN Heartbeat +#define HEARTBEAT_POSB 1 +#define HEARTBEAT_POPB 2 +#define HEARTBEAT_POKB 3 +#define HEARTBEAT_Tele 4 +#define HEARTBEAT_LARS 5 +#define HEARTBEAT_Cogswell 6 +#define HEARTBEAT_OCS 7 +#define HEARTBEAT_RC 8 +//Bit position +#define HEARTBEAT_BATT1 0 +#define HEARTBEAT_BATT2 1 +#define HEARTBEAT_ESC1 2 +#define HEARTBEAT_ESC2 3 + +#endif \ No newline at end of file diff --git a/ASV-3/POKB/POKB.ino b/ASV-3/POKB/POKB.ino new file mode 100644 index 00000000..f9ec2297 --- /dev/null +++ b/ASV-3/POKB/POKB.ino @@ -0,0 +1,236 @@ +#include +#include +#include +#include "defines.h" + +#define CAN_CHIP_SELECT 8 +#define SERIAL_BAUD_RATE 115200 +#define N2420_BAUD_RATE 115200 +#define REMOTE_KILL_ADDRESS 4 + +#define CONTACTOR_CONTROL 11 //NMOS, Active High +#define ONBOARD_SWITCH 21 +#define KILL 31 + +#define RECEIVE_REMOTE_KILL_TIMEOUT 500 +#define RECEIVE_POSB_HEARTBEAT_TIMEOUT 3000 +#define UPDATE_CONTACTOR_TIMEOUT 100 +#define SEND_POKB_HEARTBEAT_TIMEOUT 500 +#define SEND_POKB_STATUS_TIMEOUT 1000 + +// CAN Setup +uint32_t id = 0; +uint8_t len = 0; +uint8_t buf[8]; + +MCP_CAN CAN(CAN_CHIP_SELECT); + +// N2420 Setup +N2420 n2420 (POKB); +uint8_t inByte = 0; +uint8_t* inBuf; + +// Time Counter Variables +uint32_t receiveRemoteKillTime = 0; +uint32_t receivePOSBHeartbeatTime = 0; +uint32_t updateContactorTime = 0; +uint32_t sendPOKBHeartbeatTime = 0; +uint32_t sendPOKBStatusTime = 0; + +int noData = 0; + +// Control Variables +bool onboardKill = false; +bool remoteKill = false; +bool softwareKill = false; + +void setup() { + // put your setup code here, to run once: + Serial.begin(SERIAL_BAUD_RATE); + Serial1.begin(N2420_BAUD_RATE); + + n2420.setSerial(&Serial1); + + pinMode(CONTACTOR_CONTROL, OUTPUT); + digitalWrite(CONTACTOR_CONTROL, LOW); + + pinMode(ONBOARD_SWITCH, INPUT); + + Serial.println("Plenty of Kill Board."); + + canInitialise(); + + receiveRemoteKillTime = millis(); + receivePOSBHeartbeatTime = millis(); + updateContactorTime = millis(); + sendPOKBHeartbeatTime = millis(); + sendPOKBStatusTime = millis(); +} + +void loop() { + // Receive Onboard Kill via ATmega 2560 Input Pin + onboardKill = !digitalRead(ONBOARD_SWITCH); + + // Receive Remote Kill via N2420 + if ((millis() - receiveRemoteKillTime) > RECEIVE_REMOTE_KILL_TIMEOUT) { + receiveRemoteKill(); + receiveRemoteKillTime = millis(); + } + + // Receive Software Kill and POSB Heartbeat via CAN + receiveCanMessage(); + + // Update contactor + updateContactor(); + + // Send POKB Heartbeat via CAN + if ((millis() - sendPOKBHeartbeatTime) > SEND_POKB_HEARTBEAT_TIMEOUT) { + sendPOKBHeartbeat(); + sendPOKBHeartbeatTime = millis(); + } + + // Send POKB Status via CAN + if ((millis() - sendPOKBStatusTime) > SEND_POKB_STATUS_TIMEOUT) { + if (onboardKill || remoteKill || softwareKill) { + sendPOKBStatus(true); + } + + else { + sendPOKBStatus(false); + } + + sendPOKBStatusTime = millis(); + } +} + +void canInitialise() { +START_INIT: + if (CAN.begin(CAN_1000KBPS) == CAN_OK) { + Serial.println("CAN Bus: Initialisation successful."); + } + + else { + Serial.println("CAN Bus: Initialisation failed."); + Serial.println("CAN Bus: Re-initialising."); + delay(1000); + goto START_INIT; + } + + Serial.println("Starting transmission..."); +} + +void receiveRemoteKill() { + // Continuously read packets + n2420.readPacket(); + + if (n2420.isAvailable()) { + // Serial.println("Response available."); + + if (n2420.getReceivingAddress() == REMOTE_KILL_ADDRESS){ //REMOTE_KILL_ADDRESS is defined as 4 from library + // Get data + inBuf = n2420.showReceived(); + inByte = *(inBuf+2); + Serial.print("inByte: "); + Serial.println(inByte, HEX); + //Need to modify this if not 15, go to noData++ not false directly + //remoteKill = (inByte == 0x15) ? false : true; + //noData = 0; + if (inByte == 0x15) { + remoteKill = false; + noData = 0; + } + else if (inByte == 0x44) { + remoteKill = true; + noData = 0; + } + else { + noData++; + if (noData >= 20) { + remoteKill = true; + noData = 0; + Serial.println("Connection timeout kill"); + } + } + } + } + + else { + noData++; + Serial.print("noData: "); + Serial.println(noData); + + if (noData >= 20) { + remoteKill = true; + noData = 0; + Serial.println("Connection timeout kill."); + } + } +} + +void receiveCanMessage() { + if (CAN.checkReceive() == CAN_MSGAVAIL) { + CAN.readMsgBufID(&id, &len, buf); + + switch (id) { + case CAN_HEARTBEAT: + + if (buf[0] == HEARTBEAT_POSB) + receivePOSBHeartbeatTime = millis(); + break; + case CAN_SOFT_E_STOP: + softwareKill = !buf[0]; + break; + default: + break; + } + + CAN.clearMsg(); + } +} + +void updateContactor() { + // Failsafe + // Check POSB Heartbeat + + /* + if ((millis() - receivePOSBHeartbeatTime) > RECEIVE_POSB_HEARTBEAT_TIMEOUT) { + // digitalWrite(CONTACTOR_CONTROL, LOW); + } + */ + + // else if ((millis() - updateContactorTime) > UPDATE_CONTACTOR_TIMEOUT) { + if ((millis() - updateContactorTime) > UPDATE_CONTACTOR_TIMEOUT) { + // Serial.print("Onboard Kill: "); + // Serial.print(onboardKill); + // Serial.print(" | "); + // Serial.print("Remote Kill: "); + // Serial.print(remoteKill); + // Serial.print(" | "); + // Serial.print("Software Kill: "); + // Serial.println(softwareKill); + + if (onboardKill || remoteKill || softwareKill) { + digitalWrite(CONTACTOR_CONTROL, LOW); + digitalWrite(KILL, HIGH); + //Serial.println("Contactor switched off."); + } + + else { + digitalWrite(CONTACTOR_CONTROL, HIGH); + digitalWrite(KILL, LOW); + //Serial.println("Contactor switched on."); + } + + updateContactorTime = millis(); + } +} + +void sendPOKBHeartbeat() { + CAN.setupCANFrame(buf, 0, 1, HEARTBEAT_POKB); + CAN.sendMsgBuf(CAN_HEARTBEAT, 0, 1, buf); +} + +void sendPOKBStatus(bool pokbStatus) { + CAN.setupCANFrame(buf, 0, 1, pokbStatus); + CAN.sendMsgBuf(CAN_E_STOP, 0, 1, buf); +} diff --git a/ASV-3/POKB/defines.h b/ASV-3/POKB/defines.h new file mode 100644 index 00000000..509e7af1 --- /dev/null +++ b/ASV-3/POKB/defines.h @@ -0,0 +1,33 @@ +#ifndef _DEFINES_H_ +#define _DEFINES_H_ + +// CAN +#define CAN_Chip_Select 8 + +// KILL +#define NMOS_CONTACTOR 11 +#define HARDKILL_IN 5 + +// XBEE +#define XBEE_BAUDRATE 115200 +#define SERIAL_BAUDRATE 115200 + +// TIMEOUT +#define XBEE_TIMEOUT 500 +#define HEARTBEAT_TIMEOUT 500 +#define CAN_TIMEOUT 1000 +#define ESTOP_TIMEOUT 100 +#define FAILSAFE_TIMEOUT 3000 + +// HEARTBEAT + +#define HEARTBEAT_POSB 0x01 +#define HEARTBEAT_POPB 0x02 +#define HEARTBEAT_POKB 0x03 +#define HEARTBEAT_Tele 0x04 +#define HEARTBEAT_LARS 0x05 +#define HEARTBEAT_Cogswell 0x06 +#define HEARTBEAT_OCS 0x07 + + +#endif diff --git a/ASV-3/Telemetry/main_code/Telemetry.ino b/ASV-3/Telemetry/main_code/Telemetry/Telemetry.ino similarity index 100% rename from ASV-3/Telemetry/main_code/Telemetry.ino rename to ASV-3/Telemetry/main_code/Telemetry/Telemetry.ino diff --git a/AUV-3.99/Sensor and Telemetry/main code/Sensor and Telemetry/stb/stb.ino b/AUV-3.99/Sensor and Telemetry/main code/Sensor and Telemetry/stb/stb.ino new file mode 100644 index 00000000..1586a4ce --- /dev/null +++ b/AUV-3.99/Sensor and Telemetry/main code/Sensor and Telemetry/stb/stb.ino @@ -0,0 +1,658 @@ +//################################################### +//################################################### +// +//#### #### +//# # # # ###### ######## ######## +//# #### # #### # ## # ## # # ## # +//# ## # ## #### # # ## # # ## # +//# ## # # ## # ## # # ## # # ## # +//# ## # # ## # # ## # # ## # ## ## +//# ## # ## ## # ## # ## ## +// # #### # #### ####### ####### #### +// +// +// Sensor and Telemetry for BBAUV 3.99 +// Firmware Version : v1.2 +// +// Written by Chia Che edited by titus +// Change log v1.2: +// Changed SoftPMW library to PalatisSoftPWM by per1234 due to compatibility issues +// Update depth calculation for US381-000005-050PG 50psi gauge depth sensor +// Correct SBC temperature code +// +//################################################### +//################################################### + +#include +#include +#include +#include +#include +#include "LCD_Driver.h" +#include "define.h" +#include +#include +#include //for CAN controller +#include +#include "can_auv_define.h" +#include + + +// CAN variable +MCP_CAN CAN(CAN_Chip_Select); +uint32_t id = 0; +uint8_t len = 0; //length of CAN message, taken care by library +uint8_t buf[8]; //Buffer for CAN message + +//Screen variables +LCD screen = LCD(SCREEN_CS, SCREEN_RESET); +static uint16_t internalStats[INT_STAT_COUNT] = { 0 }; +static uint16_t powerStats[POWER_STAT_COUNT] = { 0 }; +static uint32_t heartbeat_timeout[HB_COUNT] = { 0 }; +static uint32_t loopTime = 0; + +//Sensor variables +Adafruit_ADS1115 ads(ADS_ADDR); +HIH613x humid(HUMIDITY_ADDR); +uint8_t humidity = 0; +uint8_t IntPressure = 0; +uint8_t temperature = 0; +uint16_t ExtPressure = 0; +uint8_t InitialP = 0; +uint16_t rawExtPressure = 0; +static uint32_t humidloop = 0; +bool readHumid = false; +static uint32_t pressure_loop = 0; +static uint32_t filter_loop = 0; + +//LED +//========== Read this before use ========== +// https://github.com/per1234/PalatisSoftPWM + +SOFTPWM_DEFINE_PIN22_CHANNEL(0); //Arduino pin 22 as channel 0 +SOFTPWM_DEFINE_PIN23_CHANNEL(1); //Arduino pin 23 as channel 1 +SOFTPWM_DEFINE_PIN24_CHANNEL(2); //Arduino pin 24 as channel 2 +SOFTPWM_DEFINE_OBJECT(3); // Initialize 3 channels +bool blink_on = false; +uint32_t time = 0; +uint8_t lightColour = 0; // 0 is off +uint8_t selfSetColour[3] = { 0 }; + + +//Others +uint8_t CPU_CoreTemp = 0; +bool sonar = false; +static uint32_t pmb1_timeout = 0; +static uint32_t pmb2_timeout = 0; +static uint32_t sbc_timeout = 0; +static uint32_t dna_timeout = 0; +static uint32_t heartbeat_loop = 0; +static uint32_t stats_loop = 0; +static uint16_t dna_pressure = 0; + +static uint32_t testing_time = 0; + +void setup() +{ + pinMode(SCREEN_CS, OUTPUT); //CS screen + digitalWrite(SCREEN_CS, HIGH); + pinMode(CAN_Chip_Select, OUTPUT); //CS CAN + digitalWrite(CAN_Chip_Select, HIGH); + + Serial.begin(115200); + Serial.println("Hi, I'm STB!"); + + //CAN init + CAN_init(); + Serial.println("CAN OK"); + CANSetMask(); + + //Screen init + screen.screen_init(); + Serial.println("Screen Ok"); + screen_prepare(); + + //Sensor init + Wire.begin(); + Serial.println("Sensors OK"); + InitialP = readInternalPressure(); + + //led init + led_init(); + Serial.println("LED OK"); + + for (int i = 0; i < HB_COUNT; i++) { + heartbeat_timeout[i] = millis(); + } +} + +void loop() +{ + reset_stats(); + update_ST_stats(); + + if ((millis() - loopTime) > SCREEN_LOOP) { // 1000ms + screen_update(); + update_heartbeat(); + loopTime = millis(); + } + + if (millis() - filter_loop > LPF_LOOP) { // 25ms + //externalPressureLPF(); + filter_loop = millis(); + } + + checkCANmsg(); + + publishCAN(); +} + +//=========================================== +// +// CAN FUNCTIONS +// +//=========================================== + +void CAN_init() { +START_INIT: + if (CAN_OK == CAN.begin(CAN_1000KBPS)) { // init can bus : baudrate = 1000k +#if DEBUG_MODE == NORMAL + Serial.println("CAN init ok!"); +#endif + } + else { +#if DEBUG_MODE == NORMAL + Serial.println("CAN init fail"); + Serial.println("Init CAN again"); + delay(1000); +#endif + goto START_INIT; + } +} + +void CANSetMask() { + /* + Truth table + mask filter id bit reject + 0 X X no + 1 0 0 no + 1 0 1 yes + 1 1 0 yes + 1 1 1 no + + Mask 0 connects to filt 0,1 + Mask 1 connects to filt 2,3,4,5 + + Mask decide which bit to check + Filt decide which bit to accept + */ + + CAN.init_Mask(0, 0, 0xA); // check 11XX + CAN.init_Mask(1, 0, 0xF); // check all bit + + CAN.init_Filt(0, 0, 0x8); // let 10XX pass (8, 9) + CAN.init_Filt(1, 0, 0xA); // let 11XX pass (10 to 15) + + CAN.init_Filt(2, 0, 0x3); // let 0011 pass (3) + CAN.init_Filt(3, 0, 0x4); // let 0100 pass (4) + CAN.init_Filt(4, 0, 0xF); // let 1111 pass (15) + CAN.init_Filt(5, 0, 0xF); // let 1111 pass (15) + +} + +/* Receive these CAN ID +3: Heartbeat: SBC, SBC_CAN, PCB, TB, ST, MANI, PMB1, PMB2 +4: Sonar trigger +9: LED +10: CAN_DNA_Stats +11: PMB1 stat1 +12: PMB1 stat2 +13: PMB2 stat1 +14: PMB2 stat2 +15: CPU Temp +*/ + +void checkCANmsg() { + if (CAN_MSGAVAIL == CAN.checkReceive()) { + CAN.readMsgBufID(&id, &len, buf); // read data, len: data length, buf: data buf + switch (CAN.getCanId()) { + case CAN_heartbeat: + { + uint32_t device = CAN.parseCANFrame(buf, 0, 1); + heartbeat_timeout[device] = millis(); + break; + } + case CAN_SONAR: + { + uint8_t temp = CAN.parseCANFrame(buf, 0, 1); + (temp == 1) ? sonar = true : sonar = false; + break; + } + case CAN_LED: + { + selfSetColour[0] = CAN.parseCANFrame(buf, 0, 1); + selfSetColour[1] = CAN.parseCANFrame(buf, 1, 1); + selfSetColour[2] = CAN.parseCANFrame(buf, 2, 1); +#ifdef SOFTPWM + setcolour(selfSetColour[0], selfSetColour[1], selfSetColour[2]); +#else + //led.colour(lightColour); +#endif + break; + } + case CAN_DNA_Stats: + internalStats[DNA_PRESS] = CAN.parseCANFrame(buf, 0, 1); + sbc_timeout = millis(); + break; + case CAN_PMB1_stats: + powerStats[BATT1_CURRENT] = CAN.parseCANFrame(buf, 0, 2); + powerStats[BATT1_VOLTAGE] = CAN.parseCANFrame(buf, 2, 2); + pmb1_timeout = millis(); + break; + case CAN_PMB1_stats2: + internalStats[PMB1_PRESS] = CAN.parseCANFrame(buf, 4, 1); + internalStats[PMB1_TEMP] = CAN.parseCANFrame(buf, 3, 1); + powerStats[BATT1_CAPACITY] = CAN.parseCANFrame(buf, 2, 1); + pmb1_timeout = millis(); + break; + case CAN_PMB2_stats: + powerStats[BATT2_CURRENT] = CAN.parseCANFrame(buf, 0, 2); + powerStats[BATT2_VOLTAGE] = CAN.parseCANFrame(buf, 2, 2); + pmb2_timeout = millis(); + break; + case CAN_PMB2_stats2: + internalStats[PMB2_PRESS] = CAN.parseCANFrame(buf, 4, 1); + internalStats[PMB2_TEMP] = CAN.parseCANFrame(buf, 3, 1); + powerStats[BATT2_CAPACITY] = CAN.parseCANFrame(buf, 2, 1); + pmb2_timeout = millis(); + break; + case CAN_CPU: + { + uint8_t temp[3] = { 0 }; + for (int i = 0; i < 3; i++) { + temp[i] = CAN.parseCANFrame(buf, i, 1); + } + uint8_t max = temp[0]; + for (int i = 1; i < 3; i++) { + if (temp[i] >= max) { + max = temp[i]; + } + } + CPU_CoreTemp = max; + internalStats[CPU_TEMP] = CPU_CoreTemp; + sbc_timeout = millis(); + break; + } + default: + break; + } + CAN.clearMsg(); + } +} + +//publish raw pressure, heartbeat and stats to CAN bus +void publishCAN() +{ + //publish raw external pressure reading every 50ms + if (millis() - pressure_loop > 50) { + publishCAN_pressure(); + pressure_loop = millis(); + } + + //publish heartbeat every 500ms + if (millis() - heartbeat_loop > 500) { + publishCAN_heartbeat(HEARTBEAT_ST); + heartbeat_loop = millis(); + } + + //publish ST stats every 1000ms + if (millis() - stats_loop > 1000) { + publishST_stats(); + stats_loop = millis(); + } +} + +void publishCAN_heartbeat(int device_id) +{ + id = CAN_heartbeat; + len = 1; + buf[0] = device_id; + CAN.sendMsgBuf(CAN_heartbeat, 0, 1, buf); +} + +void publishCAN_pressure() { + CAN.setupCANFrame(buf, 0, 2, rawExtPressure); + CAN.sendMsgBuf(CAN_pressure, 0, 2, buf); +} + +void publishST_stats() { + CAN.setupCANFrame(buf, 0, 1, temperature); + CAN.setupCANFrame(buf, 1, 1, humidity); + CAN.setupCANFrame(buf, 2, 1, IntPressure); + leak() ? CAN.setupCANFrame(buf, 3, 1, 1) : CAN.setupCANFrame(buf, 3, 1, 0); + CAN.sendMsgBuf(CAN_ST_stats, 0, 4, buf); +} + + +//========================================== +// +// LCD FUNCTIONS +// +//========================================== + +void screen_prepare() { + screen.set_cursor(0 + OFFSET, 0); + screen.write_string("Ext press:"); + screen.write_string("Int press:"); + screen.write_string("PMB1 press:"); + screen.write_string("PMB2 press:"); + screen.write_string("PMB1 temp:"); + screen.write_string("PMB2 temp:"); + screen.write_string("CPU temp:"); + screen.write_string("Humidity:"); + screen.write_string("ST temp:"); + screen.write_string("DNA press: "); + screen.write_string("SBC OK:"); + screen.write_string("SBC-CAN OK:"); + screen.write_string("PCB OK:"); + + screen.set_cursor(400 + OFFSET, 0); + screen.write_string("Batt1 capacity:"); + screen.write_string("Batt2 capacity:"); + screen.write_string("Batt1 current:"); + screen.write_string("Batt2 current:"); + screen.write_string("Batt1 voltage:"); + screen.write_string("Batt2 voltage:"); + screen.write_string("Thruster OK:"); + screen.write_string("Manipulator OK:"); + screen.write_string("PMB1 OK:"); + screen.write_string("PMB2 OK:"); +} + +void screen_update() { + // row height 35, increment_row() + screen.set_cursor(200 + OFFSET, 0); + for (int i = 0; i < INT_STAT_COUNT; i++) + { + screen.write_value_int(internalStats[i]); + } + + screen.set_cursor(645 + OFFSET, 0); + for (int i = 0; i < POWER_STAT_COUNT; i++) + { + if (i > BATT2_CAPACITY) { + screen.write_value_with_dp(powerStats[i], 1); + } + else { + screen.write_value_int(powerStats[i]); + } + } +} + +void update_heartbeat() +{ + // row height 35, increment_row() + int i; + screen.set_cursor(200 + OFFSET, 350); + for (i = 1; i < 4; i++) { + if (i != HEARTBEAT_ST) // Skip ST HB + { + if ((millis() - heartbeat_timeout[i]) > HB_TIMEOUT) { + screen.write_value_string("NO"); + } + else + screen.write_value_string("YES"); + } + } + + screen.set_cursor(645 + OFFSET, 210); + for (; i < 9; i++) { + if (i != HEARTBEAT_ST) { // Skip ST HB + if ((millis() - heartbeat_timeout[i]) > HB_TIMEOUT) { + screen.write_value_string("NO"); + } + else + screen.write_value_string("YES"); + } + } +} + +//reset pmb1 pmb2 and sbc stats +void reset_stats() +{ + reset_pmb1_stat(); + reset_pmb2_stat(); + reset_sbc_stat(); +} + +void reset_pmb1_stat() { + if ((millis() - pmb1_timeout) > STAT_TIMEOUT) { + internalStats[PMB1_PRESS] = 0xFFFF; + internalStats[PMB1_TEMP] = 0xFFFF; + powerStats[BATT1_CAPACITY] = 0xFFFF; + powerStats[BATT1_CURRENT] = 0xFFFF; + powerStats[BATT1_VOLTAGE] = 0xFFFF; + pmb1_timeout = millis(); + } +} + +void reset_pmb2_stat() { + if ((millis() - pmb2_timeout) > STAT_TIMEOUT) { + internalStats[PMB2_PRESS] = 0xFFFF; + internalStats[PMB2_TEMP] = 0xFFFF; + powerStats[BATT2_CAPACITY] = 0xFFFF; + powerStats[BATT2_CURRENT] = 0xFFFF; + powerStats[BATT2_VOLTAGE] = 0xFFFF; + pmb2_timeout = millis(); + } +} + +void reset_sbc_stat() { + if ((millis() - sbc_timeout) > STAT_TIMEOUT) { + internalStats[CPU_TEMP] = 0xFFFF; + internalStats[DNA_PRESS] = 0xFFFF; + sbc_timeout = millis(); + } +} + +//read Temperature, Humidity, External and Internal pressure +// and assign them to array for update +void update_ST_stats() { + readTempHumididty(); + rawExtPressure = readExternalPressure(); + internalStats[EXT_PRESS] = ExtPressure; + IntPressure = readInternalPressure(); + internalStats[INT_PRESS] = readInternalPressure(); + internalStats[HUMIDITY] = humidity; + internalStats[ST_TEMP] = temperature; +} + +//========================================== +// +// Sensor Functions +// +//========================================== + +//Return Internal Pressure +byte readInternalPressure() { + /* + VOUT = VS x (0.004 x P - 0.040)�� (Pressure Error x Temp Factor x 0.004 x VS) + VS = 5.1 �� 0.36 Vdc + */ + // internal raw value 9690 = 1010mb = 101kPa + ads.set_continuous_conv(1); + delay(ADS_DELAY); + uint16_t adc1 = ads.readADC_Continuous(); + + return (((double)adc1*0.0001875) / (Vref*0.0040) + 10); +} + +//Updates ExtPressure and return raw 16bit External Pressure reading +uint16_t readExternalPressure() { + // output 4-20mA range upto 50psi gauge pressure sensor -> 0psi @ sea level + // US381-000005-050PG + // ==> 1-5V as shunt resistor is 250.0ohm + ads.set_continuous_conv(0); + delay(ADS_DELAY); + uint16_t adc0 = ads.readADC_Continuous(); + // ADC Range: +/- 6.144V + // 1 psi = 6895 pascal + double voltage = ((double)adc0/32767) * 6.144; // 0 = 0V, 2^15 = 6.144V + double gauge_psi = ((voltage - 1) / 4) * 50; // 1-5V, 50psi range + double absolute_psi = gauge_psi + 14.6; // psi at sea level = 14.6psi + ExtPressure = (uint16_t)(absolute_psi * 6.895); // pressure in kpa + if (ExtPressure < 80 || ExtPressure > 350) { + ExtPressure = 0xFFFF; + } + return adc0; // for legacy reasons, return raw adc value +} + +//Filter External Pressure with LPF +void externalPressureLPF() { + //LPF filter for ext pressure sensor + uint16_t temp = readExternalPressure(); + if (temp != 0) + { + rawExtPressure = rawExtPressure + LPF_CONSTANT * (float)(temp - rawExtPressure); + } + ExtPressure = rawExtPressure; +} + +//Updates Temperature and Humidity +void readTempHumididty() { + // reading temp or humid takes 36.65ms, 2 takes 74ms + if (millis() - humidloop > 100) { + if (!readHumid) { + humid.measurementRequest(); + readHumid = true; + } + else { + humid.dataFetch(); + humidity = humid.getHumidity() + 0.5; + temperature = humid.getTemperature() + 0.5; + readHumid = false; + } + humidloop = millis(); + } +} + + +//Return bool to indicate whether is it leaking +//Blinks led if it is leaking +bool leak() { + bool leaking = false; + if ((InitialP - IntPressure > 10) || humidity > 85) { + leaking = true; + } + if (leaking) { + //colour(8); // 8 for white + blink(1, 8, 300); + } + return leaking; +} + +void sonar_init() { + pinMode(SONAR_IN, OUTPUT); + pinMode(SONAR_OUT, OUTPUT); + digitalWrite(SONAR_OUT, LOW); + + sonar ? digitalWrite(SONAR_IN, HIGH) : digitalWrite(SONAR_IN, LOW); +} + +//Updates hardware trigger of Sonar +void sonar_update() { + digitalWrite(SONAR_OUT, LOW); + sonar ? digitalWrite(SONAR_IN, HIGH) : digitalWrite(SONAR_IN, LOW); +} + +//========================================== +// +// LED Functions +// +//========================================== + +//Blinks through all colour +void led_init() { + PalatisSoftPWM.begin(200); //200Hz + for (int i = 0; i < 10; i++) { + colour(i); + delay(200); + } + colour(lightColour); +} + +void setcolour(int red, int green, int blue) { + PalatisSoftPWM.set(RED, red); + PalatisSoftPWM.set(GREEN, green); + PalatisSoftPWM.set(BLUE, blue); +} + +// 9 for off +void colour(int colour) +{ +/* 0 - Off #000000 + 1 - Red #FF0000 + 2 - Violet #EE82EE + 3 - Pink #FFCCFF + 4 - Blue #0000FF + 5 - Green #00FF00 + 6 - Cyan #00FFFF + 7 - Maroon #80000 + 8 - white #FFFFFF + 9 - Yellow #FFFF00 +*/ + switch (colour) + { + case 0://Off + setcolour(0, 0, 0); + break; + case 1://Red + setcolour(255, 0, 0); + break; + case 2://Violet + setcolour(238, 130, 238); + break; + case 3://Pink + setcolour(255, 204, 255); + break; + case 4://Blue + setcolour(0, 0, 255); + break; + case 5://Green + setcolour(0, 255, 0); + break; + case 6://Cyan + setcolour(0, 255, 255); + break; + case 7://Maroon + setcolour(128, 0, 0); + break; + case 8://White + setcolour(255, 255, 255); + break; + case 9://Yellow + setcolour(255, 255, 0); + break; + default: + setcolour(0, 0, 0); + break; + } +} + +// a for first colour, b for second colour, +// period in ms +void blink(uint8_t a, uint8_t b, uint32_t period) { + if (a > 10 || b > 10) { + return; + } + if (millis() - time > period) { + if (blink_on) { + colour(a); + } + else { + colour(b); + } + blink_on = !blink_on; + time = millis(); + } +} diff --git a/AUV-4/STB/main code/STB_4.0/MS5837.cpp b/AUV-4/STB/main code/STB_4.0/MS5837.cpp new file mode 100644 index 00000000..142f971d --- /dev/null +++ b/AUV-4/STB/main code/STB_4.0/MS5837.cpp @@ -0,0 +1,258 @@ +#include "MS5837.h" +#include + +const uint8_t MS5837_ADDR = 0x76; +const uint8_t MS5837_RESET = 0x1E; +const uint8_t MS5837_ADC_READ = 0x00; +const uint8_t MS5837_PROM_READ = 0xA0; +const uint8_t MS5837_CONVERT_D1_8192 = 0x4A; +const uint8_t MS5837_CONVERT_D2_8192 = 0x5A; + +const float MS5837::Pa = 100.0f; +const float MS5837::bar = 0.001f; +const float MS5837::mbar = 1.0f; + +const uint8_t MS5837::MS5837_30BA = 0; +const uint8_t MS5837::MS5837_02BA = 1; +const uint8_t MS5837::MS5837_UNRECOGNISED = 255; + +const uint8_t MS5837_02BA01 = 0x00; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0 +const uint8_t MS5837_02BA21 = 0x15; // Sensor version: From MS5837_02BA datasheet Version PROM Word 0 +const uint8_t MS5837_30BA26 = 0x1A; // Sensor version: From MS5837_30BA datasheet Version PROM Word 0 + +MS5837::MS5837() { + fluidDensity = 1029; +} + +bool MS5837::begin(TwoWire &wirePort) { + return (init(wirePort)); +} + +bool MS5837::init(TwoWire &wirePort) { + _i2cPort = &wirePort; //Grab which port the user wants us to use + + // Reset the MS5837, per datasheet + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_RESET); + _i2cPort->endTransmission(); + + // Wait for reset to complete + delay(10); + + // Read calibration values and CRC + for ( uint8_t i = 0 ; i < 7 ; i++ ) { + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_PROM_READ+i*2); + _i2cPort->endTransmission(); + + _i2cPort->requestFrom(MS5837_ADDR,2); + C[i] = (_i2cPort->read() << 8) | _i2cPort->read(); + } + + // Verify that data is correct with CRC + uint8_t crcRead = C[0] >> 12; + uint8_t crcCalculated = crc4(C); + + if ( crcCalculated != crcRead ) { + return false; // CRC fail + } + + uint8_t version = (C[0] >> 5) & 0x7F; // Extract the sensor version from PROM Word 0 + + // Set _model according to the sensor version + if (version == MS5837_02BA01) + { + _model = MS5837_02BA; + } + else if (version == MS5837_02BA21) + { + _model = MS5837_02BA; + } + else if (version == MS5837_30BA26) + { + _model = MS5837_30BA; + } + else + { + _model = MS5837_UNRECOGNISED; + } + // The sensor has passed the CRC check, so we should return true even if + // the sensor version is unrecognised. + // (The MS5637 has the same address as the MS5837 and will also pass the CRC check) + // (but will hopefully be unrecognised.) + return true; +} + +void MS5837::setModel(uint8_t model) { + _model = model; +} + +uint8_t MS5837::getModel() { + return (_model); +} + +void MS5837::setFluidDensity(float density) { + fluidDensity = density; +} + +void MS5837::read() { + //Check that _i2cPort is not NULL (i.e. has the user forgoten to call .init or .begin?) + if (_i2cPort == NULL) + { + return; + } + + // Request D1 conversion + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_CONVERT_D1_8192); + _i2cPort->endTransmission(); + + delay(20); // Max conversion time per datasheet + + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_ADC_READ); + _i2cPort->endTransmission(); + + _i2cPort->requestFrom(MS5837_ADDR,3); + D1_pres = 0; + D1_pres = _i2cPort->read(); + D1_pres = (D1_pres << 8) | _i2cPort->read(); + D1_pres = (D1_pres << 8) | _i2cPort->read(); + + // Request D2 conversion + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_CONVERT_D2_8192); + _i2cPort->endTransmission(); + + delay(20); // Max conversion time per datasheet + + _i2cPort->beginTransmission(MS5837_ADDR); + _i2cPort->write(MS5837_ADC_READ); + _i2cPort->endTransmission(); + + _i2cPort->requestFrom(MS5837_ADDR,3); + D2_temp = 0; + D2_temp = _i2cPort->read(); + D2_temp = (D2_temp << 8) | _i2cPort->read(); + D2_temp = (D2_temp << 8) | _i2cPort->read(); + + calculate(); +} + +void MS5837::calculate() { + // Given C1-C6 and D1, D2, calculated TEMP and P + // Do conversion first and then second order temp compensation + + int32_t dT = 0; + int64_t SENS = 0; + int64_t OFF = 0; + int32_t SENSi = 0; + int32_t OFFi = 0; + int32_t Ti = 0; + int64_t OFF2 = 0; + int64_t SENS2 = 0; + + // Terms called + dT = D2_temp-uint32_t(C[5])*256l; + if ( _model == MS5837_02BA ) { + SENS = int64_t(C[1])*65536l+(int64_t(C[3])*dT)/128l; + OFF = int64_t(C[2])*131072l+(int64_t(C[4])*dT)/64l; + P = (D1_pres*SENS/(2097152l)-OFF)/(32768l); + } else { + SENS = int64_t(C[1])*32768l+(int64_t(C[3])*dT)/256l; + OFF = int64_t(C[2])*65536l+(int64_t(C[4])*dT)/128l; + P = (D1_pres*SENS/(2097152l)-OFF)/(8192l); + } + + // Temp conversion + TEMP = 2000l+int64_t(dT)*C[6]/8388608LL; + + //Second order compensation + if ( _model == MS5837_02BA ) { + if((TEMP/100)<20){ //Low temp + Ti = (11*int64_t(dT)*int64_t(dT))/(34359738368LL); + OFFi = (31*(TEMP-2000)*(TEMP-2000))/8; + SENSi = (63*(TEMP-2000)*(TEMP-2000))/32; + } + } else { + if((TEMP/100)<20){ //Low temp + Ti = (3*int64_t(dT)*int64_t(dT))/(8589934592LL); + OFFi = (3*(TEMP-2000)*(TEMP-2000))/2; + SENSi = (5*(TEMP-2000)*(TEMP-2000))/8; + if((TEMP/100)<-15){ //Very low temp + OFFi = OFFi+7*(TEMP+1500l)*(TEMP+1500l); + SENSi = SENSi+4*(TEMP+1500l)*(TEMP+1500l); + } + } + else if((TEMP/100)>=20){ //High temp + Ti = 2*(dT*dT)/(137438953472LL); + OFFi = (1*(TEMP-2000)*(TEMP-2000))/16; + SENSi = 0; + } + } + + OFF2 = OFF-OFFi; //Calculate pressure and temp second order + SENS2 = SENS-SENSi; + + TEMP = (TEMP-Ti); + + if ( _model == MS5837_02BA ) { + P = (((D1_pres*SENS2)/2097152l-OFF2)/32768l); + } else { + P = (((D1_pres*SENS2)/2097152l-OFF2)/8192l); + } +} + +float MS5837::pressure(float conversion) { + if ( _model == MS5837_02BA ) { + return P*conversion/100.0f; + } + else { + return P*conversion/10.0f; + } +} + +float MS5837::temperature() { + return TEMP/100.0f; +} + +// The pressure sensor measures absolute pressure, so it will measure the atmospheric pressure + water pressure +// We subtract the atmospheric pressure to calculate the depth with only the water pressure +// The average atmospheric pressure of 101300 pascal is used for the calcuation, but atmospheric pressure varies +// If the atmospheric pressure is not 101300 at the time of reading, the depth reported will be offset +// In order to calculate the correct depth, the actual atmospheric pressure should be measured once in air, and +// that value should subtracted for subsequent depth calculations. +float MS5837::depth() { + return (pressure(MS5837::Pa)-101300)/(fluidDensity*9.80665); +} + +float MS5837::altitude() { + return (1-pow((pressure()/1013.25),.190284))*145366.45*.3048; +} + + +uint8_t MS5837::crc4(uint16_t n_prom[]) { + uint16_t n_rem = 0; + + n_prom[0] = ((n_prom[0]) & 0x0FFF); + n_prom[7] = 0; + + for ( uint8_t i = 0 ; i < 16; i++ ) { + if ( i%2 == 1 ) { + n_rem ^= (uint16_t)((n_prom[i>>1]) & 0x00FF); + } else { + n_rem ^= (uint16_t)(n_prom[i>>1] >> 8); + } + for ( uint8_t n_bit = 8 ; n_bit > 0 ; n_bit-- ) { + if ( n_rem & 0x8000 ) { + n_rem = (n_rem << 1) ^ 0x3000; + } else { + n_rem = (n_rem << 1); + } + } + } + + n_rem = ((n_rem >> 12) & 0x000F); + + return n_rem ^ 0x00; +} \ No newline at end of file diff --git a/AUV-4/STB/main code/STB_4.0/MS5837.h b/AUV-4/STB/main code/STB_4.0/MS5837.h new file mode 100644 index 00000000..0a910dff --- /dev/null +++ b/AUV-4/STB/main code/STB_4.0/MS5837.h @@ -0,0 +1,103 @@ +/* Blue Robotics Arduino MS5837-30BA Pressure/Temperature Sensor Library +------------------------------------------------------------ +Title: Blue Robotics Arduino MS5837-30BA Pressure/Temperature Sensor Library +Description: This library provides utilities to communicate with and to +read data from the Measurement Specialties MS5837-30BA pressure/temperature +sensor. +Authors: Rustom Jehangir, Blue Robotics Inc. + Adam Šimko, Blue Robotics Inc. +------------------------------- +The MIT License (MIT) +Copyright (c) 2015 Blue Robotics Inc. +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 MS5837_H_BLUEROBOTICS +#define MS5837_H_BLUEROBOTICS + +#include "Arduino.h" +#include + +class MS5837 { +public: + static const float Pa; + static const float bar; + static const float mbar; + + static const uint8_t MS5837_30BA; + static const uint8_t MS5837_02BA; + static const uint8_t MS5837_UNRECOGNISED; + + MS5837(); + + bool init(TwoWire &wirePort = Wire); + bool begin(TwoWire &wirePort = Wire); // Calls init() + + /** Set model of MS5837 sensor. Valid options are MS5837::MS5837_30BA (default) + * and MS5837::MS5837_02BA. + */ + void setModel(uint8_t model); + uint8_t getModel(); + + /** Provide the density of the working fluid in kg/m^3. Default is for + * seawater. Should be 997 for freshwater. + */ + void setFluidDensity(float density); + + /** The read from I2C takes up to 40 ms, so use sparingly is possible. + */ + void read(); + + /** Pressure returned in mbar or mbar*conversion rate. + */ + float pressure(float conversion = 1.0f); + + /** Temperature returned in deg C. + */ + float temperature(); + + /** Depth returned in meters (valid for operation in incompressible + * liquids only. Uses density that is set for fresh or seawater. + */ + float depth(); + + /** Altitude returned in meters (valid for operation in air only). + */ + float altitude(); + +private: + + //This stores the requested i2c port + TwoWire * _i2cPort; + + uint16_t C[8]; + uint32_t D1_pres, D2_temp; + int32_t TEMP; + int32_t P; + uint8_t _model; + + float fluidDensity; + + /** Performs calculations per the sensor data sheet for conversion and + * second order compensation. + */ + void calculate(); + + uint8_t crc4(uint16_t n_prom[]); +}; + +#endif \ No newline at end of file