├── .codespellrc ├── .github ├── dependabot.yml └── workflows │ ├── spell-check.yml │ ├── check-arduino.yml │ ├── report-size-deltas.yml │ ├── compile-examples.yml │ └── sync-labels.yml ├── library.properties ├── src ├── Arduino_PortentaMachineControl.h ├── RtcControllerClass.cpp ├── ProgrammableDINClass.cpp ├── ProgrammableDIOClass.cpp ├── USBClass.cpp ├── utility │ ├── RTC │ │ ├── PCF8563T.h │ │ └── PCF8563T.cpp │ ├── MAX31865 │ │ ├── MAX31865.h │ │ └── MAX31865.cpp │ ├── ioexpander │ │ ├── ArduinoIOExpander.h │ │ ├── ArduinoIOExpander.cpp │ │ └── TCA6424A.h │ ├── THERMOCOUPLE │ │ ├── MAX31855.h │ │ └── MAX31855.cpp │ └── QEI │ │ ├── QEI.h │ │ └── QEI.cpp ├── CANCommClass.cpp ├── RtcControllerClass.h ├── ProgrammableDINClass.h ├── EncoderClass.cpp ├── AnalogOutClass.cpp ├── pins_mc.h ├── AnalogOutClass.h ├── ProgrammableDIOClass.h ├── AnalogInClass.h ├── DigitalOutputsClass.cpp ├── USBClass.h ├── RS485CommClass.cpp ├── TCTempProbeClass.cpp ├── RTDTempProbeClass.cpp ├── TCTempProbeClass.h ├── RTDTempProbeClass.h ├── CANCommClass.h ├── EncoderClass.h ├── AnalogInClass.cpp ├── DigitalOutputsClass.h └── RS485CommClass.h ├── examples ├── USB_host │ ├── USB_host.ino │ └── TUSB_helpers.h ├── CAN │ ├── ReadCan │ │ └── ReadCan.ino │ └── WriteCan │ │ └── WriteCan.ino ├── Encoders │ └── Encoders.ino ├── Analog_Out │ └── Analog_Out.ino ├── Analog_input │ ├── Analog_input_0_10V │ │ └── Analog_input_0_10V.ino │ ├── Analog_input_4_20mA │ │ └── Analog_input_4_20mA.ino │ ├── Analog_input_NTC │ │ └── Analog_input_NTC.ino │ └── Fast_Analog_input_0_10V │ │ └── Fast_Analog_input_0_10V.ino ├── Temp_probes_Thermocouples │ └── Temp_probes_Thermocouples.ino ├── RS485_halfduplex │ └── RS485_halfduplex.ino ├── RTC_Alarm │ └── RTC_Alarm.ino ├── RTC │ └── RTC.ino ├── RS485_fullduplex │ └── RS485_fullduplex.ino ├── RS232 │ └── RS232.ino ├── Digital_programmable │ ├── Digital_input │ │ └── Digital_input.ino │ └── GPIO_programmable │ │ └── GPIO_programmable.ino ├── Ethernet │ └── Ethernet.ino ├── Digital_output │ └── Digital_output.ino └── Temp_probes_RTD │ └── Temp_probes_RTD.ino ├── README.md ├── keywords.txt └── docs └── README.md /.codespellrc: -------------------------------------------------------------------------------- 1 | # See: https://github.com/codespell-project/codespell#using-a-config-file 2 | [codespell] 3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 4 | ignore-words-list = , 5 | check-filenames = 6 | check-hidden = 7 | skip = ./.git,./src/utility/ioexpander/I2Cdev.*,./src/utility/ioexpander/TCA6424A.*,./src/utility/QEI,./src/utility/RS485, 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_PortentaMachineControl 2 | version=1.0.4 3 | author=Arduino 4 | maintainer=Arduino 5 | sentence=Arduino Library for Portenta Machine Control (PMC) 6 | paragraph=The Portenta Machine Control Library is a C++ library designed to efficiently manage the functionalities of the Portenta Machine Control board. 7 | category=Communication 8 | url=https://github.com/arduino-libraries/Arduino_PortentaMachineControl 9 | architectures=mbed, mbed_portenta 10 | includes=Arduino_PortentaMachineControl.h 11 | depends=ArduinoRS485 12 | -------------------------------------------------------------------------------- /src/Arduino_PortentaMachineControl.h: -------------------------------------------------------------------------------- 1 | #ifndef __ARDUINO_PORTENTA_MACHINE_CONTROL_H 2 | #define __ARDUINO_PORTENTA_MACHINE_CONTROL_H 3 | 4 | #include "AnalogInClass.h" 5 | #include "AnalogOutClass.h" 6 | #include "DigitalOutputsClass.h" 7 | #include "ProgrammableDIOClass.h" 8 | #include "ProgrammableDINClass.h" 9 | #include "RTDTempProbeClass.h" 10 | #include "TCTempProbeClass.h" 11 | #include "RtcControllerClass.h" 12 | #include "USBClass.h" 13 | #include "EncoderClass.h" 14 | #include "CANCommClass.h" 15 | #include "RS485CommClass.h" 16 | 17 | #endif /* __ARDUINO_PORTENTA_MACHINE_CONTROL_H */ 18 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | spellcheck: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v6 20 | 21 | - name: Spell check 22 | uses: codespell-project/actions-codespell@master 23 | -------------------------------------------------------------------------------- /src/RtcControllerClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RtcControllerClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the RtcControllerClass, a wrapper for the PCF8563T RTC of the Portenta Machine Control. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "RtcControllerClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | RtcControllerClass::RtcControllerClass(PinName int_pin) 12 | : _int{int_pin} 13 | { 14 | pinMode(_int, INPUT_PULLUP); 15 | } 16 | 17 | RtcControllerClass::~RtcControllerClass() 18 | { } 19 | 20 | RtcControllerClass MachineControl_RTCController; 21 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /src/ProgrammableDINClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ProgrammableDINClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Programmable Digital Input connector of the Portenta Machine Control library. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "ProgrammableDINClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | ProgrammableDINClass::ProgrammableDINClass() 12 | { } 13 | 14 | ProgrammableDINClass::~ProgrammableDINClass() 15 | { } 16 | 17 | bool ProgrammableDINClass::begin() { 18 | ArduinoIOExpanderClass::begin(DIN_ADD); 19 | 20 | return true; 21 | } 22 | 23 | ProgrammableDINClass MachineControl_DigitalInputs; 24 | /**** END OF FILE ****/ 25 | -------------------------------------------------------------------------------- /examples/USB_host/USB_host.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - USB Port Usage Example 3 | * 4 | * This example shows how to utilize the USB port on the Portenta Machine Control. 5 | * 6 | * Circuit: 7 | * - Portenta H7 8 | * - Portenta Machine Control 9 | * - USB device (e.g., keyboard, mouse, etc.) 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | #include 19 | #include "TUSB_helpers.h" 20 | 21 | REDIRECT_STDOUT_TO(Serial); 22 | 23 | USBHost usb; 24 | 25 | void setup() { 26 | Serial1.begin(9600); 27 | MachineControl_USBController.begin(); 28 | 29 | usb.Init(USB_CORE_ID_FS, class_table); 30 | } 31 | 32 | void loop() { 33 | usb.Task(); 34 | } -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v6 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v2 23 | with: 24 | compliance: specification 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library 29 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/report-size-deltas.ya?ml" 8 | schedule: 9 | # Run at the minimum interval allowed by GitHub Actions. 10 | # Note: GitHub Actions periodically has outages which result in workflow failures. 11 | # In this event, the workflows will start passing again once the service recovers. 12 | - cron: "*/5 * * * *" 13 | workflow_dispatch: 14 | repository_dispatch: 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Comment size deltas reports to PRs 21 | uses: arduino/report-size-deltas@v1 22 | with: 23 | # The name of the workflow artifact created by the sketch compilation workflow 24 | sketches-reports-source: sketches-reports 25 | -------------------------------------------------------------------------------- /examples/CAN/ReadCan/ReadCan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - CAN Read Example 3 | * 4 | * This sketch shows the usage of the CAN transceiver on the Machine Control 5 | * and demonstrates how to receive data from the RX CAN channel. 6 | * 7 | * Circuit: 8 | * - Portenta H7 9 | * - Portenta Machine Control 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | while (!Serial) { 21 | ; // wait for serial port to connect. 22 | } 23 | 24 | if (!MachineControl_CANComm.begin(CanBitRate::BR_500k)) { 25 | Serial.println("CAN init failed."); 26 | while(1) ; 27 | } 28 | } 29 | 30 | void loop() { 31 | if (MachineControl_CANComm.available()) { 32 | CanMsg const msg = MachineControl_CANComm.read(); 33 | Serial.println(msg); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🏭 Library for Portenta Machine Control 2 | 3 | > [!IMPORTANT] 4 | > 5 | > This library is an upgraded and improved version designed to replace the deprecated [Arduino_MachineControl](https://github.com/arduino-libraries/Arduino_MachineControl). If you need assistance with migration, refer to the [migration guide](https://docs.arduino.cc/tutorials/portenta-machine-control/pmc-arduino-library). 6 | > 7 | > Please note that all the [examples](./examples/) provided in this repository are not compatible with the deprecated library. 8 | 9 | The Portenta Machine Control library enables efficient management of the features of the Portenta Machine Control board. 10 | 11 | The Arduino Portenta Machine Control is a versatile industrial unit for driving machinery. It offers soft-PLC control, diverse I/O options, and flexible network connectivity. 12 | 13 | 📦 For more information about this product, please visit the [product page](https://www.arduino.cc/pro/hardware/product/portenta-machine-control) and refer to the [user manual](https://docs.arduino.cc/tutorials/portenta-machine-control/user-manual/). 14 | 15 | 📖 For more information about this library please read the documentation [here](./docs/). 16 | -------------------------------------------------------------------------------- /src/ProgrammableDIOClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ProgrammableDIOClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Programmable Digital IO connector of the Portenta Machine Control library. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "ProgrammableDIOClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | ProgrammableDIOClass::ProgrammableDIOClass(PinName latch_pin) 12 | : _latch{latch_pin} 13 | { } 14 | 15 | ProgrammableDIOClass::~ProgrammableDIOClass() 16 | { } 17 | 18 | bool ProgrammableDIOClass::begin(bool latch_mode) { 19 | ArduinoIOExpanderClass::begin(IO_ADD); 20 | 21 | pinMode(_latch, OUTPUT); 22 | 23 | if(latch_mode) { 24 | _setLatchMode(); 25 | } else { 26 | _setAutoRetryMode(); 27 | } 28 | 29 | return true; 30 | } 31 | 32 | void ProgrammableDIOClass::_setLatchMode() { 33 | digitalWrite(_latch, HIGH); 34 | } 35 | 36 | void ProgrammableDIOClass::_setAutoRetryMode() { 37 | digitalWrite(_latch, LOW); 38 | } 39 | 40 | ProgrammableDIOClass MachineControl_DigitalProgrammables; 41 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /src/USBClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file USBClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the USB Controller of the Portenta Machine Control. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "USBClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | USBClass::USBClass(PinName power_pin, PinName usbflag_pin) 12 | : _power{power_pin}, _usbflag{usbflag_pin} 13 | { } 14 | 15 | USBClass::~USBClass() 16 | { } 17 | 18 | bool USBClass::begin() { 19 | pinMode(_power, OUTPUT); 20 | pinMode(_usbflag, INPUT); 21 | 22 | _enable(); 23 | 24 | return true; 25 | } 26 | 27 | void USBClass::end() { 28 | _disable(); 29 | } 30 | 31 | bool USBClass::getFaultStatus() { 32 | bool res = false; 33 | 34 | if (digitalRead(_usbflag) == LOW) { 35 | /* Active−low, asserted during overcurrent, overtemperature, or reverse−voltage conditions. */ 36 | res = true; 37 | } else { 38 | res = false; 39 | } 40 | 41 | return res; 42 | } 43 | 44 | void USBClass::_enable() { 45 | digitalWrite(_power, LOW); 46 | } 47 | 48 | void USBClass::_disable() { 49 | digitalWrite(_power, HIGH); 50 | } 51 | 52 | USBClass MachineControl_USBController; 53 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /src/utility/RTC/PCF8563T.h: -------------------------------------------------------------------------------- 1 | #ifndef _PCF8563T_H_INCLUDED 2 | #define _PCF8563T_H_INCLUDED 3 | 4 | #include "Arduino.h" 5 | #include "mbed.h" 6 | #include "time.h" 7 | #include "mbed_mktime.h" 8 | #include "Wire.h" 9 | #define RTC_INT PB_9 10 | class PCF8563TClass { 11 | 12 | public: 13 | PCF8563TClass(); 14 | bool begin(); 15 | void setYear(uint8_t years); 16 | void setMonth(uint8_t months); 17 | void setDay(uint8_t days); 18 | void setHours(uint8_t hours); 19 | void setMinutes(uint8_t minutes); 20 | void setSeconds(uint8_t seconds); 21 | 22 | uint8_t getYear(); 23 | uint8_t getMonth(); 24 | uint8_t getDay(); 25 | uint8_t getHours(); 26 | uint8_t getMinutes(); 27 | uint8_t getSeconds(); 28 | 29 | void setEpoch(); 30 | void setEpoch(uint8_t years, uint8_t months, uint8_t days, uint8_t hours, uint8_t minutes, uint8_t seconds); 31 | void setEpoch(time_t seconds); 32 | time_t getEpoch(); 33 | 34 | void enableAlarm(); 35 | void disableAlarm(); 36 | void clearAlarm(); 37 | void setMinuteAlarm(uint8_t minutes); 38 | void disableMinuteAlarm(); 39 | void setHourAlarm(uint8_t hours); 40 | void disableHourAlarm(); 41 | void setDayAlarm(uint8_t days); 42 | void disableDayAlarm(); 43 | 44 | 45 | private: 46 | void writeByte(uint8_t regAddres, uint8_t data); 47 | uint8_t readByte(uint8_t regAddres); 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /examples/Encoders/Encoders.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Encoder Read Example 3 | * 4 | * This sketch shows the functionality of the encoders on the Machine Control. 5 | * It demonstrates how to periodically read and interpret the values from the two encoder channels. 6 | * 7 | * Circuit: 8 | * - Portenta H7 9 | * - Portenta Machine Control 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | while (!Serial) { 21 | ; // wait for serial port to connect. 22 | } 23 | } 24 | 25 | void loop() { 26 | Serial.print("Encoder 0 State: "); 27 | Serial.println(MachineControl_Encoders.getCurrentState(0),BIN); 28 | Serial.print("Encoder 0 Pulses: "); 29 | Serial.println(MachineControl_Encoders.getPulses(0)); 30 | Serial.print("Encoder 0 Revolutions: "); 31 | Serial.println(MachineControl_Encoders.getRevolutions(0)); 32 | Serial.println(); 33 | 34 | Serial.print("Encoder 1 State: "); 35 | Serial.println(MachineControl_Encoders.getCurrentState(1),BIN); 36 | Serial.print("Encoder 1 Pulses: "); 37 | Serial.println(MachineControl_Encoders.getPulses(1)); 38 | Serial.print("Encoder 1 Revolutions: "); 39 | Serial.println(MachineControl_Encoders.getRevolutions(1)); 40 | Serial.println(); 41 | 42 | delay(25); 43 | } 44 | -------------------------------------------------------------------------------- /examples/CAN/WriteCan/WriteCan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - CAN Write Example 3 | * 4 | * This sketch shows the usage of the CAN transceiver on the Machine Control 5 | * and demonstrates how to transmit data from the TX CAN channel. 6 | * 7 | * Circuit: 8 | * - Portenta H7 9 | * - Portenta Machine Control 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | static uint32_t const CAN_ID = 13ul; 19 | static uint32_t msg_cnt = 0; 20 | 21 | void setup() { 22 | Serial.begin(9600); 23 | while (!Serial) { 24 | ; // wait for serial port to connect. 25 | } 26 | 27 | if (!MachineControl_CANComm.begin(CanBitRate::BR_500k)) { 28 | Serial.println("CAN init failed."); 29 | while(1) ; 30 | } 31 | } 32 | 33 | void loop() { 34 | /* Assemble the CAN message */ 35 | uint8_t const msg_data[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; 36 | CanMsg msg(CAN_ID, sizeof(msg_data), msg_data); 37 | 38 | /* Transmit the CAN message */ 39 | int const rc = MachineControl_CANComm.write(msg); 40 | if (rc <= 0) { 41 | Serial.print("CAN write failed with error code: "); 42 | Serial.println(rc); 43 | while(1) ; 44 | } 45 | 46 | Serial.println("CAN write message!"); 47 | 48 | /* Increase the message counter */ 49 | msg_cnt++; 50 | 51 | /* Only send one message per second */ 52 | delay(1000); 53 | } -------------------------------------------------------------------------------- /src/CANCommClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CANCommClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the CANCommClass used to initialize and interact with the CAN Bus communication protocol on the Portenta Machine Control board. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "CANCommClass.h" 9 | #include 10 | 11 | /* Functions -----------------------------------------------------------------*/ 12 | CANCommClass::CANCommClass(PinName can_tx_pin, PinName can_rx_pin, PinName can_stb_pin) : 13 | _can(can_rx_pin, can_tx_pin), _tx{can_tx_pin}, _rx{can_rx_pin}, _stb{can_stb_pin} 14 | { } 15 | 16 | CANCommClass::~CANCommClass() 17 | { } 18 | 19 | bool CANCommClass::begin(CanBitRate can_bitrate) { 20 | pinMode(PinNameToIndex(_stb), OUTPUT); 21 | 22 | _enable(); 23 | 24 | return _can.begin(can_bitrate); 25 | } 26 | 27 | int CANCommClass::write(CanMsg const & msg) { 28 | return _can.write(msg); 29 | } 30 | 31 | size_t CANCommClass::available() { 32 | return _can.available(); 33 | } 34 | 35 | CanMsg CANCommClass::read() { 36 | return _can.read(); 37 | } 38 | 39 | void CANCommClass::end() { 40 | _disable(); 41 | 42 | _can.end(); 43 | } 44 | 45 | void CANCommClass::_enable() { 46 | digitalWrite(PinNameToIndex(_stb), LOW); 47 | } 48 | 49 | void CANCommClass::_disable() { 50 | digitalWrite(PinNameToIndex(_stb), HIGH); 51 | } 52 | 53 | CANCommClass MachineControl_CANComm; 54 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ################################################ 2 | # Syntax Coloring Map For Arduino_MachineControl 3 | ################################################ 4 | # Class (KEYWORD1) 5 | ################################################ 6 | 7 | Arduino_MachineControl KEYWORD1 8 | 9 | MachineControl_AnalogIn KEYWORD1 10 | MachineControl_AnalogOut KEYWORD1 11 | MachineControl_CANComm KEYWORD1 12 | MachineControl_DigitalOutputs KEYWORD1 13 | MachineControl_Encoders KEYWORD1 14 | MachineControl_DigitalInputs KEYWORD1 15 | MachineControl_DigitalProgrammables KEYWORD1 16 | MachineControl_RS485Comm KEYWORD1 17 | MachineControl_RTDTempProbe KEYWORD1 18 | MachineControl_RTCController KEYWORD1 19 | MachineControl_TCTempProbe KEYWORD1 20 | MachineControl_USBController KEYWORD1 21 | 22 | ################################################ 23 | # Methods and Functions (KEYWORD2) 24 | ################################################ 25 | 26 | begin KEYWORD2 27 | end KEYWORD2 28 | 29 | read KEYWORD2 30 | 31 | setPeriod KEYWORD2 32 | write KEYWORD2 33 | 34 | available KEYWORD2 35 | 36 | writeAll KEYWORD2 37 | 38 | reset KEYWORD2 39 | getCurrentState KEYWORD2 40 | getPulses KEYWORD2 41 | getRevolutions KEYWORD2 42 | 43 | setModeRS232 KEYWORD2 44 | setYZTerm KEYWORD2 45 | setABTerm KEYWORD2 46 | setSlew KEYWORD2 47 | setFullDuplex KEYWORD2 48 | 49 | selectChannel KEYWORD2 50 | 51 | getFaultStatus KEYWORD2 52 | 53 | ################################################ 54 | # Constants (LITERAL1) 55 | ################################################ 56 | -------------------------------------------------------------------------------- /examples/Analog_Out/Analog_Out.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Analog out 3 | * 4 | * This example demonstrates the utilization of the Analog out channels on the Machine Control. 5 | * The example configures the channels' PWM period in the setup and then iterates through voltage 6 | * output values from 0V to 10.5V in a loop. 7 | * 8 | * The circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * 12 | * This example code is in the public domain. 13 | * Copyright (c) 2024 Arduino 14 | * SPDX-License-Identifier: MPL-2.0 15 | */ 16 | 17 | #include 18 | 19 | #define PERIOD_MS 4 /* 4ms - 250Hz */ 20 | 21 | float voltage = 0; 22 | 23 | void setup() { 24 | Serial.begin(9600); 25 | while (!Serial) { 26 | ; // wait for serial port to connect. 27 | } 28 | 29 | MachineControl_AnalogOut.begin(); 30 | 31 | MachineControl_AnalogOut.setPeriod(0, PERIOD_MS); 32 | MachineControl_AnalogOut.setPeriod(1, PERIOD_MS); 33 | MachineControl_AnalogOut.setPeriod(2, PERIOD_MS); 34 | MachineControl_AnalogOut.setPeriod(3, PERIOD_MS); 35 | } 36 | 37 | void loop() { 38 | MachineControl_AnalogOut.write(0, voltage); 39 | MachineControl_AnalogOut.write(1, voltage); 40 | MachineControl_AnalogOut.write(2, voltage); 41 | MachineControl_AnalogOut.write(3, voltage); 42 | 43 | Serial.println("All channels set at " + String(voltage) + "V"); 44 | 45 | voltage = voltage + 0.1; 46 | /* Maximum output value is 10.5V */ 47 | if (voltage >= 10.5) { 48 | voltage = 0; 49 | delay(100); /* Additional 100 ms delay introduced to manage 10.5V -> 0V fall time of 150 ms */ 50 | } 51 | 52 | delay(100); 53 | } 54 | -------------------------------------------------------------------------------- /examples/Analog_input/Analog_input_0_10V/Analog_input_0_10V.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Analog in 0-10 V 3 | * 4 | * This example provides the voltage value acquired by the Machine Control. 5 | * For each channel of the ANALOG IN connector, there is a resistor divider made by a 100k and 39k, 6 | * the input voltage is divided by a ratio of 0.28. 7 | * Maximum input voltage is 10V. 8 | * To use the 0V-10V functionality, a 24V supply on the PWR SUPPLY connector is necessary. 9 | * 10 | * The circuit: 11 | * - Portenta H7 12 | * - Portenta Machine Control 13 | * 14 | * This example code is in the public domain. 15 | * Copyright (c) 2024 Arduino 16 | * SPDX-License-Identifier: MPL-2.0 17 | */ 18 | 19 | #include 20 | 21 | const float RES_DIVIDER = 0.28057; 22 | const float REFERENCE = 3.0; 23 | 24 | void setup() { 25 | Serial.begin(9600); 26 | while (!Serial) { 27 | ; // wait for serial port to connect. 28 | } 29 | 30 | MachineControl_AnalogIn.begin(SensorType::V_0_10); 31 | } 32 | 33 | void loop() { 34 | float raw_voltage_ch0 = MachineControl_AnalogIn.read(0); 35 | float voltage_ch0 = (raw_voltage_ch0 * REFERENCE) / 65535 / RES_DIVIDER; 36 | Serial.print("Voltage CH0: "); 37 | Serial.print(voltage_ch0, 3); 38 | Serial.println("V"); 39 | 40 | float raw_voltage_ch1 = MachineControl_AnalogIn.read(1); 41 | float voltage_ch1 = (raw_voltage_ch1 * REFERENCE) / 65535 / RES_DIVIDER; 42 | Serial.print("Voltage CH1: "); 43 | Serial.print(voltage_ch1, 3); 44 | Serial.println("V"); 45 | 46 | float raw_voltage_ch2 = MachineControl_AnalogIn.read(2); 47 | float voltage_ch2 = (raw_voltage_ch2 * REFERENCE) / 65535 / RES_DIVIDER; 48 | Serial.print("Voltage CH2: "); 49 | Serial.print(voltage_ch2, 3); 50 | Serial.println("V"); 51 | 52 | Serial.println(); 53 | delay(250); 54 | } 55 | -------------------------------------------------------------------------------- /src/RtcControllerClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RtcControllerClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the RtcControllerClass, a wrapper for the PCF8563T RTC of the Portenta Machine Control. 5 | * 6 | * This header file defines the RtcControllerClass, which serves as a wrapper for the PCF8563TClass. 7 | * The class allows setting and getting the time to/from the PCF8563T RTC (Real-Time Clock). 8 | */ 9 | 10 | #ifndef __RTC_CONTROLLER_CLASS_H 11 | #define __RTC_CONTROLLER_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include "utility/RTC/PCF8563T.h" 15 | #include 16 | #include 17 | #include "pins_mc.h" 18 | 19 | /* Class ----------------------------------------------------------------------*/ 20 | 21 | /** 22 | * @class RtcControllerClass 23 | * @brief Class for controlling the PCF8563T RTC. 24 | * 25 | * This class serves as a wrapper for the PCF8563TClass and provides additional functionalities 26 | * for controlling the Real-Time Clock on the target platform. 27 | */ 28 | class RtcControllerClass : public PCF8563TClass { 29 | public: 30 | /** 31 | * @brief Construct a RtcControllerClass object with an interrupt pin. 32 | * 33 | * This constructor initializes a RtcControllerClass object. 34 | * 35 | * @param int_pin The pin number for the interrupt pin (default is PB_9). 36 | */ 37 | RtcControllerClass(PinName int_pin = MC_RTC_INT_PIN); 38 | 39 | /** 40 | * @brief Destructor for the RtcControllerClass. 41 | * 42 | * This destructor is responsible for cleaning up any resources associated with the RtcControllerClass. 43 | */ 44 | ~RtcControllerClass(); 45 | 46 | private: 47 | PinName _int; // Pin for the interrupt 48 | }; 49 | 50 | extern RtcControllerClass MachineControl_RTCController; 51 | 52 | #endif /* __RTC_CONTROLLER_CLASS_H */ 53 | -------------------------------------------------------------------------------- /src/ProgrammableDINClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ProgrammableDINClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Programmable Digital Input connector of the Portenta Machine Control library. 5 | * 6 | * This library allows interfacing with the IO Expander and configuring the digital inputs. 7 | */ 8 | 9 | #ifndef __PROGRAMMABLE_DIN_CLASS_H 10 | #define __PROGRAMMABLE_DIN_CLASS_H 11 | 12 | /* Includes -------------------------------------------------------------------*/ 13 | #include "utility/ioexpander/ArduinoIOExpander.h" 14 | #include 15 | #include 16 | 17 | /* Class ----------------------------------------------------------------------*/ 18 | 19 | /** 20 | * @class ProgrammableDINClass 21 | * @brief Class for the Programmable Digital Input connector of the Portenta Machine Control. 22 | * 23 | * This class extends the ArduinoIOExpanderClass to interface with the IO Expander and provides methods to configure digital inputs. 24 | */ 25 | class ProgrammableDINClass : public ArduinoIOExpanderClass { 26 | public: 27 | /** 28 | * @brief Construct a ProgrammableDINClass object. 29 | * 30 | * This constructor initializes a ProgrammableDINClass object. 31 | */ 32 | ProgrammableDINClass(); 33 | 34 | /** 35 | * @brief Destruct the ProgrammableDINClass object. 36 | * 37 | * This destructor releases any resources used by the ProgrammableDINClass object. 38 | */ 39 | ~ProgrammableDINClass(); 40 | 41 | /** 42 | * @brief Initialize the ProgrammableDIN module. 43 | * 44 | * Test connection with the IOExpander and set all the pins to the default mode. 45 | * 46 | * @return true If the ProgrammableDIN module is successfully initialized, false otherwise. 47 | */ 48 | bool begin(); 49 | }; 50 | 51 | extern ProgrammableDINClass MachineControl_DigitalInputs; 52 | 53 | #endif /* __PROGRAMMABLE_DIN_CLASS_H */ -------------------------------------------------------------------------------- /examples/Temp_probes_Thermocouples/Temp_probes_Thermocouples.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Thermocouples Temperature Reading Example 3 | * 4 | * This example reads the temperatures measured by the thermocouples 5 | * connected to the temp probe inputs on the Portenta Machine Control Carrier. 6 | * It prints the temperature values to the Serial Monitor every second. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * - Two K Type thermocouple temperature sensors connected to TEMP PROBES CH0 and CH1 12 | * - A J Type thermocouple temperature sensor connected to TEMP PROBES CH3 13 | * 14 | * This example code is in the public domain. 15 | * Copyright (c) 2024 Arduino 16 | * SPDX-License-Identifier: MPL-2.0 17 | */ 18 | 19 | #include 20 | 21 | void setup() { 22 | Serial.begin(9600); 23 | while (!Serial) { 24 | ; 25 | } 26 | 27 | // Initialize temperature probes 28 | MachineControl_TCTempProbe.begin(); 29 | Serial.println("Temperature probes initialization done"); 30 | } 31 | 32 | void loop() { 33 | //Set CH0, has internal 150 ms delay 34 | MachineControl_TCTempProbe.selectChannel(0); 35 | //Take CH0 measurement 36 | float temp_ch0 = MachineControl_TCTempProbe.readTemperature(); 37 | Serial.print("Temperature CH0 [°C]: "); 38 | Serial.print(temp_ch0); 39 | Serial.println(); 40 | 41 | //Set CH1, has internal 150 ms delay 42 | MachineControl_TCTempProbe.selectChannel(1); 43 | //Take CH1 measurement 44 | float temp_ch1 = MachineControl_TCTempProbe.readTemperature(); 45 | Serial.print("Temperature CH1 [°C]: "); 46 | Serial.print(temp_ch1); 47 | Serial.println(); 48 | 49 | //Set CH2, has internal 150 ms delay 50 | MachineControl_TCTempProbe.selectChannel(2); 51 | //Take CH2 measurement 52 | float temp_ch2 = MachineControl_TCTempProbe.readTemperature(); 53 | Serial.print("Temperature CH2 [°C]: "); 54 | Serial.print(temp_ch2); 55 | Serial.println(); 56 | 57 | Serial.println(); 58 | } -------------------------------------------------------------------------------- /examples/Analog_input/Analog_input_4_20mA/Analog_input_4_20mA.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Analog in 4-20 mA 3 | * 4 | * This example provides the current value acquired by the Machine Control. 5 | * For each channel of the ANALOG IN connector, a 120 ohm resistor to GND is present. 6 | * The current from the 4-20mA sensor passes through it, generating a voltage 7 | * that is sampled by the Portenta's ADC. 8 | * To use the 4-20mA functionality, a 24V supply on the PWR SUPPLY connector is required. 9 | * 10 | * The circuit: 11 | * - Portenta H7 12 | * - Portenta Machine Control 13 | * 14 | * This example code is in the public domain. 15 | * Copyright (c) 2024 Arduino 16 | * SPDX-License-Identifier: MPL-2.0 17 | */ 18 | 19 | #include 20 | 21 | #define SENSE_RES 120 22 | 23 | const float REFERENCE = 3.0; 24 | 25 | void setup() { 26 | Serial.begin(9600); 27 | while (!Serial) { 28 | ; // wait for serial port to connect. 29 | } 30 | 31 | MachineControl_AnalogIn.begin(SensorType::MA_4_20); 32 | } 33 | 34 | void loop() { 35 | float raw_voltage_ch0 = MachineControl_AnalogIn.read(0); 36 | float voltage_ch0 = (raw_voltage_ch0 * REFERENCE) / 65535; 37 | float current_ch0 = (voltage_ch0 / SENSE_RES) * 1000; 38 | Serial.print("Measured Current CH0: "); 39 | Serial.print(current_ch0); 40 | Serial.println("mA"); 41 | 42 | float raw_voltage_ch1 = MachineControl_AnalogIn.read(1); 43 | float voltage_ch1 = (raw_voltage_ch1 * REFERENCE) / 65535; 44 | float current_ch1 = (voltage_ch1 / SENSE_RES) * 1000; 45 | Serial.print("Measured Current CH1: "); 46 | Serial.print(current_ch1); 47 | Serial.println("mA"); 48 | 49 | float raw_voltage_ch2 = MachineControl_AnalogIn.read(2); 50 | float voltage_ch2 = (raw_voltage_ch2 * REFERENCE) / 65535; 51 | float current_ch2 = (voltage_ch2 / SENSE_RES) * 1000; 52 | Serial.print("Measured Current CH2: "); 53 | Serial.print(current_ch2); 54 | Serial.println("mA"); 55 | 56 | Serial.println(); 57 | delay(250); 58 | } 59 | -------------------------------------------------------------------------------- /src/EncoderClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EncoderClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the encoder module of the Portenta Machine Control. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "EncoderClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | EncoderClass::EncoderClass(PinName enc0_A_pin, PinName enc0_B_pin, PinName enc0_I_pin, 12 | PinName enc1_A_pin, PinName enc1_B_pin, PinName enc1_I_pin) 13 | : _enc0(enc0_A_pin, enc0_B_pin, enc0_I_pin, 0), 14 | _enc1(enc1_A_pin, enc1_B_pin, enc1_I_pin, 0) 15 | { } 16 | 17 | EncoderClass::~EncoderClass() 18 | { } 19 | 20 | void EncoderClass::reset(int channel) { 21 | switch (channel) { 22 | case 0: 23 | _enc0.reset(); 24 | break; 25 | case 1: 26 | _enc1.reset(); 27 | break; 28 | default: 29 | return; 30 | } 31 | } 32 | 33 | int EncoderClass::getCurrentState(int channel) { 34 | switch (channel) { 35 | case 0: 36 | return _enc0.getCurrentState(); 37 | case 1: 38 | return _enc1.getCurrentState(); 39 | default: 40 | return -1; 41 | } 42 | } 43 | 44 | int EncoderClass::getPulses(int channel) { 45 | switch (channel) { 46 | case 0: 47 | return _enc0.getPulses(); 48 | case 1: 49 | return _enc1.getPulses(); 50 | default: 51 | return -1; 52 | } 53 | } 54 | 55 | int EncoderClass::getRevolutions(int channel) { 56 | switch (channel) { 57 | case 0: 58 | return _enc0.getRevolutions(); 59 | case 1: 60 | return _enc1.getRevolutions(); 61 | default: 62 | return -1; 63 | } 64 | } 65 | 66 | void EncoderClass::setEncoding(int channel, QEI::Encoding encoding) { 67 | switch (channel) { 68 | case 0: 69 | return _enc0.setEncoding(encoding); 70 | case 1: 71 | return _enc1.setEncoding(encoding); 72 | } 73 | } 74 | 75 | EncoderClass MachineControl_Encoders; 76 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /src/AnalogOutClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AnalogOutClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Analog OUT connector of the Portenta Machine Control library. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "AnalogOutClass.h" 9 | 10 | /* Private defines -----------------------------------------------------------*/ 11 | #define MCAO_MAX_VOLTAGE 10.5 12 | 13 | /* Functions -----------------------------------------------------------------*/ 14 | AnalogOutClass::AnalogOutClass(PinName ao0_pin, PinName ao1_pin, PinName ao2_pin, PinName ao3_pin) 15 | : _ao0{mbed::PwmOut(ao0_pin)}, _ao1{mbed::PwmOut(ao1_pin)}, _ao2{mbed::PwmOut(ao2_pin)}, _ao3{mbed::PwmOut(ao3_pin)} 16 | { } 17 | 18 | AnalogOutClass::~AnalogOutClass() 19 | { } 20 | 21 | bool AnalogOutClass::begin() { 22 | setPeriod(0, 2); //2ms - 500Hz 23 | setPeriod(1, 2); 24 | setPeriod(2, 2); 25 | setPeriod(3, 2); 26 | 27 | return true; 28 | } 29 | 30 | void AnalogOutClass::setPeriod(int channel, uint8_t period_ms) { 31 | switch (channel) { 32 | case 0: 33 | _ao0.period_ms(period_ms); 34 | break; 35 | case 1: 36 | _ao1.period_ms(period_ms); 37 | break; 38 | case 2: 39 | _ao2.period_ms(period_ms); 40 | break; 41 | case 3: 42 | _ao3.period_ms(period_ms); 43 | break; 44 | } 45 | } 46 | 47 | void AnalogOutClass::write(int channel, float voltage) { 48 | if (voltage < 0) { 49 | voltage = 0; 50 | } 51 | 52 | if (voltage > MCAO_MAX_VOLTAGE) { 53 | voltage = MCAO_MAX_VOLTAGE; 54 | } 55 | 56 | switch (channel) { 57 | case 0: 58 | _ao0.write(voltage / MCAO_MAX_VOLTAGE); 59 | break; 60 | case 1: 61 | _ao1.write(voltage / MCAO_MAX_VOLTAGE); 62 | break; 63 | case 2: 64 | _ao2.write(voltage / MCAO_MAX_VOLTAGE); 65 | break; 66 | case 3: 67 | _ao3.write(voltage / MCAO_MAX_VOLTAGE); 68 | break; 69 | } 70 | } 71 | 72 | AnalogOutClass MachineControl_AnalogOut; 73 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | name: Compile Examples 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/compile-examples.yml" 7 | - "library.properties" 8 | - "src/**" 9 | - "examples/**" 10 | push: 11 | paths: 12 | - ".github/workflows/compile-examples.yml" 13 | - "library.properties" 14 | - "src/**" 15 | - "examples/**" 16 | # Scheduled trigger checks for breakage caused by changes to external resources (libraries, platforms) 17 | schedule: 18 | # run every Tuesday at 3 AM UTC 19 | - cron: "0 3 * * 2" 20 | # workflow_dispatch event allows the workflow to be triggered manually 21 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch 22 | workflow_dispatch: 23 | # repository_dispatch event allows the workflow to be triggered via the GitHub API 24 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#repository_dispatch 25 | repository_dispatch: 26 | 27 | env: 28 | SKETCHES_REPORTS_PATH: sketches-reports 29 | SKETCHES_REPORTS_ARTIFACT_NAME: sketches-reports 30 | 31 | jobs: 32 | compile-examples: 33 | runs-on: ubuntu-latest 34 | 35 | steps: 36 | - name: Checkout repository 37 | uses: actions/checkout@v6 38 | 39 | - name: Compile example sketches 40 | uses: arduino/compile-sketches@v1 41 | with: 42 | fqbn: arduino:mbed_portenta:envie_m7 43 | platforms: | 44 | - name: arduino:mbed_portenta 45 | libraries: | 46 | - source-path: ./ 47 | - name: ArduinoRS485 48 | - name: Arduino_AdvancedAnalog 49 | sketch-paths: | 50 | - examples 51 | enable-deltas-report: true 52 | enable-warnings-report: true 53 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 54 | github-token: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | - name: Save memory usage change report as artifact 57 | uses: actions/upload-artifact@v5 58 | with: 59 | if-no-files-found: error 60 | path: ${{ env.SKETCHES_REPORTS_PATH }} 61 | name: ${{ env.SKETCHES_REPORTS_ARTIFACT_NAME }} 62 | -------------------------------------------------------------------------------- /examples/RS485_halfduplex/RS485_halfduplex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - RS485 Half Duplex Communication Example 3 | * 4 | * This sketch shows the usage of the SP335ECR1 on the Machine Control 5 | * as a half duplex (AB) RS485 interface. It demonstrates how to periodically send a string 6 | * on the RS485 TX channel. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * - A Slave device with RS485 interface 12 | * - Connect TXP to A(+) and TXN to B(-) 13 | * 14 | * This example code is in the public domain. 15 | * Copyright (c) 2024 Arduino 16 | * SPDX-License-Identifier: MPL-2.0 17 | */ 18 | 19 | #include "Arduino_PortentaMachineControl.h" 20 | 21 | constexpr unsigned long sendInterval { 1000 }; 22 | unsigned long sendNow { 0 }; 23 | unsigned long counter { 0 }; 24 | 25 | void setup() { 26 | Serial.begin(9600); 27 | while (!Serial) { 28 | ; 29 | } 30 | 31 | delay(1000); 32 | Serial.println("Start RS485 initialization"); 33 | 34 | // Set the PMC Communication Protocols to default config 35 | // RS485/RS232 default config is: 36 | // - RS485 mode 37 | // - Half Duplex 38 | // - No A/B and Y/Z 120 Ohm termination enabled 39 | // Enable the RS485/RS232 system 40 | MachineControl_RS485Comm.begin(115200, SERIAL_8N1, 0, 500); // Specify baudrate, serial config and preamble and postamble times for RS485 communication 41 | 42 | // Start in receive mode 43 | MachineControl_RS485Comm.receive(); 44 | 45 | Serial.println("Initialization done!"); 46 | } 47 | 48 | void loop() { 49 | if (MachineControl_RS485Comm.available()) 50 | Serial.write(MachineControl_RS485Comm.read()); 51 | 52 | if (millis() > sendNow) { 53 | // Disable receive mode before transmission 54 | MachineControl_RS485Comm.noReceive(); 55 | 56 | MachineControl_RS485Comm.beginTransmission(); 57 | 58 | MachineControl_RS485Comm.print("hello "); 59 | MachineControl_RS485Comm.println(counter++); 60 | 61 | MachineControl_RS485Comm.endTransmission(); 62 | 63 | // Re-enable receive mode after transmission 64 | MachineControl_RS485Comm.receive(); 65 | 66 | sendNow = millis() + sendInterval; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/utility/MAX31865/MAX31865.h: -------------------------------------------------------------------------------- 1 | #ifndef MAX31865_H 2 | #define MAX31865_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #define MAX31856_CONFIG_REG 0x00 10 | #define MAX31856_RTD_MSB_REG 0x01 11 | #define MAX31856_FAULT_STATUS_REG 0x07 12 | 13 | //config bias mask 14 | #define MAX31856_CONFIG_BIAS_MASK 0x7F 15 | #define MAX31856_CONFIG_BIAS_ON 0x80 16 | 17 | //config conversion mode mask 18 | #define MAX31856_CONFIG_CONV_MODE_MASK 0xBF 19 | #define MAX31856_CONFIG_CONV_MODE_AUTO 0x40 20 | 21 | //config one shot mask 22 | #define MAX31856_CONFIG_ONE_SHOT_MASK 0xDF 23 | #define MAX31856_CONFIG_ONE_SHOT 0x20 24 | 25 | //config wire mask 26 | #define MAX31856_CONFIG_WIRE_MASK 0xEF 27 | #define MAX31856_CONFIG_3_WIRE 0x10 28 | 29 | //config wire fault detection cycle mask 30 | #define MAX31856_CONFIG_FAULT_DECT_CYCLE_MASK 0xF3 31 | #define MAX31856_CONFIG_CLEAR_FAULT_CYCLE 0xD3 32 | 33 | //config fault status mask 34 | #define MAX31856_CONFIG_FAULT_STATUS_MASK 0xFD 35 | #define MAX31856_CONFIG_CLEAR_FAULT 0x02 36 | 37 | // config 50 60 filter frequency mask 38 | #define MAX31856_CONFIG_60_50_HZ_FILTER_MASK 0xFE 39 | 40 | // fault mask 41 | #define MAX31865_FAULT_HIGH_THRESH 0x80 42 | #define MAX31865_FAULT_LOW_THRESH 0x40 43 | #define MAX31865_FAULT_LOW_REFIN 0x20 44 | #define MAX31865_FAULT_HIGH_REFIN 0x10 45 | #define MAX31865_FAULT_LOW_RTDIN 0x08 46 | #define MAX31865_FAULT_OVER_UNDER_VOLTAGE 0x04 47 | 48 | #define RTD_A 3.9083e-3 49 | #define RTD_B -5.775e-7 50 | 51 | #define TWO_WIRE 0 52 | #define THREE_WIRE 1 53 | 54 | 55 | class MAX31865Class { 56 | public: 57 | MAX31865Class(PinName cs = PA_6); 58 | 59 | bool begin(int wires); 60 | void end(); 61 | 62 | float readTemperature(float RTDnominal, float refResistor); 63 | uint8_t readFault(void); 64 | void clearFault(void); 65 | uint32_t readRTD(); 66 | bool getHighThresholdFault(uint8_t fault); 67 | bool getLowThresholdFault(uint8_t fault); 68 | bool getLowREFINFault(uint8_t fault); 69 | bool getHighREFINFault(uint8_t fault); 70 | bool getLowRTDINFault(uint8_t fault); 71 | bool getVoltageFault(uint8_t fault); 72 | 73 | 74 | 75 | private: 76 | uint8_t readByte(uint8_t addr); 77 | uint16_t readBytes(uint8_t addr); 78 | void writeByte(uint8_t addr, uint8_t data); 79 | 80 | PinName _cs; 81 | SPIClass& _spi; 82 | }; 83 | 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /examples/RTC_Alarm/RTC_Alarm.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - RTC Alarm Example 3 | * 4 | * This sketch shows the usage of the RTC PCF8563T on the Machine 5 | * Control Carrier and demonstrates how to configure and utilize the PCF8563T's alarm. 6 | * 7 | * Circuit: 8 | * - Portenta H7 9 | * - Portenta Machine Control 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | int hours = 12; 19 | int minutes = 45; 20 | int seconds = 57; 21 | 22 | bool alarm_flag = false; 23 | int counter = 1; 24 | 25 | void callback_alarm(); 26 | 27 | void setup() { 28 | Serial.begin(9600); 29 | while (!Serial) { 30 | ; 31 | } 32 | 33 | Serial.print("RTC Initialization"); 34 | if(!MachineControl_RTCController.begin()) { 35 | Serial.println(" fail!"); 36 | } 37 | Serial.println(" done!"); 38 | 39 | // APIs to set date's fields: hours, minutes and seconds 40 | MachineControl_RTCController.setHours(hours); 41 | MachineControl_RTCController.setMinutes(minutes); 42 | MachineControl_RTCController.setSeconds(seconds); 43 | // Enables Alarm on PCF8563T 44 | MachineControl_RTCController.enableAlarm(); 45 | 46 | // set the minutes at which the alarm should rise 47 | MachineControl_RTCController.setMinuteAlarm(46); 48 | 49 | // Attach an interrupt to the RTC interrupt pin 50 | attachInterrupt(RTC_INT, callback_alarm, FALLING); 51 | } 52 | 53 | void loop() { 54 | if (alarm_flag) { 55 | Serial.println("Alarm!!"); 56 | detachInterrupt(RTC_INT); 57 | MachineControl_RTCController.setSeconds(seconds); 58 | MachineControl_RTCController.setMinuteAlarm(minutes + counter); 59 | MachineControl_RTCController.clearAlarm(); 60 | attachInterrupt(RTC_INT, callback_alarm, FALLING); 61 | alarm_flag = false; 62 | 63 | // To disable the alarm uncomment the following line: 64 | // MachineControl_RTCController.disableAlarm(); 65 | } 66 | 67 | // APIs to get date's fields 68 | Serial.print(MachineControl_RTCController.getHours()); 69 | Serial.print(":"); 70 | Serial.print(MachineControl_RTCController.getMinutes()); 71 | Serial.print(":"); 72 | Serial.println(MachineControl_RTCController.getSeconds()); 73 | 74 | delay(1000); 75 | } 76 | 77 | void callback_alarm () { 78 | alarm_flag = true; 79 | } -------------------------------------------------------------------------------- /examples/RTC/RTC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - RTC Example 3 | * 4 | * This sketch shows the utilization of the RTC PCF8563T on the Machine 5 | * Control Carrier and demonstrates how to configure the PCF8563T's time registers. 6 | * 7 | * Circuit: 8 | * - Portenta H7 9 | * - Portenta Machine Control 10 | * 11 | * This example code is in the public domain. 12 | * Copyright (c) 2024 Arduino 13 | * SPDX-License-Identifier: MPL-2.0 14 | */ 15 | 16 | #include 17 | 18 | int year = 20; 19 | int month = 9; 20 | int day = 24; 21 | int hours = 12; 22 | int minutes = 43; 23 | int seconds = 31; 24 | 25 | void setup() { 26 | Serial.begin(9600); 27 | while (!Serial) { 28 | ; 29 | } 30 | 31 | Serial.print("RTC Initialization"); 32 | if(!MachineControl_RTCController.begin()) { 33 | Serial.println(" fail!"); 34 | } 35 | Serial.println(" done!"); 36 | 37 | // APIs to set date's fields: years, months, days, hours, minutes and seconds 38 | // The RTC time can be set as epoch, using one of the following two options: 39 | // - Calendar time: MachineControl_RTCController.setEpoch(years, months, days, hours, minutes, seconds); 40 | // - UTC time: MachineControl_RTCController.setEpoch(date_in_seconds); 41 | MachineControl_RTCController.setYear(year); 42 | MachineControl_RTCController.setMonth(month); 43 | MachineControl_RTCController.setDay(day); 44 | MachineControl_RTCController.setHours(hours); 45 | MachineControl_RTCController.setMinutes(minutes); 46 | MachineControl_RTCController.setSeconds(seconds); 47 | MachineControl_RTCController.setEpoch(); 48 | } 49 | 50 | void loop() { 51 | // APIs to get date's fields 52 | Serial.print("Date: "); 53 | Serial.print(MachineControl_RTCController.getYear()); 54 | Serial.print("/"); 55 | Serial.print(MachineControl_RTCController.getMonth()); 56 | Serial.print("/"); 57 | Serial.print(MachineControl_RTCController.getDay()); 58 | Serial.print(" - "); 59 | Serial.print(MachineControl_RTCController.getHours()); 60 | Serial.print(":"); 61 | Serial.print(MachineControl_RTCController.getMinutes()); 62 | Serial.print(":"); 63 | Serial.println(MachineControl_RTCController.getSeconds()); 64 | 65 | time_t utc_time = MachineControl_RTCController.getEpoch(); 66 | Serial.print("Date as UTC time: "); 67 | Serial.println(utc_time); 68 | Serial.println(); 69 | 70 | delay(1000); 71 | } 72 | -------------------------------------------------------------------------------- /examples/RS485_fullduplex/RS485_fullduplex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - RS485 Full Duplex Communication Example 3 | * 4 | * This sketch shows the usage of the SP335ECR1 on the Machine Control 5 | * as a full duplex (AB and YZ) RS485 interface. It shows how to periodically send a string 6 | * on the RS485 TX channel and how to receive data from the interface RX channel. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * - A Slave device with RS485 interface 12 | * - Connect TXP to A(+) and TXN to B(-) 13 | * - Connect RXP to Y(+) and RXN to Z(-) 14 | * 15 | * This example code is in the public domain. 16 | * Copyright (c) 2024 Arduino 17 | * SPDX-License-Identifier: MPL-2.0 18 | */ 19 | 20 | #include "Arduino_PortentaMachineControl.h" 21 | 22 | constexpr unsigned long sendInterval { 1000 }; 23 | unsigned long sendNow { 0 }; 24 | unsigned long counter = 0; 25 | 26 | void setup() { 27 | Serial.begin(9600); 28 | while (!Serial) { 29 | ; 30 | } 31 | 32 | delay(1000); 33 | Serial.println("Start RS485 initialization"); 34 | 35 | // Set the PMC Communication Protocols to default config 36 | // RS485/RS232 default config is: 37 | // - RS485 mode 38 | // - Half Duplex 39 | // - No A/B and Y/Z 120 Ohm termination enabled 40 | // Enable the RS485/RS232 system 41 | MachineControl_RS485Comm.begin(115200, SERIAL_8N1, 0, 500); // Specify baudrate, serial_config and preamble and postamble times for RS485 communication 42 | 43 | // Enable Full Duplex mode 44 | // This will also enable A/B and Y/Z 120 Ohm termination resistors 45 | MachineControl_RS485Comm.setFullDuplex(true); 46 | 47 | // Start in receive mode 48 | MachineControl_RS485Comm.receive(); 49 | 50 | Serial.println("Initialization done!"); 51 | } 52 | 53 | void loop() { 54 | if (MachineControl_RS485Comm.available()) 55 | Serial.write(MachineControl_RS485Comm.read()); 56 | 57 | if (millis() > sendNow) { 58 | // Disable receive mode before transmission 59 | MachineControl_RS485Comm.noReceive(); 60 | 61 | MachineControl_RS485Comm.beginTransmission(); 62 | 63 | MachineControl_RS485Comm.print("hello "); 64 | MachineControl_RS485Comm.println(counter++); 65 | 66 | MachineControl_RS485Comm.endTransmission(); 67 | 68 | // Re-enable receive mode after transmission 69 | MachineControl_RS485Comm.receive(); 70 | 71 | sendNow = millis() + sendInterval; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/RS232/RS232.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - RS232 Communication Example 3 | * 4 | * This sketch shows the usage of the SP335ECR1 on the Machine Control 5 | * as an RS232 interface. It demonstrates how to periodically send a string on the RS232 TX channel 6 | * and how to receive data from the interface RX channel. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * - Device with RS232 interface 12 | * - Connect PMC TXN to RS232 Device RXD 13 | * - Connect PMC RXP to RS232 Device TXD 14 | * - Connect PMC GND to RS232 Device GND 15 | * 16 | * This example code is in the public domain. 17 | * Copyright (c) 2024 Arduino 18 | * SPDX-License-Identifier: MPL-2.0 19 | */ 20 | 21 | #include 22 | 23 | constexpr unsigned long sendInterval { 1000 }; 24 | unsigned long sendNow { 0 }; 25 | unsigned long counter { 0 }; 26 | 27 | void setup() { 28 | Serial.begin(9600); 29 | while (!Serial) { 30 | ; 31 | } 32 | 33 | delay(2500); 34 | Serial.println("Start RS232 initialization"); 35 | 36 | // Set the PMC Communication Protocols to default config and enable the RS485/RS232 system 37 | // RS485/RS232 default config is: 38 | // - RS485/RS232 system disabled 39 | // - RS485 mode 40 | // - Half Duplex 41 | // - No A/B and Y/Z 120 Ohm termination enabled 42 | MachineControl_RS485Comm.begin(115200); // Specify baudrate for the communication 43 | 44 | // Enable the RS232 mode 45 | MachineControl_RS485Comm.setModeRS232(true); 46 | 47 | // Start in receive mode 48 | MachineControl_RS485Comm.receive(); 49 | 50 | Serial.println("Initialization done!"); 51 | } 52 | 53 | void loop() { 54 | if (MachineControl_RS485Comm.available()) 55 | Serial.write(MachineControl_RS485Comm.read()); 56 | 57 | if (millis() > sendNow) { 58 | String log = "["; 59 | log += sendNow; 60 | log += "] "; 61 | 62 | String msg = "hello "; 63 | msg += counter++; 64 | 65 | log += msg; 66 | Serial.println(log); 67 | 68 | // Disable receive mode before transmission 69 | MachineControl_RS485Comm.noReceive(); 70 | 71 | MachineControl_RS485Comm.beginTransmission(); 72 | MachineControl_RS485Comm.println(msg); 73 | MachineControl_RS485Comm.endTransmission(); 74 | 75 | // Re-enable receive mode after transmission 76 | MachineControl_RS485Comm.receive(); 77 | 78 | sendNow = millis() + sendInterval; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/pins_mc.h: -------------------------------------------------------------------------------- 1 | #ifndef __PINS_MC_H 2 | #define __PINS_MC_H 3 | 4 | /** Portenta H7 **/ 5 | 6 | /* AnalogIn */ 7 | #define MC_AI_AI0_PIN PC_3C 8 | #define MC_AI_AI1_PIN PC_2C 9 | #define MC_AI_AI2_PIN PA_1C 10 | 11 | #define MC_AI_CH0_IN1_PIN PD_4 12 | #define MC_AI_CH0_IN2_PIN PD_5 13 | #define MC_AI_CH0_IN3_PIN PE_3 14 | #define MC_AI_CH0_IN4_PIN PG_3 15 | 16 | #define MC_AI_CH1_IN1_PIN PD_7 17 | #define MC_AI_CH1_IN2_PIN PH_6 18 | #define MC_AI_CH1_IN3_PIN PJ_7 19 | #define MC_AI_CH1_IN4_PIN PH_15 20 | 21 | #define MC_AI_CH2_IN1_PIN PH_10 22 | #define MC_AI_CH2_IN2_PIN PA_4 23 | #define MC_AI_CH2_IN3_PIN PA_8 24 | #define MC_AI_CH2_IN4_PIN PC_6 25 | 26 | /* AnalogOut */ 27 | #define MC_AO_AO0_PIN PJ_11 28 | #define MC_AO_AO1_PIN PK_1 29 | #define MC_AO_AO2_PIN PG_7 30 | #define MC_AO_AO3_PIN PC_7 31 | 32 | /* CAN */ 33 | #define MC_CAN_TX_PIN PB_8 34 | #define MC_CAN_RX_PIN PH_13 35 | #define MC_CAN_STB_PIN PA_13 36 | 37 | /* DigitalOut */ 38 | #define MC_DO_DO0_PIN PI_6 39 | #define MC_DO_DO1_PIN PH_9 40 | #define MC_DO_DO2_PIN PJ_9 41 | #define MC_DO_DO3_PIN PE_2 42 | #define MC_DO_DO4_PIN PI_3 43 | #define MC_DO_DO5_PIN PI_2 44 | #define MC_DO_DO6_PIN PD_3 45 | #define MC_DO_DO7_PIN PA_14 46 | #define MC_DO_LATCH_PIN PB_2 47 | 48 | /* Encoder */ 49 | #define MC_ENC_0A_PIN PJ_8 50 | #define MC_ENC_0B_PIN PH_12 51 | #define MC_ENC_0I_PIN PH_11 52 | #define MC_ENC_1A_PIN PC_13 53 | #define MC_ENC_1B_PIN PI_7 54 | #define MC_ENC_1I_PIN PJ_10 55 | 56 | /* Programmable DIO */ 57 | #define MC_PDIO_LATCH_PIN PH_14 58 | 59 | /* RS485/232 */ 60 | #define MC_RS485_TX_PIN PA_0 61 | #define MC_RS485_DE_PIN PI_13 62 | #define MC_RS485_RE_PIN PI_10 63 | 64 | #define MC_RS485_RX_PIN PI_9 65 | #define MC_RS485_EN_PIN PG_9 66 | #define MC_RS485_RS232_PIN PA_10 67 | #define MC_RS485_FDTERM_PIN PI_15 68 | #define MC_RS485_TERM_PIN PI_14 69 | #define MC_RS485_SLEW_PIN PG_14 70 | #define MC_RS485_HF_PIN PA_9 71 | 72 | /* RTC */ 73 | #define MC_RTC_INT_PIN PB_9 74 | 75 | /* RTD Temperature probe */ 76 | #define MC_RTD_CS_PIN PA_6 77 | #define MC_RTD_SEL0_PIN PD_6 78 | #define MC_RTD_SEL1_PIN PI_4 79 | #define MC_RTD_SEL2_PIN PG_10 80 | #define MC_RTD_TH_PIN PC_15 81 | 82 | /* TC Temperature probe */ 83 | #define MC_TC_CS_PIN PI_0 84 | #define MC_TC_SEL0_PIN MC_RTD_SEL0_PIN 85 | #define MC_TC_SEL1_PIN MC_RTD_SEL1_PIN 86 | #define MC_TC_SEL2_PIN MC_RTD_SEL2_PIN 87 | #define MC_TC_TH_PIN MC_RTD_TH_PIN 88 | 89 | /* USB */ 90 | #define MC_USB_PWR_PIN PB_14 91 | #define MC_USB_FLAG_PIN PB_15 92 | 93 | #endif /* __PINS_MC_H */ -------------------------------------------------------------------------------- /src/AnalogOutClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AnalogOutClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Analog OUT connector of the Portenta Machine Control library. 5 | * 6 | * This library allows to configure the analog channels as PWM, to set frequency and value. 7 | */ 8 | 9 | #ifndef __ANALOGOUT_CLASS_H 10 | #define __ANALOGOUT_CLASS_H 11 | 12 | /* Includes -------------------------------------------------------------------*/ 13 | #include 14 | #include 15 | #include "pins_mc.h" 16 | 17 | /* Class ----------------------------------------------------------------------*/ 18 | 19 | /** 20 | * @class AnalogOutClass 21 | * @brief Class for the Analog OUT connector of the Portenta Machine Control. 22 | */ 23 | class AnalogOutClass { 24 | public: 25 | /** 26 | * @brief Construct an Analog Output writer for the Portenta Machine Control. 27 | * 28 | * @param ao0_pin The analog pin number of the channel 0 29 | * @param ao1_pin The analog pin number of the channel 1 30 | * @param ao2_pin The analog pin number of the channel 2 31 | * @param ao3_pin The analog pin number of the channel 2 32 | */ 33 | AnalogOutClass(PinName ao0_pin = MC_AO_AO0_PIN, PinName ao1_pin = MC_AO_AO1_PIN, PinName ao2_pin = MC_AO_AO2_PIN, PinName ao3_pin = MC_AO_AO3_PIN); 34 | 35 | /** 36 | * @brief Destruct the AnalogOutClass object. 37 | * 38 | * This destructor releases any resources used by the AnalogOutClass object. 39 | */ 40 | ~AnalogOutClass(); 41 | 42 | /** 43 | * @brief Initialize the PWM, configure the default frequency for all channels (500Hz) 44 | * 45 | * @return true If the PWM is successfully initialized, false Otherwise 46 | */ 47 | bool begin(); 48 | 49 | /** 50 | * @brief Set the PWM period (frequency) on the selected channel 51 | * 52 | * @param channel selected channel 53 | * @param period_ms PWM period in ms 54 | */ 55 | void setPeriod(int channel, uint8_t period_ms); 56 | 57 | /** 58 | * @brief Set output voltage value on the selected channel 59 | * 60 | * @param channel selected channel 61 | * @param voltage desired output voltage (max 10.5V) 62 | */ 63 | void write(int channel, float voltage); 64 | 65 | private: 66 | mbed::PwmOut _ao0; // PWM output for Analog Out channel 0 67 | mbed::PwmOut _ao1; // PWM output for Analog Out channel 1 68 | mbed::PwmOut _ao2; // PWM output for Analog Out channel 2 69 | mbed::PwmOut _ao3; // PWM output for Analog Out channel 3 70 | }; 71 | 72 | extern AnalogOutClass MachineControl_AnalogOut; 73 | 74 | #endif /* __ANALOGOUT_CLASS_H */ -------------------------------------------------------------------------------- /examples/Analog_input/Analog_input_NTC/Analog_input_NTC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Analog in NTC 3 | * 4 | * This example provides the resistance value acquired by the Machine Control. 5 | * A 3V voltage REFERENCE is connected to each channel of the ANALOG IN connector. 6 | * The REFERENCE has a 100k resistor in series, allowing only a low current flow. 7 | * The voltage sampled by the Portenta's ADC is the REFERENCE voltage divided by the voltage divider 8 | * composed of the input resistor and the 100k resistor in series with the voltage REFERENCE. 9 | * The resistor value is calculated by inversely applying the voltage divider formula. 10 | * To use the NTC functionality, a 24V supply on the PWR SUPPLY connector is required. 11 | * 12 | * The circuit: 13 | * - Portenta H7 14 | * - Portenta Machine Control 15 | * 16 | * This example code is in the public domain. 17 | * Copyright (c) 2024 Arduino 18 | * SPDX-License-Identifier: MPL-2.0 19 | */ 20 | 21 | #include 22 | 23 | #define REFERENCE_RES 100000 24 | 25 | const float REFERENCE = 3.0; 26 | const float LOWEST_VOLTAGE = 2.7; 27 | 28 | void setup() { 29 | Serial.begin(9600); 30 | while (!Serial) { 31 | ; // wait for serial port to connect. 32 | } 33 | 34 | MachineControl_AnalogIn.begin(SensorType::NTC); 35 | } 36 | 37 | void loop() { 38 | float raw_voltage_ch0 = MachineControl_AnalogIn.read(0); 39 | float voltage_ch0 = (raw_voltage_ch0 * REFERENCE) / 65535; 40 | float resistance_ch0; 41 | Serial.print("Resistance CH0: "); 42 | if (voltage_ch0 < LOWEST_VOLTAGE) { 43 | resistance_ch0 = ((-REFERENCE_RES) * voltage_ch0) / (voltage_ch0 - REFERENCE); 44 | Serial.print(resistance_ch0); 45 | Serial.println(" ohm"); 46 | } else { 47 | resistance_ch0 = -1; 48 | Serial.println("NaN"); 49 | } 50 | 51 | float raw_voltage_ch1 = MachineControl_AnalogIn.read(1); 52 | float voltage_ch1 = (raw_voltage_ch1 * REFERENCE) / 65535; 53 | float resistance_ch1; 54 | Serial.print("Resistance CH1: "); 55 | if (voltage_ch1 < LOWEST_VOLTAGE) { 56 | resistance_ch1 = ((-REFERENCE_RES) * voltage_ch1) / (voltage_ch1 - REFERENCE); 57 | Serial.print(resistance_ch1); 58 | Serial.println(" ohm"); 59 | } else { 60 | resistance_ch1 = -1; 61 | Serial.println("NaN"); 62 | } 63 | 64 | float raw_voltage_ch2 = MachineControl_AnalogIn.read(2); 65 | float voltage_ch2 = (raw_voltage_ch2 * REFERENCE) / 65535; 66 | float resistance_ch2; 67 | Serial.print("Resistance CH2: "); 68 | if (voltage_ch2 < LOWEST_VOLTAGE) { 69 | resistance_ch2 = ((-REFERENCE_RES) * voltage_ch2) / (voltage_ch2 - REFERENCE); 70 | Serial.print(resistance_ch2); 71 | Serial.println(" ohm"); 72 | } else { 73 | resistance_ch2 = -1; 74 | Serial.println("NaN"); 75 | } 76 | 77 | Serial.println(); 78 | delay(250); 79 | } 80 | -------------------------------------------------------------------------------- /src/ProgrammableDIOClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ProgrammableDIOClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Programmable Digital IO connector of the Portenta Machine Control library. 5 | * 6 | * This library allows interfacing with the IO Expander and configuring the thermal shutdown mode of the high-side switches. 7 | */ 8 | 9 | #ifndef __PROGRAMMABLE_DIO_CLASS_H 10 | #define __PROGRAMMABLE_DIO_CLASS_H 11 | 12 | /* Includes -------------------------------------------------------------------*/ 13 | #include "utility/ioexpander/ArduinoIOExpander.h" 14 | #include 15 | #include 16 | #include "pins_mc.h" 17 | 18 | /* Class ----------------------------------------------------------------------*/ 19 | 20 | /** 21 | * @class ProgrammableDIOClass 22 | * @brief Class for the Programmable Digital IO connector of the Portenta Machine Control. 23 | * 24 | * This class extends the ArduinoIOExpanderClass to interface with the IO Expander and provides methods to configure thermal shutdown modes. 25 | */ 26 | class ProgrammableDIOClass : public ArduinoIOExpanderClass { 27 | public: 28 | /** 29 | * @brief Construct a ProgrammableDIOClass object. 30 | * 31 | * This constructor initializes a ProgrammableDIOClass object with the specified pin assignment for the latch pin. 32 | * 33 | * @param latch_pin The pin number for the latch mode control. 34 | */ 35 | ProgrammableDIOClass(PinName latch_pin = MC_PDIO_LATCH_PIN); 36 | 37 | /** 38 | * @brief Destruct the ProgrammableDIOClass object. 39 | * 40 | * This destructor releases any resources used by the ProgrammableDIOClass object. 41 | */ 42 | ~ProgrammableDIOClass(); 43 | 44 | /** 45 | * @brief Initialize the ProgrammableDIO module with the specified latch mode. 46 | * 47 | * @param latch_mode The latch mode for thermal shutdown. If true, thermal shutdown operates in the latch mode. Otherwise, it operates in the auto-retry mode. 48 | * @return true If the ProgrammableDIO module is successfully initialized, false otherwise. 49 | */ 50 | bool begin(bool latch_mode = true); 51 | 52 | private: 53 | PinName _latch; // Latch control pin 54 | 55 | /** 56 | * @brief Configures the thermal shutdown of the high-side switches (TPS4H160) to operate in latch mode. 57 | * The output latches off when thermal shutdown occurs. 58 | */ 59 | void _setLatchMode(); 60 | 61 | /** 62 | * @brief Configures the thermal shutdown of the high-side switches (TPS4H160) to operate in auto-retry mode. 63 | * The output automatically recovers when TJ < T(SD) – T(hys), but the current is limited to ICL(TSD) 64 | * to avoid repetitive thermal shutdown. 65 | */ 66 | void _setAutoRetryMode(); 67 | }; 68 | 69 | extern ProgrammableDIOClass MachineControl_DigitalProgrammables; 70 | 71 | #endif /* __PROGRAMMABLE_DIO_CLASS_H */ -------------------------------------------------------------------------------- /src/AnalogInClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AnalogInClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Analog IN connector of the Portenta Machine Control library. 5 | * 6 | * This library allows to set the resistor configuration for the type of connected analog sensor (0-10V, 4-20mA or NTC) 7 | * and to capture the analog values acquired by the ANALOG IN channels. 8 | */ 9 | 10 | #ifndef __ANALOGIN_CLASS_H 11 | #define __ANALOGIN_CLASS_H 12 | 13 | /* Includes ------------------------------------------------------------------*/ 14 | #include 15 | #include 16 | #include "pins_mc.h" 17 | 18 | /* Exported defines ----------------------------------------------------------*/ 19 | 20 | /** 21 | * @brief Enum class that represents different sensor types. 22 | */ 23 | enum class SensorType { 24 | NTC = 1, 25 | V_0_10 = 2, 26 | MA_4_20 = 3 27 | }; 28 | 29 | /* Class ----------------------------------------------------------------------*/ 30 | 31 | /** 32 | * @class AnalogInClass 33 | * @brief Class for the Analog IN connector of the Portenta Machine Control. 34 | */ 35 | class AnalogInClass { 36 | public: 37 | /** 38 | * @brief Construct an Analog Input reader for the Portenta Machine Control. 39 | * 40 | * @param ai0_pin The analog pin number of the channel 0 41 | * @param ai1_pin The analog pin number of the channel 1 42 | * @param ai2_pin The analog pin number of the channel 2 43 | */ 44 | AnalogInClass(PinName ai0_pin = MC_AI_AI0_PIN, PinName ai1_pin = MC_AI_AI1_PIN, PinName ai2_pin = MC_AI_AI2_PIN); 45 | 46 | /** 47 | * @brief Destruct the AnalogInClass object. 48 | * 49 | * This destructor releases any resources used by the AnalogInClass object. 50 | */ 51 | ~AnalogInClass(); 52 | 53 | /** 54 | * @brief Initialize the analog reader, configure the sensor type and read resolution. 55 | * 56 | * @param sensor_type The sensor type (NTC, V_0_10 or MA_4_20) 57 | * @param res_bits Resolution in bits of the read analog value 58 | * @return true If the analog reader is successfully initialized, false otherwise 59 | */ 60 | bool begin(SensorType sensor_type, int res_bits = 16); 61 | 62 | /** 63 | * @brief Read the sampled voltage from the selected channel. 64 | * 65 | * @param channel The analog input channel number 66 | * @return uint16_t The analog value between 0.0 and 1.0 normalized to a 16-bit value 67 | */ 68 | uint16_t read(int channel); 69 | 70 | private: 71 | PinName _ai0; // Analog input pin for channel 0 72 | PinName _ai1; // Analog input pin for channel 1 73 | PinName _ai2; // Analog input pin for channel 2 74 | }; 75 | 76 | extern AnalogInClass MachineControl_AnalogIn; 77 | 78 | #endif /* __ANALOGIN_CLASS_H */ -------------------------------------------------------------------------------- /examples/Digital_programmable/Digital_input/Digital_input.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Digital Input Example 3 | * 4 | * This sketch shows how to periodically read from all the DIGITAL INPUTS channels on the Machine Control. 5 | * 6 | * Circuit: 7 | * - Portenta H7 8 | * - Portenta Machine Control 9 | * 10 | * This example code is in the public domain. 11 | * Copyright (c) 2024 Arduino 12 | * SPDX-License-Identifier: MPL-2.0 13 | */ 14 | 15 | #include 16 | 17 | uint16_t readings = 0; 18 | 19 | void setup() { 20 | Serial.begin(9600); 21 | while (!Serial) { 22 | ; // wait for serial port to connect. 23 | } 24 | 25 | Wire.begin(); 26 | 27 | if (!MachineControl_DigitalInputs.begin()) { 28 | Serial.println("Digital input GPIO expander initialization fail!!"); 29 | } 30 | } 31 | 32 | void loop() { 33 | //Reads and Prints all channels (in a single operation) 34 | readAll(); 35 | 36 | //Read one-by-one each channel and print them one-by-one 37 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_00); 38 | Serial.println("CH00: "+String(readings)); 39 | 40 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_01); 41 | Serial.println("CH01: "+String(readings)); 42 | 43 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_02); 44 | Serial.println("CH02: "+String(readings)); 45 | 46 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_03); 47 | Serial.println("CH03: "+String(readings)); 48 | 49 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_04); 50 | Serial.println("CH04: "+String(readings)); 51 | 52 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_05); 53 | Serial.println("CH05: "+String(readings)); 54 | 55 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_06); 56 | Serial.println("CH06: "+String(readings)); 57 | 58 | readings = MachineControl_DigitalInputs.read(DIN_READ_CH_PIN_07); 59 | Serial.println("CH07: "+String(readings)); 60 | 61 | Serial.println(); 62 | 63 | delay(250); 64 | } 65 | 66 | uint8_t readAll() { 67 | uint32_t inputs = MachineControl_DigitalInputs.readAll(); 68 | Serial.println("CH00: " + String((inputs & (1 << DIN_READ_CH_PIN_00)) >> DIN_READ_CH_PIN_00)); 69 | Serial.println("CH01: " + String((inputs & (1 << DIN_READ_CH_PIN_01)) >> DIN_READ_CH_PIN_01)); 70 | Serial.println("CH02: " + String((inputs & (1 << DIN_READ_CH_PIN_02)) >> DIN_READ_CH_PIN_02)); 71 | Serial.println("CH03: " + String((inputs & (1 << DIN_READ_CH_PIN_03)) >> DIN_READ_CH_PIN_03)); 72 | Serial.println("CH04: " + String((inputs & (1 << DIN_READ_CH_PIN_04)) >> DIN_READ_CH_PIN_04)); 73 | Serial.println("CH05: " + String((inputs & (1 << DIN_READ_CH_PIN_05)) >> DIN_READ_CH_PIN_05)); 74 | Serial.println("CH06: " + String((inputs & (1 << DIN_READ_CH_PIN_06)) >> DIN_READ_CH_PIN_06)); 75 | Serial.println("CH07: " + String((inputs & (1 << DIN_READ_CH_PIN_07)) >> DIN_READ_CH_PIN_07)); 76 | Serial.println(); 77 | } 78 | -------------------------------------------------------------------------------- /src/DigitalOutputsClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DigitalOutputsClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Digital Output connector connector of the Portenta Machine Control library. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "DigitalOutputsClass.h" 9 | 10 | /* Functions -----------------------------------------------------------------*/ 11 | DigitalOutputsClass::DigitalOutputsClass(PinName do0_pin, 12 | PinName do1_pin, 13 | PinName do2_pin, 14 | PinName do3_pin, 15 | PinName do4_pin, 16 | PinName do5_pin, 17 | PinName do6_pin, 18 | PinName do7_pin, 19 | PinName latch_pin) 20 | : _do0{do0_pin}, _do1{do1_pin}, _do2{do2_pin}, _do3{do3_pin}, _do4{do4_pin}, _do5{do5_pin}, _do6{do6_pin}, _do7{do7_pin}, _latch{latch_pin} 21 | { } 22 | 23 | DigitalOutputsClass::~DigitalOutputsClass() 24 | { } 25 | 26 | bool DigitalOutputsClass::begin(bool latch_mode) { 27 | pinMode(_do0, OUTPUT); 28 | pinMode(_do1, OUTPUT); 29 | pinMode(_do2, OUTPUT); 30 | pinMode(_do3, OUTPUT); 31 | pinMode(_do4, OUTPUT); 32 | pinMode(_do5, OUTPUT); 33 | pinMode(_do6, OUTPUT); 34 | pinMode(_do7, OUTPUT); 35 | 36 | pinMode(_latch, OUTPUT); 37 | 38 | if(latch_mode) { 39 | _setLatchMode(); 40 | } else { 41 | _setAutoRetryMode(); 42 | } 43 | 44 | return true; 45 | } 46 | 47 | void DigitalOutputsClass::write(uint8_t channel, PinStatus val) { 48 | switch (channel) { 49 | case 0: 50 | digitalWrite(_do0, val); 51 | break; 52 | case 1: 53 | digitalWrite(_do1, val); 54 | break; 55 | case 2: 56 | digitalWrite(_do2, val); 57 | break; 58 | case 3: 59 | digitalWrite(_do3, val); 60 | break; 61 | case 4: 62 | digitalWrite(_do4, val); 63 | break; 64 | case 5: 65 | digitalWrite(_do5, val); 66 | break; 67 | case 6: 68 | digitalWrite(_do6, val); 69 | break; 70 | case 7: 71 | digitalWrite(_do7, val); 72 | break; 73 | default: 74 | break; 75 | } 76 | } 77 | 78 | void DigitalOutputsClass::writeAll(uint8_t val_mask) { 79 | for (uint8_t ch = 0; ch < 8; ch++) { 80 | if (val_mask & (1 << ch)) { 81 | write(ch, HIGH); 82 | } else { 83 | write(ch, LOW); 84 | } 85 | } 86 | } 87 | 88 | void DigitalOutputsClass::_setLatchMode() { 89 | digitalWrite(_latch, HIGH); 90 | } 91 | 92 | void DigitalOutputsClass::_setAutoRetryMode() { 93 | digitalWrite(_latch, LOW); 94 | } 95 | 96 | DigitalOutputsClass MachineControl_DigitalOutputs; 97 | /**** END OF FILE ****/ 98 | -------------------------------------------------------------------------------- /src/utility/ioexpander/ArduinoIOExpander.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "TCA6424A.h" 5 | 6 | #define IO_ADD TCA6424A_ADDRESS_ADDR_LOW // address pin low (GND) 7 | #define DIN_ADD TCA6424A_ADDRESS_ADDR_HIGH // address pin high (VCC) 8 | 9 | #define SWITCH_ON HIGH 10 | #define SWITCH_OFF LOW 11 | 12 | #define SWITCH_ON_ALL 0x0000FFFF 13 | #define SWITCH_OFF_ALL 0x00000000 14 | 15 | enum { 16 | ON_VALUE_PIN_00 = 0x01, 17 | ON_VALUE_PIN_01 = 0x02, 18 | ON_VALUE_PIN_02 = 0x04, 19 | ON_VALUE_PIN_03 = 0x08, 20 | ON_VALUE_PIN_04 = 0x80, 21 | ON_VALUE_PIN_05 = 0x40, 22 | ON_VALUE_PIN_06 = 0x20, 23 | ON_VALUE_PIN_07 = 0x10, 24 | ON_VALUE_PIN_08 = 0x100, 25 | ON_VALUE_PIN_09 = 0x200, 26 | ON_VALUE_PIN_10 = 0x400, 27 | ON_VALUE_PIN_11 = 0x800, 28 | }; 29 | 30 | enum { 31 | IO_WRITE_CH_PIN_00 = TCA6424A_P00, 32 | IO_WRITE_CH_PIN_01 = TCA6424A_P01, 33 | IO_WRITE_CH_PIN_02 = TCA6424A_P02, 34 | IO_WRITE_CH_PIN_03 = TCA6424A_P03, 35 | IO_WRITE_CH_PIN_04 = TCA6424A_P07, 36 | IO_WRITE_CH_PIN_05 = TCA6424A_P06, 37 | IO_WRITE_CH_PIN_06 = TCA6424A_P05, 38 | IO_WRITE_CH_PIN_07 = TCA6424A_P04, 39 | IO_WRITE_CH_PIN_08 = TCA6424A_P10, 40 | IO_WRITE_CH_PIN_09 = TCA6424A_P11, 41 | IO_WRITE_CH_PIN_10 = TCA6424A_P12, 42 | IO_WRITE_CH_PIN_11 = TCA6424A_P13, 43 | 44 | IO_READ_CH_PIN_00 = TCA6424A_P27, 45 | IO_READ_CH_PIN_01 = TCA6424A_P26, 46 | IO_READ_CH_PIN_02 = TCA6424A_P25, 47 | IO_READ_CH_PIN_03 = TCA6424A_P24, 48 | IO_READ_CH_PIN_04 = TCA6424A_P23, 49 | IO_READ_CH_PIN_05 = TCA6424A_P22, 50 | IO_READ_CH_PIN_06 = TCA6424A_P21, 51 | IO_READ_CH_PIN_07 = TCA6424A_P20, 52 | IO_READ_CH_PIN_08 = TCA6424A_P17, 53 | IO_READ_CH_PIN_09 = TCA6424A_P16, 54 | IO_READ_CH_PIN_10 = TCA6424A_P15, 55 | IO_READ_CH_PIN_11 = TCA6424A_P14 56 | }; 57 | 58 | enum { 59 | DIN_READ_CH_PIN_00 = TCA6424A_P10, 60 | DIN_READ_CH_PIN_01 = TCA6424A_P05, 61 | DIN_READ_CH_PIN_02 = TCA6424A_P04, 62 | DIN_READ_CH_PIN_03 = TCA6424A_P02, 63 | DIN_READ_CH_PIN_04 = TCA6424A_P01, 64 | DIN_READ_CH_PIN_05 = TCA6424A_P00, 65 | DIN_READ_CH_PIN_06 = TCA6424A_P07, 66 | DIN_READ_CH_PIN_07 = TCA6424A_P06, 67 | }; 68 | 69 | class ArduinoIOExpanderClass { 70 | 71 | public: 72 | ArduinoIOExpanderClass() = default; 73 | ~ArduinoIOExpanderClass() = default; 74 | 75 | bool begin(); 76 | bool begin(uint8_t address); 77 | 78 | operator bool(); 79 | void setAddress(uint8_t address); 80 | bool set(int pin, PinStatus status); 81 | bool set(int pin, int status) { return set( pin, (PinStatus)status); }; 82 | void writeAll(uint32_t banks); 83 | int read(int pin); 84 | uint32_t readAll(); 85 | void toggle(); 86 | bool pinMode(int pin, PinMode direction); 87 | 88 | private: 89 | void initPins(); 90 | private: 91 | TCA6424A _tca {}; 92 | }; 93 | 94 | extern ArduinoIOExpanderClass Expander; 95 | -------------------------------------------------------------------------------- /src/USBClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file USBControllerClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the USB Controller of the Portenta Machine Control. 5 | * 6 | * This library provides a class to manage the USB functionality of the Portenta Machine Control. 7 | * It enables or disables the power of the USB Host (USBA) and provides methods to check the fault status. 8 | */ 9 | 10 | #ifndef __USB_CONTROLLER_CLASS_H 11 | #define __USB_CONTROLLER_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include 15 | #include 16 | #include "pins_mc.h" 17 | 18 | /* Class ----------------------------------------------------------------------*/ 19 | 20 | /** 21 | * @class USBClass 22 | * @brief Class for managing the USB functionality of the Portenta Machine Control. 23 | * 24 | * This class allows enabling or disabling the power of the USB Host (USBA) and checking for any fault status. 25 | */ 26 | class USBClass { 27 | public: 28 | /** 29 | * @brief Construct a USBClass object. 30 | * 31 | * This constructor initializes a USBClass object with the specified pin assignments for power control and fault status. 32 | * 33 | * @param power_pin The pin number for controlling the power to the USBA VBUS. 34 | * @param usbflag_pin The pin number for reading the fault status of the USBA VBUS. 35 | */ 36 | USBClass(PinName power_pin = MC_USB_PWR_PIN, PinName usbflag_pin = MC_USB_FLAG_PIN); 37 | 38 | /** 39 | * @brief Destruct the USBClass object. 40 | * 41 | * This destructor releases any resources used by the USBClass object. 42 | */ 43 | ~USBClass(); 44 | 45 | /** 46 | * @brief Begin the USB functionality. 47 | * 48 | * This method initializes the USB functionality by enabling power to the USBA VBUS. 49 | * 50 | * @return true If the initialization is successful, false otherwise. 51 | */ 52 | bool begin(); 53 | 54 | /** 55 | * @brief End the USB functionality. 56 | * 57 | * This method disables power to the USBA VBUS and releases any resources used by the USBClass object. 58 | */ 59 | void end(); 60 | 61 | /** 62 | * @brief Get the fault status of the USBA VBUS. 63 | * 64 | * This method reads the fault status of the USBA VBUS to check for overcurrent, overtemperature, 65 | * or reverse-voltage conditions. 66 | * 67 | * @return true if there is a fault, false if everything is okay. 68 | */ 69 | bool getFaultStatus(); 70 | 71 | private: 72 | PinName _power; // Pin for controlling the power to the USBA VBUS 73 | PinName _usbflag; // Pin for reading the fault status of the USBA VBUS 74 | 75 | /** 76 | * @brief Enable power to the USBA VBUS. 77 | * 78 | * This private method is used to enable power to the USBA VBUS. 79 | */ 80 | void _enable(); 81 | 82 | /** 83 | * @brief Disable power to the USBA VBUS. 84 | * 85 | * This private method is used to disable power to the USBA VBUS. 86 | */ 87 | void _disable(); 88 | }; 89 | 90 | extern USBClass MachineControl_USBController; 91 | 92 | #endif /* __USB_CONTROLLER_CLASS_H */ -------------------------------------------------------------------------------- /src/RS485CommClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RS485CommClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the RS485CommClass used to initialize and interact with RS485 and RS232 communication protocols on the Portenta Machine Control board. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "RS485CommClass.h" 9 | #include 10 | 11 | /* Functions -----------------------------------------------------------------*/ 12 | 13 | RS485CommClass::RS485CommClass(arduino::UART& uart_itf, PinName rs_tx_pin, PinName rs_de_pin, PinName rs_re_pin) 14 | : RS485Class(uart_itf, PinNameToIndex(rs_tx_pin), PinNameToIndex(rs_de_pin), PinNameToIndex(rs_re_pin)) 15 | { } 16 | 17 | RS485CommClass::~RS485CommClass() 18 | { } 19 | 20 | void RS485CommClass::begin(unsigned long baudrate, uint16_t config, int predelay, int postdelay) { 21 | /* Pinout configuration */ 22 | pinMode(PinNameToIndex(MC_RS485_TX_PIN), OUTPUT); 23 | pinMode(PinNameToIndex(MC_RS485_RX_PIN), OUTPUT); 24 | 25 | pinMode(PinNameToIndex(MC_RS485_EN_PIN), OUTPUT); 26 | pinMode(PinNameToIndex(MC_RS485_RS232_PIN), OUTPUT); 27 | pinMode(PinNameToIndex(MC_RS485_FDTERM_PIN), OUTPUT); 28 | pinMode(PinNameToIndex(MC_RS485_TERM_PIN), OUTPUT); 29 | pinMode(PinNameToIndex(MC_RS485_SLEW_PIN), OUTPUT); 30 | pinMode(PinNameToIndex(MC_RS485_HF_PIN), OUTPUT); 31 | 32 | /* Turn-off LEDs RS485 */ 33 | digitalWrite(PinNameToIndex(MC_RS485_TX_PIN), LOW); 34 | digitalWrite(PinNameToIndex(MC_RS485_RX_PIN), LOW); 35 | 36 | /* Set defaults for RS485 */ 37 | _disable(); 38 | setModeRS232(false); 39 | setFullDuplex(false); 40 | setYZTerm(false); 41 | setABTerm(false); 42 | setSlew(false); 43 | 44 | /* Enable RS485 communication */ 45 | _enable(); 46 | 47 | /* Call begin() base class to initialize RS485 communication */ 48 | RS485Class::begin(baudrate, config, predelay, postdelay); 49 | 50 | return; 51 | } 52 | 53 | void RS485CommClass::end() { 54 | _disable(); 55 | 56 | /* Call end() base class to de-initialize RS485 communication */ 57 | RS485Class::end(); 58 | } 59 | 60 | void RS485CommClass::setModeRS232(bool enable) { 61 | digitalWrite(PinNameToIndex(MC_RS485_RS232_PIN), enable ? LOW : HIGH); 62 | } 63 | 64 | void RS485CommClass::setYZTerm(bool enable) { 65 | digitalWrite(PinNameToIndex(MC_RS485_FDTERM_PIN), enable ? HIGH : LOW); 66 | } 67 | 68 | void RS485CommClass::setABTerm(bool enable) { 69 | digitalWrite(PinNameToIndex(MC_RS485_TERM_PIN), enable ? HIGH : LOW); 70 | } 71 | 72 | void RS485CommClass::setSlew(bool enable) { 73 | digitalWrite(PinNameToIndex(MC_RS485_SLEW_PIN), enable ? LOW : HIGH); 74 | } 75 | 76 | void RS485CommClass::setFullDuplex(bool enable) { 77 | digitalWrite(PinNameToIndex(MC_RS485_HF_PIN), enable ? LOW : HIGH); 78 | if (enable) { 79 | // RS485 Full Duplex require YZ and AB 120 Ohm termination enabled 80 | setYZTerm(true); 81 | setABTerm(true); 82 | } 83 | } 84 | 85 | void RS485CommClass::_enable() { 86 | digitalWrite(PinNameToIndex(MC_RS485_EN_PIN), HIGH); 87 | } 88 | 89 | void RS485CommClass::_disable() { 90 | digitalWrite(PinNameToIndex(MC_RS485_EN_PIN), LOW); 91 | } 92 | 93 | arduino::UART _UART4_ {MC_RS485_TX_PIN, MC_RS485_RX_PIN, NC, NC}; 94 | RS485CommClass MachineControl_RS485Comm(_UART4_); 95 | /**** END OF FILE ****/ 96 | -------------------------------------------------------------------------------- /src/TCTempProbeClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TCTempProbeClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Thermocouple (TC) temperature sensor connector of the Portenta Machine Control. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "TCTempProbeClass.h" 9 | 10 | #if __has_include("portenta_info.h") 11 | #include "portenta_info.h" 12 | #define TRY_REV2_RECOGNITION 13 | uint8_t* boardInfo(); 14 | #define PMC_R2_SKU (24 << 8 | 3) 15 | #endif 16 | 17 | /* Functions -----------------------------------------------------------------*/ 18 | TCTempProbeClass::TCTempProbeClass(PinName tc_cs_pin, 19 | PinName ch_sel0_pin, 20 | PinName ch_sel1_pin, 21 | PinName ch_sel2_pin, 22 | PinName tc_th_pin) 23 | : MAX31855Class(tc_cs_pin), _tc_cs{tc_cs_pin}, _ch_sel0{ch_sel0_pin}, _ch_sel1{ch_sel1_pin}, _ch_sel2{ch_sel2_pin}, _tc_th{tc_th_pin} 24 | { } 25 | 26 | TCTempProbeClass::~TCTempProbeClass() 27 | { } 28 | 29 | bool TCTempProbeClass::begin() { 30 | MAX31855Class::begin(); 31 | 32 | pinMode(_ch_sel0, OUTPUT); 33 | pinMode(_ch_sel1, OUTPUT); 34 | pinMode(_ch_sel2, OUTPUT); 35 | pinMode(_tc_th, OUTPUT); 36 | 37 | pinMode(_tc_cs, OUTPUT); 38 | 39 | _enable(); 40 | 41 | return true; 42 | } 43 | 44 | void TCTempProbeClass::selectChannel(int channel) { 45 | 46 | #ifdef TRY_REV2_RECOGNITION 47 | // check if OTP data is present AND the board is mounted on a r2 carrier 48 | auto info = (PortentaBoardInfo*)boardInfo(); 49 | if (info->magic == 0xB5 && info->carrier == PMC_R2_SKU) { 50 | // reverse channels 0 and 2 51 | switch (channel) { 52 | case 0: 53 | channel = 2; 54 | break; 55 | case 2: 56 | channel = 0; 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | #endif 63 | #undef TRY_REV2_RECOGNITION 64 | switch(channel) { 65 | case 0: 66 | digitalWrite(_ch_sel0, HIGH); 67 | digitalWrite(_ch_sel1, LOW); 68 | digitalWrite(_ch_sel2, LOW); 69 | break; 70 | case 1: 71 | digitalWrite(_ch_sel0, LOW); 72 | digitalWrite(_ch_sel1, HIGH); 73 | digitalWrite(_ch_sel2, LOW); 74 | break; 75 | case 2: 76 | digitalWrite(_ch_sel0, LOW); 77 | digitalWrite(_ch_sel1, LOW); 78 | digitalWrite(_ch_sel2, HIGH); 79 | break; 80 | default: 81 | digitalWrite(_ch_sel0, LOW); 82 | digitalWrite(_ch_sel1, LOW); 83 | digitalWrite(_ch_sel2, LOW); 84 | break; 85 | } 86 | delay(150); 87 | } 88 | 89 | void TCTempProbeClass::end() { 90 | MAX31855Class::end(); 91 | 92 | _disable(); 93 | } 94 | 95 | void TCTempProbeClass::_enable() { 96 | digitalWrite(_tc_cs, LOW); 97 | digitalWrite(_tc_th, LOW); 98 | } 99 | 100 | void TCTempProbeClass::_disable() { 101 | digitalWrite(_tc_cs, HIGH); 102 | digitalWrite(_tc_th, HIGH); 103 | } 104 | 105 | TCTempProbeClass MachineControl_TCTempProbe; 106 | /**** END OF FILE ****/ 107 | -------------------------------------------------------------------------------- /src/RTDTempProbeClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RTDTempProbeClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Resistance Temperature Detector (RTD) temperature sensor connector of the Portenta Machine Control. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "RTDTempProbeClass.h" 9 | 10 | #if __has_include("portenta_info.h") 11 | #include "portenta_info.h" 12 | #define TRY_REV2_RECOGNITION 13 | uint8_t* boardInfo(); 14 | #define PMC_R2_SKU (24 << 8 | 3) 15 | #endif 16 | 17 | /* Functions -----------------------------------------------------------------*/ 18 | RTDTempProbeClass::RTDTempProbeClass(PinName rtd_cs_pin, 19 | PinName ch_sel0_pin, 20 | PinName ch_sel1_pin, 21 | PinName ch_sel2_pin, 22 | PinName rtd_th_pin) 23 | : MAX31865Class(rtd_cs_pin), _rtd_cs{rtd_cs_pin}, _ch_sel0{ch_sel0_pin}, _ch_sel1{ch_sel1_pin}, _ch_sel2{ch_sel2_pin}, _rtd_th{rtd_th_pin} 24 | { } 25 | 26 | RTDTempProbeClass::~RTDTempProbeClass() 27 | { } 28 | 29 | bool RTDTempProbeClass::begin(uint8_t io_address) { 30 | MAX31865Class::begin(io_address); 31 | 32 | pinMode(_ch_sel0, OUTPUT); 33 | pinMode(_ch_sel1, OUTPUT); 34 | pinMode(_ch_sel2, OUTPUT); 35 | pinMode(_rtd_th, OUTPUT); 36 | 37 | pinMode(_rtd_cs, OUTPUT); 38 | 39 | _enable(); 40 | 41 | return true; 42 | } 43 | 44 | void RTDTempProbeClass::selectChannel(int channel) { 45 | 46 | #ifdef TRY_REV2_RECOGNITION 47 | // check if OTP data is present AND the board is mounted on a r2 carrier 48 | auto info = (PortentaBoardInfo*)boardInfo(); 49 | if (info->magic == 0xB5 && info->carrier == PMC_R2_SKU) { 50 | // reverse channels 0 and 2 51 | switch (channel) { 52 | case 0: 53 | channel = 2; 54 | break; 55 | case 2: 56 | channel = 0; 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | #endif 63 | #undef TRY_REV2_RECOGNITION 64 | switch(channel) { 65 | case 0: 66 | digitalWrite(_ch_sel0, HIGH); 67 | digitalWrite(_ch_sel1, LOW); 68 | digitalWrite(_ch_sel2, LOW); 69 | break; 70 | case 1: 71 | digitalWrite(_ch_sel0, LOW); 72 | digitalWrite(_ch_sel1, HIGH); 73 | digitalWrite(_ch_sel2, LOW); 74 | break; 75 | case 2: 76 | digitalWrite(_ch_sel0, LOW); 77 | digitalWrite(_ch_sel1, LOW); 78 | digitalWrite(_ch_sel2, HIGH); 79 | break; 80 | default: 81 | digitalWrite(_ch_sel0, LOW); 82 | digitalWrite(_ch_sel1, LOW); 83 | digitalWrite(_ch_sel2, LOW); 84 | break; 85 | } 86 | delay(150); 87 | } 88 | 89 | void RTDTempProbeClass::end() { 90 | MAX31865Class::end(); 91 | 92 | _disable(); 93 | } 94 | 95 | void RTDTempProbeClass::_enable() { 96 | digitalWrite(_rtd_th, HIGH); 97 | 98 | digitalWrite(_rtd_cs, LOW); 99 | } 100 | 101 | void RTDTempProbeClass::_disable() { 102 | digitalWrite(_rtd_cs, HIGH); 103 | } 104 | 105 | RTDTempProbeClass MachineControl_RTDTempProbe; 106 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /examples/Ethernet/Ethernet.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Ethernet HTTP Request Example 3 | * 4 | * This sketch provides a demonstration of testing the Ethernet port on the Machine Control. 5 | * It shows how to create an HTTP request as a client, sending it to a remote server and 6 | * receiving the response. This example illustrates the basics of making HTTP requests using the Ethernet interface. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * 12 | * This example code is in the public domain. 13 | * Copyright (c) 2024 Arduino 14 | * SPDX-License-Identifier: MPL-2.0 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | EthernetClient client; 22 | char server[] = "www.ifconfig.io"; // name address (using DNS) 23 | 24 | unsigned long beginMicros, endMicros; 25 | unsigned long byteCount = 0; 26 | bool printWebData = true; // set to false for better speed measurement 27 | 28 | void setup() { 29 | Serial.begin(9600); 30 | while (!Serial) { 31 | ; 32 | } 33 | 34 | Serial.println("Ethernet example for H7 + PMC"); 35 | 36 | if (Ethernet.begin() == 0) { 37 | Serial.println("Failed to configure Ethernet using DHCP"); 38 | while(1); 39 | } else { 40 | Serial.print("DHCP assigned IP "); 41 | Serial.println(Ethernet.localIP()); 42 | } 43 | 44 | // give the Ethernet interface a second to initialize: 45 | delay(1000); 46 | Serial.print("connecting to "); 47 | Serial.print(server); 48 | Serial.println("..."); 49 | 50 | // if you get a connection, report back via serial: 51 | if (client.connect(server, 80)) { 52 | Serial.print("connected to "); 53 | Serial.println(client.remoteIP()); 54 | 55 | // Make a HTTP request: 56 | client.println("GET / HTTP/1.1"); 57 | client.println("Host: ifconfig.io"); 58 | client.println("User-Agent: curl/7.64.1"); 59 | client.println("Connection: close"); 60 | client.println("Accept: */*"); 61 | client.println(); 62 | } else { 63 | // if you didn't get a connection to the server: 64 | Serial.println("connection failed"); 65 | } 66 | 67 | beginMicros = micros(); 68 | } 69 | 70 | void loop() { 71 | // if there are incoming bytes available 72 | // from the server, read them and print them: 73 | int len = client.available(); 74 | if (len > 0) { 75 | byte buffer[80]; 76 | if (len > 80) 77 | len = 80; 78 | client.read(buffer, len); 79 | if (printWebData) { 80 | Serial.write(buffer, len); // show in the serial monitor (slows some boards) 81 | } 82 | byteCount = byteCount + len; 83 | } 84 | 85 | // if the server's disconnected, stop the client: 86 | if (!client.connected()) { 87 | endMicros = micros(); 88 | Serial.println(); 89 | Serial.println("disconnecting."); 90 | client.stop(); 91 | Serial.print("Received "); 92 | Serial.print(byteCount); 93 | Serial.print(" bytes in "); 94 | float seconds = (float)(endMicros - beginMicros) / 1000000.0; 95 | Serial.print(seconds, 4); 96 | float rate = (float)byteCount / seconds / 1000.0; 97 | Serial.print(", rate = "); 98 | Serial.print(rate); 99 | Serial.print(" kbytes/second"); 100 | Serial.println(); 101 | 102 | // do nothing forevermore: 103 | while (true) { 104 | delay(1); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/TCTempProbeClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TCTempProbeClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Thermocouple (TC) temperature sensor connector of the Portenta Machine Control. 5 | * 6 | * This library allows interfacing with TC temperature sensors using the MAX31855 digital converter. 7 | * It provides methods to select input channel, enabling and disabling the sensor, and reading temperature values. 8 | */ 9 | 10 | #ifndef __TC_TEMPPROBE_CLASS_H 11 | #define __TC_TEMPPROBE_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include "utility/MAX31865/MAX31865.h" 15 | #include "utility/THERMOCOUPLE/MAX31855.h" 16 | #include 17 | #include 18 | #include "pins_mc.h" 19 | 20 | /* Class ----------------------------------------------------------------------*/ 21 | 22 | /** 23 | * @class TCTempProbeClass 24 | * @brief Class for managing thermocouples temperature sensor of the Portenta Machine Control. 25 | * 26 | * This class allows interfacing with thermocouple temperature sensors through the use of the MAX31855 digital converter. 27 | * It provides methods to configure and read temperature values from the selected input channel. 28 | */ 29 | class TCTempProbeClass: public MAX31855Class { 30 | public: 31 | /** 32 | * @brief Construct a TCTempProbeClass object. 33 | * 34 | * This constructor initializes a TCTempProbeClass object with the specified pin assignments for channel selection and TC connection. 35 | * 36 | * @param tc_cs_pin The pin number for the chip select (CS) pin of the Thermocouple temperature sensor. 37 | * @param ch_sel0_pin The pin number for the first channel selection bit. 38 | * @param ch_sel1_pin The pin number for the second channel selection bit. 39 | * @param ch_sel2_pin The pin number for the third channel selection bit. 40 | */ 41 | TCTempProbeClass(PinName tc_cs_pin = MC_TC_CS_PIN, 42 | PinName ch_sel0_pin = MC_TC_SEL0_PIN, 43 | PinName ch_sel1_pin = MC_TC_SEL1_PIN, 44 | PinName ch_sel2_pin = MC_TC_SEL2_PIN, 45 | PinName tc_th_pin = MC_TC_TH_PIN); 46 | 47 | /** 48 | * @brief Destruct the TCTempProbeClass object. 49 | * 50 | * This destructor releases any resources used by the TCTempProbeClass object. 51 | */ 52 | ~TCTempProbeClass(); 53 | 54 | /** 55 | * @brief Initialize the TCTempProbeClass. 56 | * 57 | * @return true If initialization is successful, false otherwise. 58 | */ 59 | bool begin(); 60 | 61 | /** 62 | * @brief Disable the temperature sensors and release any resources. 63 | */ 64 | void end(); 65 | 66 | /** 67 | * @brief Select the input channel to be read (3 channels available). 68 | * 69 | * @param channel The channel number (0-2) to be selected for temperature reading. 70 | */ 71 | void selectChannel(int channel); 72 | 73 | private: 74 | PinName _tc_cs; // Pin for the CS of Thermocouple 75 | PinName _ch_sel0; // Pin for the first channel selection bit 76 | PinName _ch_sel1; // Pin for the second channel selection bit 77 | PinName _ch_sel2; // Pin for the third channel selection bit 78 | PinName _tc_th; // Pin for the TC/RTD connection 79 | 80 | /** 81 | * @brief Enable the chip select (CS) of the MAX31855 digital converter. 82 | */ 83 | void _enable(); 84 | 85 | /** 86 | * @brief Disable the chip select (CS) of the MAX31855 digital converter. 87 | */ 88 | void _disable(); 89 | }; 90 | 91 | extern TCTempProbeClass MachineControl_TCTempProbe; 92 | 93 | #endif /* __TC_TEMPPROBE_CLASS_H */ 94 | -------------------------------------------------------------------------------- /examples/Digital_programmable/GPIO_programmable/GPIO_programmable.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - IOExpander Read and Write Example 3 | * 4 | * This sketch shows the utilization of the GPIO Expanders on the Machine Control. 5 | * It demonstrates the periodic transmission of values on the PROGRAMMABLE DIGITAL I/O output channels 6 | * and the periodic reading from the PROGRAMMABLE DIGITAL I/O input channels. 7 | * 8 | * Circuit: 9 | * - Portenta H7 10 | * - Portenta Machine Control 11 | * 12 | * This example code is in the public domain. 13 | * Copyright (c) 2024 Arduino 14 | * SPDX-License-Identifier: MPL-2.0 15 | */ 16 | 17 | #include 18 | 19 | void setup() { 20 | Serial.begin(9600); 21 | while (!Serial) { 22 | ; // wait for serial port to connect. 23 | } 24 | 25 | Wire.begin(); 26 | 27 | if (!MachineControl_DigitalProgrammables.begin()) { 28 | Serial.println("GPIO expander initialization fail!!"); 29 | } 30 | 31 | Serial.println("GPIO expander initialization done"); 32 | } 33 | 34 | void loop() { 35 | // Write the status value to On to Pin 3 36 | MachineControl_DigitalProgrammables.set(IO_WRITE_CH_PIN_03, SWITCH_ON); 37 | delay(1000); 38 | 39 | // Read from Pin 3 40 | Serial.println("Read Pin 03: " + String(MachineControl_DigitalProgrammables.read(IO_READ_CH_PIN_03))); 41 | delay(1000); 42 | 43 | // Write the status value to Off to Pin 3 44 | MachineControl_DigitalProgrammables.set(IO_WRITE_CH_PIN_03, SWITCH_OFF); 45 | delay(1000); 46 | 47 | Serial.println(); 48 | // Sets all the status Pins Values to On in one single operation 49 | uint32_t status = ON_VALUE_PIN_10 | ON_VALUE_PIN_08 | ON_VALUE_PIN_06 | ON_VALUE_PIN_04 | ON_VALUE_PIN_02 | ON_VALUE_PIN_00; 50 | MachineControl_DigitalProgrammables.writeAll(status); 51 | delay(1000); 52 | 53 | // Toggles the actual status values of all digital programmables Pins 54 | MachineControl_DigitalProgrammables.toggle(); 55 | delay(1000); 56 | 57 | Serial.println(); 58 | // Write the status value to On to all the Output Pins 59 | MachineControl_DigitalProgrammables.writeAll(SWITCH_ON_ALL); 60 | 61 | // Reads from all Input Pins 62 | readAll(); 63 | delay(1000); 64 | 65 | // Write the status value to Off all to all the Output Pins 66 | MachineControl_DigitalProgrammables.writeAll(SWITCH_OFF_ALL); 67 | 68 | // Reads from all Input Pins 69 | readAll(); 70 | Serial.println(); 71 | delay(1000); 72 | } 73 | 74 | uint8_t readAll() { 75 | uint32_t inputs = MachineControl_DigitalProgrammables.readAll(); 76 | Serial.println("CH00: " + String((inputs & (1 << IO_READ_CH_PIN_00)) >> IO_READ_CH_PIN_00)); 77 | Serial.println("CH01: " + String((inputs & (1 << IO_READ_CH_PIN_01)) >> IO_READ_CH_PIN_01)); 78 | Serial.println("CH02: " + String((inputs & (1 << IO_READ_CH_PIN_02)) >> IO_READ_CH_PIN_02)); 79 | Serial.println("CH03: " + String((inputs & (1 << IO_READ_CH_PIN_03)) >> IO_READ_CH_PIN_03)); 80 | Serial.println("CH04: " + String((inputs & (1 << IO_READ_CH_PIN_04)) >> IO_READ_CH_PIN_04)); 81 | Serial.println("CH05: " + String((inputs & (1 << IO_READ_CH_PIN_05)) >> IO_READ_CH_PIN_05)); 82 | Serial.println("CH06: " + String((inputs & (1 << IO_READ_CH_PIN_06)) >> IO_READ_CH_PIN_06)); 83 | Serial.println("CH07: " + String((inputs & (1 << IO_READ_CH_PIN_07)) >> IO_READ_CH_PIN_07)); 84 | Serial.println("CH08: " + String((inputs & (1 << IO_READ_CH_PIN_08)) >> IO_READ_CH_PIN_08)); 85 | Serial.println("CH09: " + String((inputs & (1 << IO_READ_CH_PIN_09)) >> IO_READ_CH_PIN_09)); 86 | Serial.println("CH10: " + String((inputs & (1 << IO_READ_CH_PIN_10)) >> IO_READ_CH_PIN_10)); 87 | Serial.println("CH11: " + String((inputs & (1 << IO_READ_CH_PIN_11)) >> IO_READ_CH_PIN_11)); 88 | Serial.println(); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /examples/Digital_output/Digital_output.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Digital Output Example 3 | * 4 | * This sketch shows how to send values on the DIGITAL OUT channels on the Machine Control. 5 | * The DIGITAL OUT channels are high-side switches capable of handling up to 0.5A. 6 | * There is an overcurrent protection that opens the channel when the current exceeds 0.7A with a +-20% tolerance. 7 | * The overcurrent protection can be set to operate in two different modes for all channels: 8 | * 1) Latch mode: When overcurrent is detected, the channel remains open until toggled via software. 9 | * 2) Auto retry: When overcurrent is detected, the channel opens, but after several tens of milliseconds, 10 | * it attempts to close itself automatically. If overcurrent persists, the channel will continuously toggle. 11 | * 12 | * Circuit: 13 | * - Portenta H7 14 | * - Portenta Machine Control 15 | * 16 | * NOTE: connect pin "24V IN" of the DIGITAL_OUTPUTS connector to 24V 17 | * 18 | * This example code is in the public domain. 19 | * Copyright (c) 2024 Arduino 20 | * SPDX-License-Identifier: MPL-2.0 21 | */ 22 | 23 | #include 24 | 25 | void setup() { 26 | Serial.begin(9600); 27 | while (!Serial) { 28 | ; // wait for serial port to connect. 29 | } 30 | 31 | //Set over current behavior of all channels to latch mode (true) 32 | MachineControl_DigitalOutputs.begin(true); 33 | 34 | //At startup set all channels to OPEN 35 | MachineControl_DigitalOutputs.writeAll(0); 36 | } 37 | 38 | void loop() { 39 | Serial.println("DIGITAL OUT:"); 40 | 41 | // Set all channels to CLOSED 42 | MachineControl_DigitalOutputs.writeAll(255); 43 | Serial.print("All channels are CLOSED for 1 s..."); 44 | delay(1000); 45 | 46 | // Set all channels to OPEN 47 | MachineControl_DigitalOutputs.writeAll(0); 48 | Serial.println("now they are OPEN."); 49 | delay(1000); 50 | 51 | // Toggle each channel for 1 s, one by one 52 | 53 | MachineControl_DigitalOutputs.write(0, HIGH); 54 | Serial.print("CH0 is CLOSED for 1 s..."); 55 | delay(1000); 56 | MachineControl_DigitalOutputs.write(0, LOW); 57 | Serial.println("now is OPEN."); 58 | 59 | MachineControl_DigitalOutputs.write(1, HIGH); 60 | Serial.print("CH1 is CLOSED for 1 s..."); 61 | delay(1000); 62 | MachineControl_DigitalOutputs.write(1, LOW); 63 | Serial.println("now is OPEN."); 64 | 65 | MachineControl_DigitalOutputs.write(2, HIGH); 66 | Serial.print("CH2 is CLOSED for 1 s..."); 67 | delay(1000); 68 | MachineControl_DigitalOutputs.write(2, LOW); 69 | Serial.println("now is OPEN."); 70 | 71 | MachineControl_DigitalOutputs.write(3, HIGH); 72 | Serial.print("CH3 is CLOSED for 1 s..."); 73 | delay(1000); 74 | MachineControl_DigitalOutputs.write(3, LOW); 75 | Serial.println("now is OPEN."); 76 | 77 | MachineControl_DigitalOutputs.write(4, HIGH); 78 | Serial.print("CH4 is CLOSED for 1 s..."); 79 | delay(1000); 80 | MachineControl_DigitalOutputs.write(4, LOW); 81 | Serial.println("now is OPEN."); 82 | 83 | MachineControl_DigitalOutputs.write(5, HIGH); 84 | Serial.print("CH5 is CLOSED for 1 s..."); 85 | delay(1000); 86 | MachineControl_DigitalOutputs.write(5, LOW); 87 | Serial.println("now is OPEN."); 88 | 89 | MachineControl_DigitalOutputs.write(6, HIGH); 90 | Serial.print("CH6 is CLOSED for 1 s..."); 91 | delay(1000); 92 | MachineControl_DigitalOutputs.write(6, LOW); 93 | Serial.println("now is OPEN."); 94 | 95 | MachineControl_DigitalOutputs.write(7, HIGH); 96 | Serial.print("CH7 is CLOSED for 1 s..."); 97 | delay(1000); 98 | MachineControl_DigitalOutputs.write(7, LOW); 99 | Serial.println("now is OPEN."); 100 | 101 | Serial.println(); 102 | delay(1000); 103 | } -------------------------------------------------------------------------------- /examples/Analog_input/Fast_Analog_input_0_10V/Fast_Analog_input_0_10V.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Fast Analog In 0-10 V 3 | * 4 | * This example provides the voltage value acquired by the Machine Control using the Arduino_AdvancedAnalog library, 5 | * which is designed to offer enhanced performance in terms of acquisition speed compared to the MachineControl_AnalogIn feature. 6 | * For each channel of the ANALOG IN connector, there is a resistor divider made by a 100k and 39k; the input voltage is divided by a ratio of 0.28. 7 | * The maximum input voltage is 10V. 8 | * To use the 0V-10V functionality, a 24V supply on the PWR SUPPLY connector is necessary. 9 | * 10 | * The circuit: 11 | * - Portenta H7 12 | * - Portenta Machine Control 13 | * 14 | * This example code is in the public domain. 15 | * Copyright (c) 2024 Arduino 16 | * SPDX-License-Identifier: MPL-2.0 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | const float RES_DIVIDER = 0.28057; 23 | const float REFERENCE = 3.0; 24 | 25 | //A3 is connected to PMC-AI0, A2 is connected to PMC-AI1 26 | AdvancedADC MachineControl_FastAnalogIn01(A3, A2); //A3 & A2 share the same ADC instance (ADC3) 27 | //A1 is connected to PMC-AI2 28 | AdvancedADC MachineControl_FastAnalogIn2(A1); 29 | 30 | uint16_t adc_get_buf(AdvancedADC &adc); 31 | void adc_get_buf(AdvancedADC &adc, uint16_t * sample_buf, uint8_t sample_buf_size); 32 | 33 | void setup() { 34 | Serial.begin(9600); 35 | while (!Serial) { 36 | ; // wait for serial port to connect. 37 | } 38 | 39 | // Configure the sensor type 0-10V 40 | MachineControl_AnalogIn.begin(SensorType::V_0_10); 41 | 42 | // Initialize the Advanced Analog feature on PMC AI pins 43 | if (!MachineControl_FastAnalogIn01.begin(AN_RESOLUTION_16, 8000, 32, 3)) { 44 | Serial.println("AI0, AI1: Failed to start analog acquisition!"); 45 | while (1); 46 | } 47 | 48 | if (!MachineControl_FastAnalogIn2.begin(AN_RESOLUTION_16, 8000, 32, 3)) { 49 | Serial.println("AI2: Failed to start analog acquisition!"); 50 | while (1); 51 | } 52 | } 53 | 54 | void loop() { 55 | uint16_t raw_voltage_ch01[2] = {0, 0}; 56 | uint16_t raw_voltage_ch2 = 0; 57 | 58 | adc_get_buf(MachineControl_FastAnalogIn01, raw_voltage_ch01, 2); 59 | float voltage_ch0 = ((float)raw_voltage_ch01[0] * REFERENCE) / 65535 / RES_DIVIDER; 60 | Serial.print("Voltage CH0: "); 61 | Serial.print(voltage_ch0, 3); 62 | Serial.println("V"); 63 | 64 | float voltage_ch1 = ((float)raw_voltage_ch01[1] * REFERENCE) / 65535 / RES_DIVIDER; 65 | Serial.print("Voltage CH1: "); 66 | Serial.print(voltage_ch1, 3); 67 | Serial.println("V"); 68 | 69 | raw_voltage_ch2 = adc_get_buf(MachineControl_FastAnalogIn2); 70 | float voltage_ch2 = ((float)raw_voltage_ch2 * REFERENCE) / 65535 / RES_DIVIDER; 71 | Serial.print("Voltage CH2: "); 72 | Serial.print(voltage_ch2, 3); 73 | Serial.println("V"); 74 | 75 | Serial.println(); 76 | delay(250); 77 | } 78 | 79 | uint16_t adc_get_buf(AdvancedADC &adc) { 80 | uint16_t sample = 0; 81 | 82 | if (adc.available()) { 83 | SampleBuffer buf = adc.read(); 84 | 85 | // Print first sample. 86 | sample = buf[0]; 87 | Serial.println(sample); 88 | 89 | // Release the buffer to return it to the pool. 90 | buf.release(); 91 | } 92 | 93 | return sample; 94 | } 95 | 96 | void adc_get_buf(AdvancedADC &adc, uint16_t * sample_buf, uint8_t sample_buf_size) { 97 | if (adc.available()) { 98 | SampleBuffer buf = adc.read(); 99 | 100 | for (uint8_t pos = 0; pos < sample_buf_size; pos++) { 101 | sample_buf[pos] = buf[pos]; 102 | Serial.println(sample_buf[pos]); 103 | } 104 | 105 | // Release the buffer to return it to the pool. 106 | buf.release(); 107 | } 108 | 109 | return; 110 | } -------------------------------------------------------------------------------- /src/RTDTempProbeClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RTDTempProbeClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Resistance Temperature Detector (RTD) temperature sensor connector of the Portenta Machine Control. 5 | * 6 | * This library allows interfacing with RTD temperature sensors using the MAX31865 digital converter. 7 | * It provides methods to select input channel, enabling and disabling the sensor, and reading temperature values. 8 | */ 9 | 10 | #ifndef __RTD_TEMPPROBE_CLASS_H 11 | #define __RTD_TEMPPROBE_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include "utility/MAX31865/MAX31865.h" 15 | #include "utility/THERMOCOUPLE/MAX31855.h" 16 | #include 17 | #include 18 | #include "pins_mc.h" 19 | 20 | /* Class ----------------------------------------------------------------------*/ 21 | 22 | /** 23 | * @class RTDTempProbeClass 24 | * @brief Class for managing RTD temperature sensor inputs of the Portenta Machine Control. 25 | * 26 | * This class allows interfacing with RTD temperature sensors through the use of the MAX31865 digital converter. 27 | * It provides methods to configure and read temperature values from the selected input channel. 28 | */ 29 | class RTDTempProbeClass: public MAX31865Class { 30 | public: 31 | /** 32 | * @brief Construct a RTDTempProbeClass object. 33 | * 34 | * This constructor initializes a RTDTempProbeClass object with the specified pin assignments for channel selection and RTD connection. 35 | * 36 | * @param rtd_cs_pin The pin number for the chip select (CS) pin of the RTD temperature sensor. 37 | * @param ch_sel0_pin The pin number for the first channel selection bit. 38 | * @param ch_sel1_pin The pin number for the second channel selection bit. 39 | * @param ch_sel2_pin The pin number for the third channel selection bit. 40 | * @param rtd_th_pin The pin number for the RTD connection. 41 | */ 42 | RTDTempProbeClass(PinName rtd_cs_pin = MC_RTD_CS_PIN, 43 | PinName ch_sel0_pin = MC_RTD_SEL0_PIN, 44 | PinName ch_sel1_pin = MC_RTD_SEL1_PIN, 45 | PinName ch_sel2_pin = MC_RTD_SEL2_PIN, 46 | PinName rtd_th_pin = MC_RTD_TH_PIN); 47 | 48 | /** 49 | * @brief Destruct the RTDTempProbeClass object. 50 | * 51 | * This destructor releases any resources used by the RTDTempProbeClass object. 52 | */ 53 | ~RTDTempProbeClass(); 54 | 55 | /** 56 | * @brief Initialize the RTDTempProbeClass with the specified I/O address. 57 | * 58 | * @param io_address The I/O address for communication with the digital converters (default is THREE_WIRE). 59 | * @return true If initialization is successful, false otherwise. 60 | */ 61 | bool begin(uint8_t io_address = THREE_WIRE); 62 | 63 | /** 64 | * @brief Disable the temperature sensors and release any resources. 65 | */ 66 | void end(); 67 | 68 | /** 69 | * @brief Select the input channel to be read (3 channels available). 70 | * 71 | * @param channel The channel number (0-2) to be selected for temperature reading. 72 | */ 73 | void selectChannel(int channel); 74 | 75 | private: 76 | PinName _rtd_cs; // Pin for the CS of RTD 77 | PinName _ch_sel0; // Pin for the first channel selection bit 78 | PinName _ch_sel1; // Pin for the second channel selection bit 79 | PinName _ch_sel2; // Pin for the third channel selection bit 80 | PinName _rtd_th; // Pin for the RTD connection 81 | 82 | /** 83 | * @brief Enable the chip select (CS) of the MAX31865 digital converter. 84 | */ 85 | void _enable(); 86 | 87 | /** 88 | * @brief Disable the chip select (CS) of the MAX31865 digital converter. 89 | */ 90 | void _disable(); 91 | }; 92 | 93 | extern RTDTempProbeClass MachineControl_RTDTempProbe; 94 | 95 | #endif /* __RTD_TEMPPROBE_CLASS_H */ 96 | -------------------------------------------------------------------------------- /src/utility/THERMOCOUPLE/MAX31855.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAX31855_H_ 2 | #define _MAX31855_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define PROBE_K 0 9 | #define PROBE_J 1 10 | 11 | class MAX31855Class { 12 | public: 13 | MAX31855Class(PinName cs = PI_0, SPIClass& spi = SPI); 14 | 15 | int begin(); 16 | void end(); 17 | 18 | float readTemperature(int type = PROBE_K); 19 | float readReferenceTemperature(int type = PROBE_K); 20 | void setColdOffset(float offset); 21 | 22 | private: 23 | uint32_t readSensor(); 24 | float _coldOffset; 25 | PinName _cs; 26 | SPIClass* _spi; 27 | SPISettings _spiSettings; 28 | 29 | // NIST coefficient tables 30 | static constexpr double Jm210_760[] = { 0.000000000000E+00, 0.503811878150E-01, 0.304758369300E-04,-0.856810657200E-07, 0.132281952950E-09,-0.170529583370E-12, 0.209480906970E-15,-0.125383953360E-18, 0.156317256970E-22 }; 31 | static constexpr double J760_1200[] = { 0.296456256810E+03,-0.149761277860E+01, 0.317871039240E-02,-0.318476867010E-05, 0.157208190040E-08,-0.306913690560E-12 }; 32 | 33 | static constexpr double Km270_0[] = { 0.000000000000E+00, 0.394501280250E-01, 0.236223735980E-04,-0.328589067840E-06,-0.499048287770E-08,-0.675090591730E-10,-0.574103274280E-12,-0.310888728940E-14,-0.104516093650E-16,-0.198892668780E-19,-0.163226974860E-22 }; 34 | static constexpr double K0_1372[] = { -0.176004136860E-01, 0.389212049750E-01, 0.185587700320E-04,-0.994575928740E-07, 0.318409457190E-09,-0.560728448890E-12, 0.560750590590E-15,-0.320207200030E-18, 0.971511471520E-22,-0.121047212750E-25 }; 35 | 36 | static constexpr double InvJ_neg[] = { 0.0000000E+00, 1.9528268E+01, -1.2286185E+00, -1.0752178E+00, -5.9086933E-01, -1.7256713E-01, -2.8131513E-02,-2.3963370E-03,-8.3823321E-05}; 37 | static constexpr double InvJ0_760[] = { 0.000000E+00, 1.978425E+01, -2.001204E-01, 1.036969E-02, -2.549687E-04, 3.585153E-06, -5.344285E-08, 5.099890E-10 }; 38 | static constexpr double InvJ760_1200[] = { -3.11358187E+03, 3.00543684E+02, -9.94773230E+00, 1.70276630E-01, -1.43033468E-03, 4.73886084E-06 }; 39 | 40 | static constexpr double InvK_neg[] = { 0.0000000E+00, 2.5173462E+01, 1.1662878E+00, 1.0833638E+00, 8.9773540E-01, 3.7342377E-01, 8.6632643E-02, 1.0450598E-02, 5.1920577E-04 }; 41 | static constexpr double InvK0_500[] = { 0.000000E+00, 2.508355E+01, 7.860106E-02, -2.503131E-01, 8.315270E-02, -1.228034E-02, 9.804036E-04, -4.413030E-05, 1.057734E-06, -1.052755E-08 }; 42 | static constexpr double InvK500_1372[] = { -1.318058E+02, 4.830222E+01, -1.646031E+00, 5.464731E-02, -9.650715E-04, 8.802193E-06, -3.110810E-08 }; 43 | 44 | typedef struct { 45 | int size; 46 | double max; 47 | const double *coeffs; 48 | } coefftable; 49 | 50 | static constexpr coefftable CoeffJ []= { 51 | {0,-210, NULL}, 52 | {sizeof(Jm210_760) / sizeof(double), 760.0f, &Jm210_760[0]}, 53 | {sizeof(J760_1200) / sizeof(double), 1200.0f, &J760_1200[0]} 54 | }; 55 | 56 | static constexpr coefftable CoeffK []= { 57 | {0,-270, NULL}, 58 | {sizeof(Jm210_760) / sizeof(double), 0.0f, &Km270_0[0]}, 59 | {sizeof(K0_1372) / sizeof(double), 1200.0f, &K0_1372[0]} 60 | }; 61 | 62 | static constexpr coefftable InvCoeffJ []= { 63 | {0,-0.895, NULL}, 64 | {sizeof(InvJ_neg) / sizeof(double), 0.0f, &InvJ_neg[0]}, 65 | {sizeof(InvJ0_760) / sizeof(double), 42.919f, &InvJ0_760[0]}, 66 | {sizeof(InvJ760_1200) / sizeof(double), 69.533f, &InvJ760_1200[0]} 67 | }; 68 | 69 | static constexpr coefftable InvCoeffK []= { 70 | {0,-5.891, NULL}, 71 | {sizeof(InvK_neg) / sizeof(double), 0.0f, &InvK_neg[0]}, 72 | {sizeof(InvK0_500) / sizeof(double), 20.644f, &InvK0_500[0]}, 73 | {sizeof(InvK500_1372) / sizeof(double), 54.886f, &InvK500_1372[0]}, 74 | }; 75 | 76 | double mvtoTemp(int type, double voltage); 77 | double coldTempTomv(int type, double temp); 78 | double polynomial(double value, int tableEntries, coefftable const (*table) ); 79 | 80 | }; 81 | 82 | extern MAX31855Class THERM; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /src/utility/ioexpander/ArduinoIOExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "ArduinoIOExpander.h" 2 | 3 | bool ArduinoIOExpanderClass::begin() 4 | { 5 | if(!_tca.testConnection()) { 6 | return false; 7 | } 8 | //Initialize all pins to the default mode 9 | initPins(); 10 | 11 | return true; 12 | } 13 | 14 | bool ArduinoIOExpanderClass::begin(uint8_t address) 15 | { 16 | setAddress(address); 17 | if(!_tca.testConnection()) { 18 | return false; 19 | } 20 | //Initialize all pins to the default mode 21 | initPins(); 22 | 23 | return true; 24 | } 25 | 26 | ArduinoIOExpanderClass::operator bool() 27 | { 28 | return _tca.testConnection(); 29 | } 30 | 31 | bool ArduinoIOExpanderClass::pinMode(int pin, PinMode direction) 32 | { 33 | if (direction > OUTPUT) 34 | return false; 35 | 36 | _tca.setPinDirection(pin, direction == INPUT ? TCA6424A_INPUT : TCA6424A_OUTPUT); 37 | return true; 38 | } 39 | 40 | void ArduinoIOExpanderClass::setAddress(uint8_t address) { 41 | _tca.setAddress(address); 42 | } 43 | 44 | bool ArduinoIOExpanderClass::set(int pin, PinStatus status) 45 | { 46 | if (pin < IO_READ_CH_PIN_00) { 47 | if (status > HIGH) 48 | return false; 49 | 50 | _tca.writePin(pin, status == HIGH ? TCA6424A_HIGH : TCA6424A_LOW); 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | int ArduinoIOExpanderClass::read(int pin) 57 | { 58 | if(_tca.getAddress() == IO_ADD) { 59 | if (pin > TCA6424A_P13 && pin <= TCA6424A_P27) { 60 | return _tca.readPin(pin) == true ? 1 : 0; 61 | } 62 | } else if(_tca.getAddress() == DIN_ADD) { 63 | if ((pin >= TCA6424A_P00) && (pin <= TCA6424A_P10) && (pin !=TCA6424A_P03)) { 64 | return _tca.readPin(pin) == true ? 1 : 0; 65 | } 66 | } 67 | return -1; 68 | } 69 | 70 | void ArduinoIOExpanderClass::writeAll(uint32_t banks) { 71 | _tca.writeAll(banks & 0xFF, (banks >> 8) & 0xFF, 0x00); 72 | } 73 | 74 | uint32_t ArduinoIOExpanderClass::readAll() 75 | { 76 | uint8_t banks[3]; 77 | _tca.readAll(banks); 78 | return *(uint32_t*)banks; 79 | } 80 | 81 | 82 | void ArduinoIOExpanderClass::toggle(){ 83 | writeAll(~(readAll())); 84 | } 85 | 86 | void ArduinoIOExpanderClass::initPins() 87 | { 88 | 89 | if (_tca.getAddress() == IO_ADD) { 90 | PinStatus status = SWITCH_OFF; 91 | set(IO_WRITE_CH_PIN_00, status); 92 | set(IO_WRITE_CH_PIN_01, status); 93 | set(IO_WRITE_CH_PIN_02, status); 94 | set(IO_WRITE_CH_PIN_03, status); 95 | set(IO_WRITE_CH_PIN_04, status); 96 | set(IO_WRITE_CH_PIN_05, status); 97 | set(IO_WRITE_CH_PIN_06, status); 98 | set(IO_WRITE_CH_PIN_07, status); 99 | set(IO_WRITE_CH_PIN_08, status); 100 | set(IO_WRITE_CH_PIN_09, status); 101 | set(IO_WRITE_CH_PIN_10, status); 102 | set(IO_WRITE_CH_PIN_11, status); 103 | 104 | pinMode(IO_WRITE_CH_PIN_00, OUTPUT); 105 | pinMode(IO_WRITE_CH_PIN_01, OUTPUT); 106 | pinMode(IO_WRITE_CH_PIN_02, OUTPUT); 107 | pinMode(IO_WRITE_CH_PIN_03, OUTPUT); 108 | pinMode(IO_WRITE_CH_PIN_04, OUTPUT); 109 | pinMode(IO_WRITE_CH_PIN_05, OUTPUT); 110 | pinMode(IO_WRITE_CH_PIN_06, OUTPUT); 111 | pinMode(IO_WRITE_CH_PIN_07, OUTPUT); 112 | pinMode(IO_WRITE_CH_PIN_08, OUTPUT); 113 | pinMode(IO_WRITE_CH_PIN_09, OUTPUT); 114 | pinMode(IO_WRITE_CH_PIN_10, OUTPUT); 115 | pinMode(IO_WRITE_CH_PIN_11, OUTPUT); 116 | pinMode(IO_READ_CH_PIN_00, INPUT); 117 | pinMode(IO_READ_CH_PIN_01, INPUT); 118 | pinMode(IO_READ_CH_PIN_02, INPUT); 119 | pinMode(IO_READ_CH_PIN_03, INPUT); 120 | pinMode(IO_READ_CH_PIN_04, INPUT); 121 | pinMode(IO_READ_CH_PIN_05, INPUT); 122 | pinMode(IO_READ_CH_PIN_06, INPUT); 123 | pinMode(IO_READ_CH_PIN_07, INPUT); 124 | pinMode(IO_READ_CH_PIN_08, INPUT); 125 | pinMode(IO_READ_CH_PIN_09, INPUT); 126 | pinMode(IO_READ_CH_PIN_10, INPUT); 127 | pinMode(IO_READ_CH_PIN_11, INPUT); 128 | 129 | writeAll(SWITCH_OFF_ALL); 130 | } else { 131 | pinMode(DIN_READ_CH_PIN_00, INPUT); 132 | pinMode(DIN_READ_CH_PIN_01, INPUT); 133 | pinMode(DIN_READ_CH_PIN_02, INPUT); 134 | pinMode(DIN_READ_CH_PIN_03, INPUT); 135 | pinMode(DIN_READ_CH_PIN_04, INPUT); 136 | pinMode(DIN_READ_CH_PIN_05, INPUT); 137 | pinMode(DIN_READ_CH_PIN_06, INPUT); 138 | pinMode(DIN_READ_CH_PIN_07, INPUT); 139 | } 140 | } 141 | 142 | ArduinoIOExpanderClass Expander; 143 | -------------------------------------------------------------------------------- /src/CANCommClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CANCommClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the CANCommClass used to initialize and interact with the CAN Bus communication protocol on the Portenta Machine Control board. 5 | * 6 | * This library provides a class to manage the CAN Bus communication protocol of the Portenta Machine Control board. 7 | * It allows initializing and interacting with the CAN Bus protocol. 8 | */ 9 | 10 | #ifndef __CAN_COMM_CLASS_H 11 | #define __CAN_COMM_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include 15 | #include 16 | #include 17 | #include "pins_mc.h" 18 | 19 | /* Class ----------------------------------------------------------------------*/ 20 | 21 | /** 22 | * @class CANCommClass 23 | * @brief Class for managing the CAN Bus communication protocol of the Portenta Machine Control. 24 | * 25 | * The `CANCommClass` provides methods to work with the CAN Bus communication protocol on the Portenta Machine Control board. 26 | */ 27 | class CANCommClass { 28 | public: 29 | /** 30 | * @brief Construct a CANCommClass object. 31 | * 32 | * This constructor initializes a CANCommClass object with the specified CAN_TX and CAN_RX pins. 33 | * 34 | * @param can_tx_pin The pin for transmitting data on the CAN Bus. 35 | * @param can_rx_pin The pin for receiving data on the CAN Bus. 36 | * @param can_stb_pin The pin to control the standby (low-power) mode of the CAN transceiver. 37 | */ 38 | CANCommClass(PinName can_tx_pin = MC_CAN_TX_PIN, PinName can_rx_pin = MC_CAN_RX_PIN, PinName can_stb_pin = MC_CAN_STB_PIN); 39 | 40 | /** 41 | * @brief Destruct the CANCommClass object. 42 | * 43 | * This destructor releases any resources used by the CANCommClass object. 44 | */ 45 | ~CANCommClass(); 46 | 47 | /** 48 | * @brief Begin the CAN communication protocol. 49 | * 50 | * This method initializes the CAN communication protocol. 51 | * 52 | * @param can_bitrate The desired bitrate for the CAN communication protocol. 53 | * @return true If the initialization is successful, false otherwise. 54 | */ 55 | bool begin(CanBitRate can_bitrate); 56 | 57 | /** 58 | * @brief Write a CAN message to the bus. 59 | * 60 | * This method sends a CAN message over the bus. 61 | * 62 | * @param msg The CAN message to be sent, represented by a `CanMsg` object. 63 | * @return The number of bytes sent or <=0 in case of an error. 64 | */ 65 | int write(CanMsg const & msg); 66 | 67 | /** 68 | * @brief Check the number of available CAN messages in the receive buffer. 69 | * 70 | * This method checks the number of CAN messages available to read from the bus. 71 | * 72 | * @return The number of available CAN messages. 73 | */ 74 | size_t available(); 75 | 76 | /** 77 | * @brief Read a CAN message from the bus. 78 | * 79 | * This method reads a CAN message from the receive buffer. 80 | * 81 | * @return The received CAN message as a `CanMsg` object. 82 | */ 83 | CanMsg read(); 84 | 85 | /** 86 | * @brief Close the CAN communication protocol. 87 | * 88 | * This method de-initializes the CAN communication protocol, stopping communication on the CAN Bus. 89 | */ 90 | void end(); 91 | 92 | private: 93 | Arduino_CAN _can; 94 | PinName _tx; 95 | PinName _rx; 96 | PinName _stb; 97 | 98 | /** 99 | * @brief Set the CAN transceiver in Normal mode. 100 | * 101 | * In this mode, the CAN transceiver can transmit and receive data via the bus lines CANH and CANL. 102 | */ 103 | void _enable(); 104 | 105 | /** 106 | * @brief Set the CAN transceiver in Standby (low power) mode. 107 | * 108 | * In this mode, the CAN transceiver will not be able to transmit or correctly receive data via the bus lines. 109 | * The wake-up filter on the output of the low-power receiver does not latch bus dominant states, 110 | * but ensures that only bus dominant and bus recessive states that persist longer than tfltr(wake) 111 | * bus are reflected on pin RXD. 112 | */ 113 | void _disable(); 114 | }; 115 | 116 | extern CANCommClass MachineControl_CANComm; 117 | 118 | #endif /* __CAN_COMM_CLASS_H */ 119 | -------------------------------------------------------------------------------- /src/EncoderClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EncoderClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the encoder module of the Portenta Machine Control. 5 | * 6 | * This library provides a class to manage the Quadrature Encoder Interface devices 7 | * of the Portenta Machine Control. It allows interfacing with two encoders through 8 | * the QEI (Quadrature Encoder Interface) library and provides methods to access 9 | * and control each encoder individually. 10 | */ 11 | 12 | #ifndef __ENCODER_CLASS_H 13 | #define __ENCODER_CLASS_H 14 | 15 | /* Includes -------------------------------------------------------------------*/ 16 | #include "utility/QEI/QEI.h" 17 | #include 18 | #include 19 | #include "pins_mc.h" 20 | 21 | /* Class ----------------------------------------------------------------------*/ 22 | 23 | /** 24 | * @class EncoderClass 25 | * @brief Class for managing Quadrature Encoder Interface devices of the Portenta Machine Control. 26 | * 27 | * This class provides methods to interact with two quadrature encoders. Each encoder 28 | * has two channels (A and B) for quadrature signals and an index channel. The class 29 | * allows reading the current state, pulses, and revolutions of each encoder. 30 | */ 31 | class EncoderClass { 32 | public: 33 | /** 34 | * @brief Construct an EncoderClass object. 35 | * 36 | * This constructor initializes the two QEI objects for encoder 0 and encoder 1 37 | * with the specified pin assignments. 38 | * 39 | * @param enc0_A_pin Pin assignment for encoder 0 channel A (default: PJ_8). 40 | * @param enc0_B_pin Pin assignment for encoder 0 channel B (default: PH_12). 41 | * @param enc0_I_pin Pin assignment for encoder 0 Index channel (default: PH_11). 42 | * @param enc1_A_pin Pin assignment for encoder 1 channel A (default: PC_13). 43 | * @param enc1_B_pin Pin assignment for encoder 1 channel B (default: PI_7). 44 | * @param enc1_I_pin Pin assignment for encoder 1 Index channel (default: PJ_10). 45 | */ 46 | EncoderClass(PinName enc0_A_pin = MC_ENC_0A_PIN, PinName enc0_B_pin = MC_ENC_0B_PIN, PinName enc0_I_pin = MC_ENC_0I_PIN, 47 | PinName enc1_A_pin = MC_ENC_1A_PIN, PinName enc1_B_pin = MC_ENC_1B_PIN, PinName enc1_I_pin = MC_ENC_1I_PIN); 48 | 49 | /** 50 | * @brief Destruct the EncoderClass object. 51 | * 52 | * This destructor releases any resources used by the EncoderClass object. 53 | */ 54 | ~EncoderClass(); 55 | 56 | /** 57 | * @brief Reset the encoder counter for the specified channel. 58 | * 59 | * @param channel The encoder channel (0 or 1) to reset. 60 | */ 61 | void reset(int channel); 62 | 63 | /** 64 | * @brief Get the current state of the specified encoder channel. 65 | * 66 | * The current state is the value of the encoder counter. 67 | * 68 | * @param channel The encoder channel (0 or 1) to read the state from. 69 | * @return The current state of the encoder channel as a 2-bit number, where: 70 | * bit 0 = The reading from channel B 71 | * bit 1 = The reading from channel A 72 | */ 73 | int getCurrentState(int channel); 74 | 75 | /** 76 | * @brief Get the number of pulses counted by the specified encoder channel. 77 | * 78 | * This method returns the number of pulses counted by the encoder. Each pulse 79 | * corresponds to a change in the encoder's quadrature signal. 80 | * 81 | * @param channel The encoder channel (0 or 1) to read the pulses from. 82 | * @return The number of pulses counted by the encoder channel. 83 | */ 84 | int getPulses(int channel); 85 | 86 | /** 87 | * @brief Get the number of revolutions counted by the specified encoder channel. 88 | * 89 | * This method returns the number of full revolutions counted by the encoder. 90 | * It utilizes the index channel to track revolutions. 91 | * 92 | * @param channel The encoder channel (0 or 1) to read the revolutions from. 93 | * @return The number of revolutions counted by the encoder channel. 94 | */ 95 | int getRevolutions(int channel); 96 | 97 | /** 98 | * @brief Set the encoding type for the specified encoder channel. 99 | * 100 | * This method changes the encoding type from the default X2_ENCODING. 101 | * 102 | * @param channel The encoder channel (0 or 1) to be changed. 103 | * @param encoding The encoding type. 104 | */ 105 | void setEncoding(int channel, QEI::Encoding encoding); 106 | 107 | private: 108 | QEI _enc0; // QEI object for encoder 0 109 | QEI _enc1; // QEI object for encoder 1 110 | }; 111 | 112 | extern EncoderClass MachineControl_Encoders; 113 | 114 | #endif /* __ENCODER_CLASS_H */ -------------------------------------------------------------------------------- /src/AnalogInClass.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AnalogInClass.cpp 3 | * @author Leonardo Cavagnis 4 | * @brief Source file for the Analog IN connector of the Portenta Machine Control library. 5 | */ 6 | 7 | /* Includes -----------------------------------------------------------------*/ 8 | #include "AnalogInClass.h" 9 | 10 | /* Private defines -----------------------------------------------------------*/ 11 | #define CH0_IN1 MC_AI_CH0_IN1_PIN 12 | #define CH0_IN2 MC_AI_CH0_IN2_PIN 13 | #define CH0_IN3 MC_AI_CH0_IN3_PIN 14 | #define CH0_IN4 MC_AI_CH0_IN4_PIN 15 | 16 | #define CH1_IN1 MC_AI_CH1_IN1_PIN 17 | #define CH1_IN2 MC_AI_CH1_IN2_PIN 18 | #define CH1_IN3 MC_AI_CH1_IN3_PIN 19 | #define CH1_IN4 MC_AI_CH1_IN4_PIN 20 | 21 | #define CH2_IN1 MC_AI_CH2_IN1_PIN 22 | #define CH2_IN2 MC_AI_CH2_IN2_PIN 23 | #define CH2_IN3 MC_AI_CH2_IN3_PIN 24 | #define CH2_IN4 MC_AI_CH2_IN4_PIN 25 | 26 | /* Functions -----------------------------------------------------------------*/ 27 | AnalogInClass::AnalogInClass(PinName ai0_pin, PinName ai1_pin, PinName ai2_pin) 28 | : _ai0{ai0_pin}, _ai1{ai1_pin}, _ai2{ai2_pin} 29 | { 30 | // Pin configuration for CH0 31 | pinMode(CH0_IN1, OUTPUT); 32 | pinMode(CH0_IN2, OUTPUT); 33 | pinMode(CH0_IN3, OUTPUT); 34 | pinMode(CH0_IN4, OUTPUT); 35 | 36 | // Pin configuration for CH1 37 | pinMode(CH1_IN1, OUTPUT); 38 | pinMode(CH1_IN2, OUTPUT); 39 | pinMode(CH1_IN3, OUTPUT); 40 | pinMode(CH1_IN4, OUTPUT); 41 | 42 | // Pin configuration for CH2 43 | pinMode(CH2_IN1, OUTPUT); 44 | pinMode(CH2_IN2, OUTPUT); 45 | pinMode(CH2_IN3, OUTPUT); 46 | pinMode(CH2_IN4, OUTPUT); 47 | } 48 | 49 | AnalogInClass::~AnalogInClass() 50 | { } 51 | 52 | bool AnalogInClass::begin(SensorType sensor_type, int res_bits) { 53 | bool ret = true; 54 | 55 | /* Set bit resolution of ADC */ 56 | analogReadResolution(res_bits); 57 | 58 | switch (sensor_type) { 59 | case SensorType::NTC: 60 | /* Enable a 100K resistor in series with the reference voltage. 61 | * The voltage sampled is the voltage division between the 100k resistor and the input resistor (NTC/PTC) */ 62 | digitalWrite(CH0_IN1, LOW); 63 | digitalWrite(CH0_IN2, LOW); 64 | digitalWrite(CH0_IN3, HIGH); 65 | digitalWrite(CH0_IN4, HIGH); 66 | 67 | digitalWrite(CH1_IN1, LOW); 68 | digitalWrite(CH1_IN2, LOW); 69 | digitalWrite(CH1_IN3, HIGH); 70 | digitalWrite(CH1_IN4, HIGH); 71 | 72 | digitalWrite(CH2_IN1, LOW); 73 | digitalWrite(CH2_IN2, LOW); 74 | digitalWrite(CH2_IN3, HIGH); 75 | digitalWrite(CH2_IN4, HIGH); 76 | break; 77 | case SensorType::V_0_10: 78 | /* Configure the input resistor dividers to have a ratio of 0.28 79 | * (Maximum input voltage is 10V). */ 80 | digitalWrite(CH0_IN1, HIGH); 81 | digitalWrite(CH0_IN2, HIGH); 82 | digitalWrite(CH0_IN3, LOW); 83 | digitalWrite(CH0_IN4, HIGH); 84 | 85 | digitalWrite(CH1_IN1, HIGH); 86 | digitalWrite(CH1_IN2, HIGH); 87 | digitalWrite(CH1_IN3, LOW); 88 | digitalWrite(CH1_IN4, HIGH); 89 | 90 | digitalWrite(CH2_IN1, HIGH); 91 | digitalWrite(CH2_IN2, HIGH); 92 | digitalWrite(CH2_IN3, LOW); 93 | digitalWrite(CH2_IN4, HIGH); 94 | break; 95 | case SensorType::MA_4_20: 96 | /* Enable a 120 ohm resistor to GND to convert the 4-20mA sensor currents to voltage. 97 | * Note: 24V are available from the carrier to power the 4-20mA sensors. */ 98 | digitalWrite(CH0_IN1, HIGH); 99 | digitalWrite(CH0_IN2, LOW); 100 | digitalWrite(CH0_IN3, HIGH); 101 | digitalWrite(CH0_IN4, LOW); 102 | 103 | digitalWrite(CH1_IN1, HIGH); 104 | digitalWrite(CH1_IN2, LOW); 105 | digitalWrite(CH1_IN3, HIGH); 106 | digitalWrite(CH1_IN4, LOW); 107 | 108 | digitalWrite(CH2_IN1, HIGH); 109 | digitalWrite(CH2_IN2, LOW); 110 | digitalWrite(CH2_IN3, HIGH); 111 | digitalWrite(CH2_IN4, LOW); 112 | break; 113 | default: 114 | /* Unknown sensor type */ 115 | ret = false; 116 | break; 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | uint16_t AnalogInClass::read(int channel) { 123 | uint16_t value = 0; 124 | 125 | switch (channel) { 126 | case 0: 127 | value = analogRead(_ai0); 128 | break; 129 | case 1: 130 | value = analogRead(_ai1); 131 | break; 132 | case 2: 133 | value = analogRead(_ai2); 134 | break; 135 | default: 136 | break; 137 | } 138 | 139 | return value; 140 | } 141 | 142 | AnalogInClass MachineControl_AnalogIn; 143 | /**** END OF FILE ****/ -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v6 31 | 32 | - name: Download JSON schema for labels configuration file 33 | id: download-schema 34 | uses: carlosperate/download-file-action@v2 35 | with: 36 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 37 | location: ${{ runner.temp }}/label-configuration-schema 38 | 39 | - name: Install JSON schema validator 40 | run: | 41 | sudo npm install \ 42 | --global \ 43 | ajv-cli \ 44 | ajv-formats 45 | 46 | - name: Validate local labels configuration 47 | run: | 48 | # See: https://github.com/ajv-validator/ajv-cli#readme 49 | ajv validate \ 50 | --all-errors \ 51 | -c ajv-formats \ 52 | -s "${{ steps.download-schema.outputs.file-path }}" \ 53 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 54 | 55 | download: 56 | needs: check 57 | runs-on: ubuntu-latest 58 | 59 | strategy: 60 | matrix: 61 | filename: 62 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 63 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 64 | - universal.yml 65 | 66 | steps: 67 | - name: Download 68 | uses: carlosperate/download-file-action@v2 69 | with: 70 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 71 | 72 | - name: Pass configuration files to next job via workflow artifact 73 | uses: actions/upload-artifact@v5 74 | with: 75 | path: | 76 | *.yaml 77 | *.yml 78 | if-no-files-found: error 79 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 80 | 81 | sync: 82 | needs: download 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Set environment variables 87 | run: | 88 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 89 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 90 | 91 | - name: Determine whether to dry run 92 | id: dry-run 93 | if: > 94 | github.event_name == 'pull_request' || 95 | ( 96 | ( 97 | github.event_name == 'push' || 98 | github.event_name == 'workflow_dispatch' 99 | ) && 100 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 101 | ) 102 | run: | 103 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 104 | # configuration. 105 | echo "::set-output name=flag::--dry-run" 106 | 107 | - name: Checkout repository 108 | uses: actions/checkout@v6 109 | 110 | - name: Download configuration files artifact 111 | uses: actions/download-artifact@v6 112 | with: 113 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 114 | path: ${{ env.CONFIGURATIONS_FOLDER }} 115 | 116 | - name: Remove unneeded artifact 117 | uses: geekyeggo/delete-artifact@v5 118 | with: 119 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 120 | 121 | - name: Merge label configuration files 122 | run: | 123 | # Merge all configuration files 124 | shopt -s extglob 125 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 126 | 127 | - name: Install github-label-sync 128 | run: sudo npm install --global github-label-sync 129 | 130 | - name: Sync labels 131 | env: 132 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 133 | run: | 134 | # See: https://github.com/Financial-Times/github-label-sync 135 | github-label-sync \ 136 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 137 | ${{ steps.dry-run.outputs.flag }} \ 138 | ${{ github.repository }} 139 | -------------------------------------------------------------------------------- /src/DigitalOutputsClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DigitalOutputsClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the Digital Output connector of the Portenta Machine Control library. 5 | * 6 | * This library allows to interface with the IO Expander and set the digital outputs. 7 | */ 8 | 9 | #ifndef __DIGITALOUTPUTS_CLASS_H 10 | #define __DIGITALOUTPUTS_CLASS_H 11 | 12 | /* Includes -------------------------------------------------------------------*/ 13 | #include 14 | #include 15 | #include "pins_mc.h" 16 | 17 | /* Class ----------------------------------------------------------------------*/ 18 | 19 | /** 20 | * @class DigitalOutputsClass 21 | * @brief Class for the Digital Output connector of the Portenta Machine Control. 22 | */ 23 | class DigitalOutputsClass { 24 | public: 25 | /** 26 | * @brief Construct a DigitalOutputsClass object. 27 | * 28 | * This constructor initializes a DigitalOutputsClass object with the specified pin assignments for digital outputs. 29 | * 30 | * @param do0_pin The pin number for the digital output channel 0. 31 | * @param do1_pin The pin number for the digital output channel 1. 32 | * @param do2_pin The pin number for the digital output channel 2. 33 | * @param do3_pin The pin number for the digital output channel 3. 34 | * @param do4_pin The pin number for the digital output channel 4. 35 | * @param do5_pin The pin number for the digital output channel 5. 36 | * @param do6_pin The pin number for the digital output channel 6. 37 | * @param do7_pin The pin number for the digital output channel 7. 38 | * @param latch_pin The pin number for the latch mode control. 39 | */ 40 | DigitalOutputsClass(PinName do0_pin = MC_DO_DO0_PIN, 41 | PinName do1_pin = MC_DO_DO1_PIN, 42 | PinName do2_pin = MC_DO_DO2_PIN, 43 | PinName do3_pin = MC_DO_DO3_PIN, 44 | PinName do4_pin = MC_DO_DO4_PIN, 45 | PinName do5_pin = MC_DO_DO5_PIN, 46 | PinName do6_pin = MC_DO_DO6_PIN, 47 | PinName do7_pin = MC_DO_DO7_PIN, 48 | PinName latch_pin = MC_DO_LATCH_PIN); 49 | 50 | /** 51 | * @brief Destruct the DigitalOutputsClass object. 52 | * 53 | * This destructor releases any resources used by the DigitalOutputsClass object. 54 | */ 55 | ~DigitalOutputsClass(); 56 | 57 | /** 58 | * @brief Initialize the DigitalOutputs module with the specified latch mode. 59 | * 60 | * @param latch_mode The latch mode for thermal shutdown. If true, thermal shutdown operates in the latch mode. Otherwise, it operates in the auto-retry mode. 61 | * @return true If the DigitalOutputs module is successfully initialized, false Otherwise 62 | */ 63 | bool begin(bool latch_mode = true); 64 | 65 | /** 66 | * @brief Write the output value for the given channel. 67 | * 68 | * @param channel The channel number to write to. 69 | * @param val The value to write. It can be either PinStatus::HIGH or PinStatus::LOW. 70 | */ 71 | void write(uint8_t channel, PinStatus val); 72 | 73 | /** 74 | * @brief Set the state of all digital outputs simultaneously. 75 | * 76 | * @param val_mask An 8-bit integer representing the state of all 8 channels. Each bit corresponds to a channel, where 1 represents HIGH and 0 represents LOW. 77 | * For example: 78 | * - To set all channels to HIGH: val_mask = 255 (0b11111111) 79 | * - To set all channels to LOW: val_mask = 0 (0b00000000) 80 | */ 81 | void writeAll(uint8_t val_mask); 82 | 83 | private: 84 | PinName _do0; // Digital output pin for DO (Digital Out) channel 0 85 | PinName _do1; // Digital output pin for DO (Digital Out) channel 1 86 | PinName _do2; // Digital output pin for DO (Digital Out) channel 2 87 | PinName _do3; // Digital output pin for DO (Digital Out) channel 3 88 | PinName _do4; // Digital output pin for DO (Digital Out) channel 4 89 | PinName _do5; // Digital output pin for DO (Digital Out) channel 5 90 | PinName _do6; // Digital output pin for DO (Digital Out) channel 6 91 | PinName _do7; // Digital output pin for DO (Digital Out) channel 7 92 | PinName _latch; // Latch control pin 93 | 94 | /** 95 | * @brief Configures the thermal shutdown of the high-side switches (TPS4H160) to operate in latch mode. 96 | * The output latches off when thermal shutdown occurs. 97 | */ 98 | void _setLatchMode(); 99 | 100 | /** 101 | * @brief Configures the thermal shutdown of the high-side switches (TPS4H160) to operate in auto-retry mode. 102 | * The output automatically recovers when TJ < T(SD) – T(hys), but the current is limited to ICL(TSD) 103 | * to avoid repetitive thermal shutdown. 104 | */ 105 | void _setAutoRetryMode(); 106 | }; 107 | 108 | extern DigitalOutputsClass MachineControl_DigitalOutputs; 109 | 110 | #endif /* __DIGITALOUTPUTS_CLASS_H */ -------------------------------------------------------------------------------- /src/utility/MAX31865/MAX31865.cpp: -------------------------------------------------------------------------------- 1 | #include "MAX31865.h" 2 | 3 | 4 | MAX31865Class::MAX31865Class(PinName cs) : _spi(SPI), _cs(cs) { 5 | } 6 | 7 | static SPISettings _spiSettings(1000000, MSBFIRST, SPI_MODE1); 8 | 9 | bool MAX31865Class::begin(int wires) { 10 | _spi.begin(); 11 | 12 | pinMode(_cs, OUTPUT); 13 | digitalWrite(_cs, HIGH); 14 | // sets 2 or 4 wire 15 | if (wires == THREE_WIRE) { 16 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) | MAX31856_CONFIG_3_WIRE)); 17 | } else { 18 | 19 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_WIRE_MASK)); 20 | } 21 | 22 | // disable bias 23 | writeByte(MAX31856_CONFIG_REG, readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_BIAS_MASK); 24 | 25 | // disable auto convert mode 26 | writeByte(MAX31856_CONFIG_REG, readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_CONV_MODE_MASK); 27 | 28 | // clear fault 29 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_CLEAR_FAULT_CYCLE)| MAX31856_CONFIG_CLEAR_FAULT); 30 | 31 | // set filter frequency 32 | writeByte(MAX31856_CONFIG_REG, readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_60_50_HZ_FILTER_MASK); 33 | 34 | return true; 35 | } 36 | 37 | void MAX31865Class::clearFault(void) { 38 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_CLEAR_FAULT_CYCLE) | MAX31856_CONFIG_CLEAR_FAULT); 39 | } 40 | 41 | uint8_t MAX31865Class::readFault(void) { 42 | return readByte(MAX31856_FAULT_STATUS_REG); 43 | } 44 | 45 | bool MAX31865Class::getHighThresholdFault(uint8_t fault) { 46 | if (fault & MAX31865_FAULT_HIGH_THRESH) { 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | bool MAX31865Class::getLowThresholdFault(uint8_t fault) { 53 | if (fault & MAX31865_FAULT_LOW_THRESH) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | bool MAX31865Class::getLowREFINFault(uint8_t fault) { 60 | 61 | if (fault & MAX31865_FAULT_LOW_REFIN) { 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | bool MAX31865Class::getHighREFINFault(uint8_t fault) { 68 | 69 | if (fault & MAX31865_FAULT_HIGH_REFIN) { 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | bool MAX31865Class::getLowRTDINFault(uint8_t fault) { 76 | 77 | if (fault & MAX31865_FAULT_LOW_RTDIN) { 78 | return true; 79 | } 80 | return false; 81 | } 82 | 83 | bool MAX31865Class::getVoltageFault(uint8_t fault) { 84 | if (fault & MAX31865_FAULT_OVER_UNDER_VOLTAGE) { 85 | return true; 86 | } 87 | return false; 88 | } 89 | 90 | float MAX31865Class::readTemperature(float RTDnominal, float refResistor) { 91 | float Z1, Z2, Z3, Z4, Rt, temp; 92 | 93 | Rt = readRTD(); 94 | Rt /= 32768; 95 | Rt *= refResistor; 96 | 97 | 98 | Z1 = -RTD_A; 99 | Z2 = RTD_A * RTD_A - (4 * RTD_B); 100 | Z3 = (4 * RTD_B) / RTDnominal; 101 | Z4 = 2 * RTD_B; 102 | 103 | temp = Z2 + (Z3 * Rt); 104 | temp = (sqrt(temp) + Z1) / Z4; 105 | 106 | if (temp >= 0) 107 | return temp; 108 | 109 | // ugh. 110 | Rt /= RTDnominal; 111 | Rt *= 100; // normalize to 100 ohm 112 | 113 | float rpoly = Rt; 114 | 115 | temp = -242.02; 116 | temp += 2.2228 * rpoly; 117 | rpoly *= Rt; // square 118 | temp += 2.5859e-3 * rpoly; 119 | rpoly *= Rt; // ^3 120 | temp -= 4.8260e-6 * rpoly; 121 | rpoly *= Rt; // ^4 122 | temp -= 2.8183e-8 * rpoly; 123 | rpoly *= Rt; // ^5 124 | temp += 1.5243e-10 * rpoly; 125 | 126 | return temp; 127 | } 128 | 129 | uint32_t MAX31865Class::readRTD() { 130 | 131 | // clear fault 132 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) & MAX31856_CONFIG_CLEAR_FAULT_CYCLE) | MAX31856_CONFIG_CLEAR_FAULT); 133 | 134 | // enable bias 135 | writeByte(MAX31856_CONFIG_REG, (readByte(MAX31856_CONFIG_REG) | MAX31856_CONFIG_BIAS_ON)); 136 | delay(10); 137 | 138 | // ONE shot cOnfIg and make readings change with readByte 139 | writeByte(MAX31856_CONFIG_REG, readByte(MAX31856_CONFIG_REG) | MAX31856_CONFIG_ONE_SHOT); 140 | delay(65); 141 | 142 | //readings bytes 143 | uint16_t read = (readBytes(MAX31856_RTD_MSB_REG)); 144 | read = read >>1; 145 | // disable bias 146 | writeByte(MAX31856_CONFIG_REG, readByte(MAX31856_CONFIG_REG) & (MAX31856_CONFIG_BIAS_MASK)); 147 | 148 | return read; 149 | } 150 | 151 | uint8_t MAX31865Class::readByte(uint8_t addr) { 152 | addr &= 0x7F; 153 | uint8_t read = 0; 154 | digitalWrite(_cs, LOW); 155 | 156 | _spi.beginTransaction(_spiSettings); 157 | _spi.transfer(addr); 158 | _spi.transfer(&read,1); 159 | _spi.endTransaction(); 160 | 161 | digitalWrite(_cs, HIGH); 162 | 163 | return read; 164 | } 165 | 166 | uint16_t MAX31865Class::readBytes(uint8_t addr) { 167 | digitalWrite(_cs, LOW); 168 | uint16_t read = 0x00; 169 | _spi.beginTransaction(_spiSettings); 170 | _spi.transfer(addr); 171 | int i; 172 | for (i = 0; i<2; i++) { 173 | read = read << 8; 174 | read |= _spi.transfer(0); 175 | } 176 | 177 | _spi.endTransaction(); 178 | 179 | digitalWrite(_cs, HIGH); 180 | 181 | return read; 182 | } 183 | 184 | void MAX31865Class::writeByte(uint8_t addr, uint8_t data) { 185 | addr |= 0x80; // make sure top bit is set 186 | uint8_t buffer[2] = {addr, data}; 187 | digitalWrite(_cs, LOW); 188 | 189 | _spi.beginTransaction(_spiSettings); 190 | _spi.transfer(buffer,2); 191 | 192 | _spi.endTransaction(); 193 | 194 | digitalWrite(_cs, HIGH); 195 | } 196 | -------------------------------------------------------------------------------- /examples/USB_host/TUSB_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | _______ _ _ _____ ____ 3 | |__ __| | | | |/ ____| _ \ 4 | | | ___ ___ _ __ _ _| | | | (___ | |_) | 5 | | |/ _ \/ _ \ '_ \| | | | | | |\___ \| _ < 6 | | | __/ __/ | | | |_| | |__| |____) | |_) | 7 | |_|\___|\___|_| |_|\__, |\____/|_____/|____/ 8 | __/ | 9 | |___/ 10 | 11 | TeenyUSB - light weight usb stack for STM32 micro controllers 12 | 13 | Copyright (c) 2019 XToolBox - admin@xtoolbox.org 14 | www.tusb.org 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | */ 34 | 35 | #pragma once 36 | 37 | #include 38 | 39 | static const tusbh_boot_key_class_t cls_boot_key = { 40 | .backend = &tusbh_boot_keyboard_backend, 41 | //.on_key = process_key 42 | }; 43 | 44 | static const tusbh_boot_mouse_class_t cls_boot_mouse = { 45 | .backend = &tusbh_boot_mouse_backend, 46 | // .on_mouse = process_mouse 47 | }; 48 | 49 | static const tusbh_hid_class_t cls_hid = { 50 | .backend = &tusbh_hid_backend, 51 | //.on_recv_data = process_hid_recv, 52 | //.on_send_done = process_hid_sent, 53 | }; 54 | 55 | static const tusbh_hub_class_t cls_hub = { 56 | .backend = &tusbh_hub_backend, 57 | }; 58 | 59 | static const tusbh_vendor_class_t cls_vendor = { 60 | .backend = &tusbh_vendor_backend, 61 | //.transfer_done = process_vendor_xfer_done 62 | }; 63 | 64 | int msc_ff_mount(tusbh_interface_t* interface, int max_lun, const tusbh_block_info_t* blocks); 65 | int msc_ff_unmount(tusbh_interface_t* interface); 66 | 67 | static const tusbh_msc_class_t cls_msc_bot = { 68 | .backend = &tusbh_msc_bot_backend, 69 | // .mount = msc_ff_mount, 70 | // .unmount = msc_ff_unmount, 71 | }; 72 | 73 | static const tusbh_cdc_acm_class_t cls_cdc_acm = { 74 | .backend = &tusbh_cdc_acm_backend, 75 | }; 76 | 77 | static const tusbh_cdc_rndis_class_t cls_cdc_rndis = { 78 | .backend = &tusbh_cdc_rndis_backend, 79 | }; 80 | 81 | static const tusbh_class_reg_t class_table[] = { 82 | (tusbh_class_reg_t)&cls_boot_key, 83 | (tusbh_class_reg_t)&cls_boot_mouse, 84 | (tusbh_class_reg_t)&cls_hub, 85 | (tusbh_class_reg_t)&cls_msc_bot, 86 | (tusbh_class_reg_t)&cls_cdc_acm, 87 | (tusbh_class_reg_t)&cls_cdc_rndis, 88 | (tusbh_class_reg_t)&cls_hid, 89 | (tusbh_class_reg_t)&cls_vendor, 90 | 0, 91 | }; 92 | 93 | #define MOD_CTRL (0x01 | 0x10) 94 | #define MOD_SHIFT (0x02 | 0x20) 95 | #define MOD_ALT (0x04 | 0x40) 96 | #define MOD_WIN (0x08 | 0x80) 97 | 98 | #define LED_NUM_LOCK 1 99 | #define LED_CAPS_LOCK 2 100 | #define LED_SCROLL_LOCK 4 101 | 102 | #define stdin_recvchar Serial1.write 103 | 104 | static uint8_t key_leds; 105 | static const char knum[] = "1234567890"; 106 | static const char ksign[] = "!@#$%^&*()"; 107 | static const char tabA[] = "\t -=[]\\#;'`,./"; 108 | static const char tabB[] = "\t _+{}|~:\"~<>?"; 109 | 110 | // route the key event to stdin 111 | static int process_key(tusbh_ep_info_t* ep, const uint8_t* keys) 112 | { 113 | Serial.println(); 114 | 115 | uint8_t modify = keys[0]; 116 | uint8_t key = keys[2]; 117 | uint8_t last_leds = key_leds; 118 | if (key >= KEY_A && key <= KEY_Z) { 119 | char ch = 'A' + key - KEY_A; 120 | if ( (!!(modify & MOD_SHIFT)) == (!!(key_leds & LED_CAPS_LOCK)) ) { 121 | ch += 'a' - 'A'; 122 | } 123 | stdin_recvchar(ch); 124 | Serial.print(ch); 125 | } else if (key >= KEY_1 && key <= KEY_0) { 126 | if (modify & MOD_SHIFT) { 127 | stdin_recvchar(ksign[key - KEY_1]); 128 | } else { 129 | stdin_recvchar(knum[key - KEY_1]); 130 | } 131 | } else if (key >= KEY_TAB && key <= KEY_SLASH) { 132 | if (modify & MOD_SHIFT) { 133 | stdin_recvchar(tabB[key - KEY_TAB]); 134 | } else { 135 | stdin_recvchar(tabA[key - KEY_TAB]); 136 | } 137 | } else if (key == KEY_ENTER) { 138 | stdin_recvchar('\r'); 139 | } else if (key == KEY_CAPSLOCK) { 140 | key_leds ^= LED_CAPS_LOCK; 141 | } else if (key == KEY_NUMLOCK) { 142 | key_leds ^= LED_NUM_LOCK; 143 | } else if (key == KEY_SCROLLLOCK) { 144 | key_leds ^= LED_SCROLL_LOCK; 145 | } 146 | 147 | if (key_leds != last_leds) { 148 | tusbh_set_keyboard_led(ep, key_leds); 149 | } 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /src/RS485CommClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RS485CommClass.h 3 | * @author Leonardo Cavagnis 4 | * @brief Header file for the RS485CommClass used to initialize and interact with RS485 and RS232 communication protocols on the Portenta Machine Control board. 5 | * 6 | * This library provides a class to manage the RS485 and RS232 communication protocols of the Portenta Machine Control board. 7 | * It allows initializing and interacting with the serial protocols. The library also initializes the corresponding LEDs. 8 | */ 9 | 10 | #ifndef __RS485_COMM_CLASS_H 11 | #define __RS485_COMM_CLASS_H 12 | 13 | /* Includes -------------------------------------------------------------------*/ 14 | #include 15 | #include 16 | #include 17 | #include "pins_mc.h" 18 | 19 | /* Class ----------------------------------------------------------------------*/ 20 | 21 | /** 22 | * @class RS485CommClass 23 | * @brief Class for managing the RS485 and RS232 communication protocols of the Portenta Machine Control. 24 | * 25 | * The `RS485CommClass` is a subclass of `RS485Class` and provides methods to work with the RS485 and RS232 communication protocols on the Portenta Machine Control board. 26 | * It includes features to initialize, configure, and interact with the serial protocols. The library also initializes the corresponding LED for RS485. 27 | */ 28 | class RS485CommClass : public RS485Class { 29 | public: 30 | /** 31 | * @brief Construct a RS485CommClass object. 32 | * 33 | * This constructor initializes a RS485CommClass object with the specified UART interface and pins. 34 | * 35 | * @param uart_itf The UART interface to use for communication. 36 | * @param rs_tx_pin The pin for transmitting data on the RS485 Bus. 37 | * @param rs_de_pin The pin for enabling the RS485 driver. 38 | * @param rs_re_pin The pin for setting the RS485 driver in receive or transmit mode. 39 | */ 40 | RS485CommClass(arduino::UART& uart_itf, PinName rs_tx_pin = MC_RS485_TX_PIN, PinName rs_de_pin = MC_RS485_DE_PIN, PinName rs_re_pin = MC_RS485_RE_PIN); 41 | 42 | 43 | /** 44 | * @brief Destruct the RS485CommClass object. 45 | * 46 | * This destructor releases any resources used by the RS485CommClass object. 47 | * It will automatically be called when the object goes out of scope. 48 | */ 49 | ~RS485CommClass(); 50 | 51 | /** 52 | * @brief Begin the RS485 communication protocol. 53 | * 54 | * This method initializes the RS485 communication protocol with the specified baud rate and pre/post delays. 55 | * 56 | * @param baudrate The desired baud rate for the RS485 communication. 57 | * @param config The desired Serial config (bits, parity and stopbits), see HardwareSerial.h 58 | * @param predelay The delay before sending data in the RS485 communication (default: RS485_DEFAULT_PRE_DELAY). 59 | * @param postdelay The delay after sending data in the RS485 communication (default: RS485_DEFAULT_POST_DELAY). 60 | */ 61 | void begin(unsigned long baudrate = 115200, uint16_t config = SERIAL_8N1, int predelay = RS485_DEFAULT_PRE_DELAY, int postdelay = RS485_DEFAULT_POST_DELAY); 62 | 63 | /** 64 | * @brief Close the RS485 communication protocol. 65 | * 66 | * This method de-initializes the RS485 communication protocol, stopping communication on the RS485 Bus. 67 | */ 68 | void end(); 69 | 70 | /** 71 | * @brief Set RS485 mode to RS232. 72 | * 73 | * This method sets the RS485 mode to RS232 or RS485. 74 | * 75 | * @param enable If true, sets the RS485 mode to RS232, else sets to RS485 mode. 76 | */ 77 | void setModeRS232(bool enable); 78 | 79 | /** 80 | * @brief Set YZ termination for RS485 communication. 81 | * 82 | * This method enables or disables YZ termination for RS485 communication. 83 | * 84 | * @param enable If true, enables YZ termination, else disables it. 85 | */ 86 | void setYZTerm(bool enable); 87 | 88 | /** 89 | * @brief Set AB termination for RS485 communication. 90 | * 91 | * This method enables or disables AB termination for RS485 communication. 92 | * 93 | * @param enable If true, enables AB termination, else disables it. 94 | */ 95 | void setABTerm(bool enable); 96 | 97 | /** 98 | * @brief Set the slew rate for RS485 communication. 99 | * 100 | * This method enables or disables the slew rate control for RS485 communication. 101 | * 102 | * @param enable If true, enables the slew rate control, else disables it. 103 | */ 104 | void setSlew(bool enable); 105 | 106 | /** 107 | * @brief Set RS485 communication to Full Duplex mode. 108 | * 109 | * This method sets RS485 communication to Full Duplex or Half Duplex mode. 110 | * 111 | * @param enable If true, sets RS485 communication to Full Duplex mode, else to Half Duplex mode. 112 | */ 113 | void setFullDuplex(bool enable); 114 | 115 | private: 116 | /** 117 | * @brief Enable RS485 communication. 118 | * 119 | * This method enables RS485 communication. 120 | */ 121 | void _enable(); 122 | 123 | /** 124 | * @brief Disable RS485 communication. 125 | * 126 | * This method disables RS485 communication. 127 | */ 128 | void _disable(); 129 | }; 130 | 131 | extern RS485CommClass MachineControl_RS485Comm; 132 | 133 | #endif /* __RS485_COMM_CLASS_H */ 134 | -------------------------------------------------------------------------------- /examples/Temp_probes_RTD/Temp_probes_RTD.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Portenta Machine Control - Temperature Probes RTD Example 3 | * 4 | * This example provides a method to test the 3-wire RTDs 5 | * on the Machine Control Carrier. It is also possible to 6 | * acquire 2-wire RTDs by shorting the RTDx pin to the TPx pin. 7 | * The Machine Control Carrier features a precise 400 ohm 0.1% reference resistor, 8 | * which serves as a reference for the MAX31865. 9 | * 10 | * Circuit: 11 | * - Portenta H7 12 | * - Portenta Machine Control 13 | * - 3-wire RTD or 2-wire RTD 14 | * 15 | * This example code is in the public domain. 16 | * Copyright (c) 2024 Arduino 17 | * SPDX-License-Identifier: MPL-2.0 18 | */ 19 | 20 | #include 21 | 22 | // The value of the Rref resistor. Use 430.0 for PT100 23 | #define RREF 400.0 24 | // The 'nominal' 0-degrees-C resistance of the sensor 25 | // 100.0 for PT100 26 | #define RNOMINAL 100.0 27 | 28 | void setup() { 29 | Serial.begin(9600); 30 | while (!Serial) { 31 | ; 32 | } 33 | 34 | MachineControl_RTDTempProbe.begin(THREE_WIRE); 35 | } 36 | 37 | void loop() { 38 | MachineControl_RTDTempProbe.selectChannel(0); 39 | Serial.println("CHANNEL 0 SELECTED"); 40 | uint16_t rtd = MachineControl_RTDTempProbe.readRTD(); 41 | float ratio = rtd; 42 | ratio /= 32768; 43 | 44 | // Check and print any faults 45 | uint8_t fault = MachineControl_RTDTempProbe.readFault(); 46 | if (fault) { 47 | Serial.print("Fault 0x"); Serial.println(fault, HEX); 48 | if (MachineControl_RTDTempProbe.getHighThresholdFault(fault)) { 49 | Serial.println("RTD High Threshold"); 50 | } 51 | if (MachineControl_RTDTempProbe.getLowThresholdFault(fault)) { 52 | Serial.println("RTD Low Threshold"); 53 | } 54 | if (MachineControl_RTDTempProbe.getLowREFINFault(fault)) { 55 | Serial.println("REFIN- > 0.85 x Bias"); 56 | } 57 | if (MachineControl_RTDTempProbe.getHighREFINFault(fault)) { 58 | Serial.println("REFIN- < 0.85 x Bias - FORCE- open"); 59 | } 60 | if (MachineControl_RTDTempProbe.getLowRTDINFault(fault)) { 61 | Serial.println("RTDIN- < 0.85 x Bias - FORCE- open"); 62 | } 63 | if (MachineControl_RTDTempProbe.getVoltageFault(fault)) { 64 | Serial.println("Under/Over voltage"); 65 | } 66 | MachineControl_RTDTempProbe.clearFault(); 67 | } else { 68 | Serial.print("RTD value: "); Serial.println(rtd); 69 | Serial.print("Ratio = "); Serial.println(ratio, 8); 70 | Serial.print("Resistance = "); Serial.println(RREF * ratio, 8); 71 | Serial.print("Temperature = "); Serial.println(MachineControl_RTDTempProbe.readTemperature(RNOMINAL, RREF)); 72 | } 73 | Serial.println(); 74 | delay(100); 75 | 76 | MachineControl_RTDTempProbe.selectChannel(1); 77 | Serial.println("CHANNEL 1 SELECTED"); 78 | rtd = MachineControl_RTDTempProbe.readRTD(); 79 | ratio = rtd; 80 | ratio /= 32768; 81 | 82 | // Check and print any faults 83 | fault = MachineControl_RTDTempProbe.readFault(); 84 | if (fault) { 85 | Serial.print("Fault 0x"); Serial.println(fault, HEX); 86 | if (MachineControl_RTDTempProbe.getHighThresholdFault(fault)) { 87 | Serial.println("RTD High Threshold"); 88 | } 89 | if (MachineControl_RTDTempProbe.getLowThresholdFault(fault)) { 90 | Serial.println("RTD Low Threshold"); 91 | } 92 | if (MachineControl_RTDTempProbe.getLowREFINFault(fault)) { 93 | Serial.println("REFIN- > 0.85 x Bias"); 94 | } 95 | if (MachineControl_RTDTempProbe.getHighREFINFault(fault)) { 96 | Serial.println("REFIN- < 0.85 x Bias - FORCE- open"); 97 | } 98 | if (MachineControl_RTDTempProbe.getLowRTDINFault(fault)) { 99 | Serial.println("RTDIN- < 0.85 x Bias - FORCE- open"); 100 | } 101 | if (MachineControl_RTDTempProbe.getVoltageFault(fault)) { 102 | Serial.println("Under/Over voltage"); 103 | } 104 | MachineControl_RTDTempProbe.clearFault(); 105 | } else { 106 | Serial.print("RTD value: "); Serial.println(rtd); 107 | Serial.print("Ratio = "); Serial.println(ratio, 8); 108 | Serial.print("Resistance = "); Serial.println(RREF * ratio, 8); 109 | Serial.print("Temperature = "); Serial.println(MachineControl_RTDTempProbe.readTemperature(RNOMINAL, RREF)); 110 | } 111 | Serial.println(); 112 | delay(100); 113 | 114 | MachineControl_RTDTempProbe.selectChannel(2); 115 | Serial.println("CHANNEL 2 SELECTED"); 116 | rtd = MachineControl_RTDTempProbe.readRTD(); 117 | ratio = rtd; 118 | ratio /= 32768; 119 | 120 | // Check and print any faults 121 | fault = MachineControl_RTDTempProbe.readFault(); 122 | if (fault) { 123 | Serial.print("Fault 0x"); Serial.println(fault, HEX); 124 | if (MachineControl_RTDTempProbe.getHighThresholdFault(fault)) { 125 | Serial.println("RTD High Threshold"); 126 | } 127 | if (MachineControl_RTDTempProbe.getLowThresholdFault(fault)) { 128 | Serial.println("RTD Low Threshold"); 129 | } 130 | if (MachineControl_RTDTempProbe.getLowREFINFault(fault)) { 131 | Serial.println("REFIN- > 0.85 x Bias"); 132 | } 133 | if (MachineControl_RTDTempProbe.getHighREFINFault(fault)) { 134 | Serial.println("REFIN- < 0.85 x Bias - FORCE- open"); 135 | } 136 | if (MachineControl_RTDTempProbe.getLowRTDINFault(fault)) { 137 | Serial.println("RTDIN- < 0.85 x Bias - FORCE- open"); 138 | } 139 | if (MachineControl_RTDTempProbe.getVoltageFault(fault)) { 140 | Serial.println("Under/Over voltage"); 141 | } 142 | MachineControl_RTDTempProbe.clearFault(); 143 | } else { 144 | Serial.print("RTD value: "); Serial.println(rtd); 145 | Serial.print("Ratio = "); Serial.println(ratio, 8); 146 | Serial.print("Resistance = "); Serial.println(RREF * ratio, 8); 147 | Serial.print("Temperature = "); Serial.println(MachineControl_RTDTempProbe.readTemperature(RNOMINAL, RREF)); 148 | } 149 | Serial.println(); 150 | delay(1000); 151 | } 152 | -------------------------------------------------------------------------------- /src/utility/ioexpander/TCA6424A.h: -------------------------------------------------------------------------------- 1 | // I2Cdev library collection - TCA6424A I2C device class header file 2 | // Based on Texas Instruments TCA6424A datasheet, 9/2010 (document SCPS193B) 3 | // 7/31/2011 by Jeff Rowberg 4 | // Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib 5 | // 6 | // Changelog: 7 | // 2011-07-31 - initial release 8 | 9 | /* ============================================ 10 | I2Cdev device library code is placed under the MIT license 11 | Copyright (c) 2011 Jeff Rowberg 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | =============================================== 31 | */ 32 | 33 | #ifndef _TCA6424A_H_ 34 | #define _TCA6424A_H_ 35 | 36 | #include "I2Cdev.h" 37 | 38 | #define TCA6424A_ADDRESS_ADDR_LOW 0x22 // address pin low (GND) 39 | #define TCA6424A_ADDRESS_ADDR_HIGH 0x23 // address pin high (VCC) 40 | #define TCA6424A_DEFAULT_ADDRESS TCA6424A_ADDRESS_ADDR_LOW 41 | 42 | #define TCA6424A_RA_INPUT0 0x00 43 | #define TCA6424A_RA_INPUT1 0x01 44 | #define TCA6424A_RA_INPUT2 0x02 45 | #define TCA6424A_RA_OUTPUT0 0x04 46 | #define TCA6424A_RA_OUTPUT1 0x05 47 | #define TCA6424A_RA_OUTPUT2 0x06 48 | #define TCA6424A_RA_POLARITY0 0x08 49 | #define TCA6424A_RA_POLARITY1 0x09 50 | #define TCA6424A_RA_POLARITY2 0x0A 51 | #define TCA6424A_RA_CONFIG0 0x0C 52 | #define TCA6424A_RA_CONFIG1 0x0D 53 | #define TCA6424A_RA_CONFIG2 0x0E 54 | 55 | #define TCA6424A_AUTO_INCREMENT 0x80 56 | 57 | #define TCA6424A_LOW 0 58 | #define TCA6424A_HIGH 1 59 | 60 | #define TCA6424A_POLARITY_NORMAL 0 61 | #define TCA6424A_POLARITY_INVERTED 1 62 | 63 | #define TCA6424A_OUTPUT 0 64 | #define TCA6424A_INPUT 1 65 | 66 | #define TCA6424A_P00 0 67 | #define TCA6424A_P01 1 68 | #define TCA6424A_P02 2 69 | #define TCA6424A_P03 3 70 | #define TCA6424A_P04 4 71 | #define TCA6424A_P05 5 72 | #define TCA6424A_P06 6 73 | #define TCA6424A_P07 7 74 | #define TCA6424A_P10 8 75 | #define TCA6424A_P11 9 76 | #define TCA6424A_P12 10 77 | #define TCA6424A_P13 11 78 | #define TCA6424A_P14 12 79 | #define TCA6424A_P15 13 80 | #define TCA6424A_P16 14 81 | #define TCA6424A_P17 15 82 | #define TCA6424A_P20 16 83 | #define TCA6424A_P21 17 84 | #define TCA6424A_P22 18 85 | #define TCA6424A_P23 19 86 | #define TCA6424A_P24 20 87 | #define TCA6424A_P25 21 88 | #define TCA6424A_P26 22 89 | #define TCA6424A_P27 23 90 | 91 | class TCA6424A { 92 | public: 93 | TCA6424A(); 94 | TCA6424A(uint8_t address); 95 | 96 | void initialize(); 97 | bool testConnection(); 98 | 99 | void setAddress(uint8_t address); 100 | uint8_t getAddress(); 101 | 102 | // INPUT* registers (x0h - x2h) 103 | bool readPin(uint16_t pin); 104 | uint8_t readBank(uint8_t bank); 105 | void readAll(uint8_t *banks); 106 | void readAll(uint8_t *bank0, uint8_t *bank1, uint8_t *bank2); 107 | 108 | // OUTPUT* registers (x4h - x6h) 109 | bool getPinOutputLevel(uint16_t pin); 110 | uint8_t getBankOutputLevel(uint8_t bank); 111 | void getAllOutputLevel(uint8_t *banks); 112 | void getAllOutputLevel(uint8_t *bank0, uint8_t *bank1, uint8_t *bank2); 113 | void writePin(uint16_t pin, bool polarity); 114 | void writeBank(uint8_t bank, uint8_t value); 115 | void writeAll(uint8_t *banks); 116 | void writeAll(uint8_t bank0, uint8_t bank1, uint8_t bank2); 117 | 118 | // POLARITY* registers (x8h - xAh) 119 | bool getPinPolarity(uint16_t pin); 120 | uint8_t getBankPolarity(uint8_t bank); 121 | void getAllPolarity(uint8_t *banks); 122 | void getAllPolarity(uint8_t *bank0, uint8_t *bank1, uint8_t *bank2); 123 | void setPinPolarity(uint16_t pin, bool polarity); 124 | void setBankPolarity(uint8_t bank, uint8_t polarity); 125 | void setAllPolarity(uint8_t *banks); 126 | void setAllPolarity(uint8_t bank0, uint8_t bank1, uint8_t bank2); 127 | 128 | // CONFIG* registers (xCh - xEh) 129 | bool getPinDirection(uint16_t pin); 130 | uint8_t getBankDirection(uint8_t bank); 131 | void getAllDirection(uint8_t *banks); 132 | void getAllDirection(uint8_t *bank0, uint8_t *bank1, uint8_t *bank2); 133 | void setPinDirection(uint16_t pin, bool direction); 134 | void setBankDirection(uint8_t bank, uint8_t direction); 135 | void setAllDirection(uint8_t *banks); 136 | void setAllDirection(uint8_t bank0, uint8_t bank1, uint8_t bank2); 137 | 138 | private: 139 | uint8_t devAddr; 140 | uint8_t buffer[3]; 141 | }; 142 | 143 | #endif /* _TCA6424A_H_ */ 144 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Portenta Machine Control Library 2 | 3 | [![Check Arduino status](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/check-arduino.yml) 4 | [![Compile Examples status](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/compile-examples.yml) 5 | [![Spell Check status](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/actions/workflows/spell-check.yml) 6 | 7 | [![License](https://img.shields.io/badge/License-MPLv2.0-blue.svg)](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/blob/main/LICENSE) 8 | 9 | The Portenta Machine Control Library is a C++ library designed to efficiently manage the functionalities of the Portenta Machine Control board. It provides extensive support for inputs such as digital, analog, and encoder signals, while offering outputs including digital and analog signals. This library also menages communication through protocols like CAN-BUS and serial ports, and allows connectivity via Ethernet, USB, Wi-Fi, and Bluetooth Low Energy. 10 | 11 | The library empowers users to easily initialize, control, and access the diverse functionalities of the Portenta Machine Control, enhancing its capability and adaptability for industrial applications. 12 | 13 | 📚 For more information about this library please visit us at: 14 | https://www.arduino.cc/reference/en/libraries/arduino_portentamachinecontrol/ 15 | 16 | ## Features 17 | 18 | - Manages input signals, including: 19 | - 8 digital inputs at 24Vdc 20 | - 2 channels for encoder readings 21 | - 3 analog inputs for PT100/J/K temperature probes 22 | - 3 analog inputs for 4-20mA/0-10V/NTC signals 23 | 24 | - Manages output signals, including: 25 | - 8 digital outputs at 24Vdc 26 | - 4 analog outputs at 0-10V 27 | 28 | - Provides control for other I/O: 29 | - 12 programmable digital I/O at 24V 30 | 31 | - Supports various communication protocols: 32 | - CAN-BUS 33 | - Serial protocols (RS232/RS422/RS485) 34 | - USB 35 | 36 | - Handles RTC (Real-Time Clock) functionality 37 | 38 | ## Usage 39 | 40 | To use this library, you must have a properly powered Portenta Machine Control board running at 24V. Once you have ensured the correct power supply and established the connection, you can include the machine control library in your Arduino sketch and use its functions to manage the board features. 41 | 42 | Here is a minimal example to control a digital output: 43 | 44 | ```cpp 45 | // Include the Arduino PortentaMachineControl library 46 | #include 47 | 48 | void setup() { 49 | // Initialize the digital outputs module of the MachineControl library 50 | MachineControl_DigitalOutputs.begin(); 51 | } 52 | 53 | void loop() { 54 | // Turn on the digital output at channel 0 55 | MachineControl_DigitalOutputs.write(0, HIGH); 56 | delay(1000); 57 | // Turn off the digital output at channel 0 58 | MachineControl_DigitalOutputs.write(0, LOW); 59 | delay(1000); 60 | } 61 | ``` 62 | 63 | ## Examples 64 | 65 | - **[Analog_input_0_10V](../examples/Analog_input/Analog_input_0_10V):** This example demonstrates how to read analog input signals in the 0-10V range. 66 | - **[Analog_input_4_20mA](../examples/Analog_input/Analog_input_4_20mA):** This example demonstrates how to read analog input signals in the 4-20mA range. 67 | - **[Analog_input_NTC](../examples/Analog_input/Analog_input_NTC):** This example shows reading analog input signals from NTC temperature probes. 68 | - **[Fast_Analog_input_0_10V](../examples/Analog_input/Fast_Analog_input_0_10V):** This example demonstrates how to read analog input signals in the 0-10V range using the [Arduino_AdvancedAnalog](https://github.com/arduino-libraries/Arduino_AdvancedAnalog) library. 69 | - **[Analog_Out](../examples/Analog_Out):** This example shows how to control analog output signals. 70 | - **[ReadCan](../examples/CAN/ReadCan):** This example demonstrates how to read data using the CAN-BUS communication protocol. 71 | - **[WriteCan](../examples/CAN/WriteCan):** This example demonstrates how to send data using the CAN-BUS communication protocol. 72 | - **[Digital_output](../examples/Digital_output):** This example shows how to control digital output signals. 73 | - **[Digital_input](../examples/Digital_programmable/Digital_input):** This example shows how to read digital input signals. 74 | - **[GPIO_programmable](../examples/Digital_programmable/GPIO_programmable):** This example demonstrates the usage of programmable digital I/O pins. 75 | - **[Encoders](../examples/Encoders):** This example shows how to work with encoder readings. 76 | - **[Ethernet](../examples/Ethernet):** This example shows how to establish Ethernet communication and connects to a website. 77 | - **[RS232](../examples/RS232):** This example demonstrates serial communication using the RS232 protocol. 78 | - **[RS485_fullduplex](../examples/RS485_fullduplex):** This example demonstrates full-duplex serial communication using the RS485 protocol. 79 | - **[RS485_halfduplex](../examples/RS485_halfduplex):** This example demonstrates half-duplex serial communication using the RS485 protocol. 80 | - **[RTC](../examples/RTC):** This example shows how to interact with the Real-Time Clock functionality. 81 | - **[RTC_Alarm](../examples/RTC_Alarm):** This example demonstrates how to set up and utilize RTC alarms. 82 | - **[Temp_probes_RTD](../examples/Temp_probes_RTD):** This example demonstrates the temperature probe readings using RTD sensors. 83 | - **[Temp_probes_Thermocouples](../examples/Temp_probes_Thermocouples):** This example demonstrates the temperature probe readings using thermocouple sensors. 84 | - **[USB_host](../examples/USB_host):** This example shows the USB host functionality. 85 | 86 | ## API 87 | 88 | The API documentation can be found [here](./api.md). 89 | 90 | ## License 91 | 92 | This library is released under the [MPL-2.0 license](https://github.com/arduino-libraries/Arduino_PortentaMachineControl/blob/main/LICENSE). 93 | -------------------------------------------------------------------------------- /src/utility/THERMOCOUPLE/MAX31855.cpp: -------------------------------------------------------------------------------- 1 | #include "MAX31855.h" 2 | 3 | const double MAX31855Class::Jm210_760[] ; 4 | const double MAX31855Class::J760_1200[] ; 5 | const double MAX31855Class::Km270_0[] ; 6 | const double MAX31855Class::K0_1372[] ; 7 | 8 | const double MAX31855Class::InvJ_neg[] ; 9 | const double MAX31855Class::InvJ0_760[] ; 10 | const double MAX31855Class::InvJ760_1200[] ; 11 | 12 | const double MAX31855Class::InvK_neg[] ; 13 | const double MAX31855Class::InvK0_500[] ; 14 | const double MAX31855Class::InvK500_1372[] ; 15 | 16 | const MAX31855Class::coefftable MAX31855Class::CoeffJ[]; 17 | const MAX31855Class::coefftable MAX31855Class::CoeffK[]; 18 | 19 | const MAX31855Class::coefftable MAX31855Class::InvCoeffJ[]; 20 | const MAX31855Class::coefftable MAX31855Class::InvCoeffK[]; 21 | 22 | MAX31855Class::MAX31855Class(PinName cs, SPIClass& spi) : 23 | _cs(cs), 24 | _spi(&spi), 25 | _spiSettings(4000000, MSBFIRST, SPI_MODE0), 26 | _coldOffset(2.10f) 27 | { 28 | } 29 | 30 | int MAX31855Class::begin() 31 | { 32 | uint32_t rawword; 33 | 34 | pinMode(_cs, OUTPUT); 35 | digitalWrite(_cs, HIGH); 36 | _spi->begin(); 37 | 38 | rawword = readSensor(); 39 | if (rawword == 0xFFFFFF) { 40 | end(); 41 | 42 | return 0; 43 | } 44 | 45 | return 1; 46 | } 47 | 48 | void MAX31855Class::end() 49 | { 50 | pinMode(_cs, INPUT); 51 | digitalWrite(_cs, LOW); 52 | _spi->end(); 53 | } 54 | 55 | uint32_t MAX31855Class::readSensor() 56 | { 57 | uint32_t read = 0x00; 58 | 59 | digitalWrite(_cs, LOW); 60 | delayMicroseconds(1); 61 | 62 | _spi->beginTransaction(_spiSettings); 63 | 64 | 65 | for (int i = 0; i < 4; i++) { 66 | read <<= 8; 67 | read |= _spi->transfer(0); 68 | } 69 | 70 | _spi->endTransaction(); 71 | 72 | digitalWrite(_cs, HIGH); 73 | return read; 74 | } 75 | 76 | double MAX31855Class::polynomial(double value, int tableEntries, coefftable const (*table) ) 77 | { 78 | double output = 0; 79 | double valuePower = 1; 80 | for (int i=0;i0) { 118 | voltage += 0.118597600000E+00 * exp( -0.118343200000E-03 * pow(temp-0.126968600000E+03, 2)); 119 | } 120 | return voltage; 121 | } 122 | 123 | double MAX31855Class::mvtoTemp(int type, double voltage) { 124 | coefftable const (*table); 125 | int tableEntries; 126 | 127 | switch (type) { 128 | case PROBE_J: 129 | table = InvCoeffJ; 130 | tableEntries = sizeof(InvCoeffJ)/sizeof(coefftable); 131 | break; 132 | case PROBE_K: 133 | table = InvCoeffK; 134 | tableEntries = sizeof(InvCoeffJ)/sizeof(coefftable); 135 | break; 136 | } 137 | return polynomial(voltage, tableEntries, table); 138 | } 139 | 140 | float MAX31855Class::readTemperature(int type) 141 | { 142 | uint32_t rawword; 143 | int32_t measuredTempInt; 144 | int32_t measuredColdInt; 145 | double measuredTemp; 146 | double measuredCold; 147 | double measuredVolt; 148 | 149 | rawword = readSensor(); 150 | 151 | // Check for reading error 152 | if (rawword & 0x7) { 153 | return NAN; 154 | } 155 | // The cold junction temperature is stored in the last 14 word's bits 156 | // whereas the ttermocouple temperature (non linearized) is in the topmost 18 bits 157 | // sent by the Thermocouple-to-Digital Converter 158 | 159 | // sign extend thermocouple value 160 | if (rawword & 0x80000000) { 161 | // Negative value, drop the lower 18 bits and explicitly extend sign bits. 162 | measuredTempInt = 0xFFFC0000 | ((rawword >> 18) & 0x00003FFFF); 163 | } else { 164 | // Positive value, just drop the lower 18 bits. 165 | measuredTempInt = rawword>>18; 166 | } 167 | 168 | // convert it to degrees 169 | measuredTemp = measuredTempInt * 0.25f; 170 | 171 | // sign extend cold junction temperature 172 | measuredColdInt = (rawword>>4)&0xfff; 173 | if (measuredColdInt&0x800) { 174 | // Negative value, sign extend 175 | measuredColdInt |= 0xfffff000; 176 | } 177 | 178 | // convert it to degrees 179 | measuredCold = (measuredColdInt/16.0f); 180 | // now the tricky part... since MAX31855K is considering a linear response 181 | // and is trimemd for K thermocouples, we have to convert the reading back 182 | // to mV and then use NIST polynomial approximation to determine temperature 183 | // we know that reading from chip is calculated as: 184 | // temp = chip_temperature + thermocouple_voltage/0.041276f 185 | // 186 | // convert temperature to mV is accomplished converting the chip temperature 187 | // to mV using NIST polynomial and then by adding the measured voltage 188 | // calculated inverting the function above 189 | // this way we calculate the voltage we would have measured if cold junction 190 | // was at 0 degrees celsius 191 | 192 | measuredVolt = coldTempTomv(type, measuredCold - _coldOffset)+(measuredTemp - measuredCold) * 0.041276f; 193 | 194 | // finally from the cold junction compensated voltage we calculate the temperature 195 | // using NIST polynomial approximation for the thermocouple type we are using 196 | return mvtoTemp(type,measuredVolt); 197 | } 198 | 199 | float MAX31855Class::readReferenceTemperature(int type) 200 | { 201 | uint32_t rawword; 202 | float ref; 203 | 204 | rawword = readSensor(); 205 | 206 | // ignore first 4 FAULT bits 207 | rawword >>= 4; 208 | 209 | // The cold junction reference temperature is stored in the first 11 word's bits 210 | // sent by the Thermocouple-to-Digital Converter 211 | rawword = rawword & 0xFFF; 212 | // check sign bit and convert to negative value. 213 | if (rawword & 0x800) { 214 | ref = (0xF800 | (rawword & 0x7FF))*0.0625; 215 | } else { 216 | // multiply for the LSB value 217 | ref = rawword * 0.0625f; 218 | } 219 | 220 | return ref; 221 | } 222 | 223 | void MAX31855Class::setColdOffset(float offset) 224 | { 225 | _coldOffset = offset; 226 | } 227 | 228 | MAX31855Class THERM; 229 | -------------------------------------------------------------------------------- /src/utility/QEI/QEI.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Aaron Berk 3 | * 4 | * @section LICENSE 5 | * 6 | * Copyright (c) 2010 ARM Limited 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | * @section DESCRIPTION 27 | * 28 | * Quadrature Encoder Interface. 29 | * 30 | * A quadrature encoder consists of two code tracks on a disc which are 90 31 | * degrees out of phase. It can be used to determine how far a wheel has 32 | * rotated, relative to a known starting position. 33 | * 34 | * Only one code track changes at a time leading to a more robust system than 35 | * a single track, because any jitter around any edge won't cause a state 36 | * change as the other track will remain constant. 37 | * 38 | * Encoders can be a homebrew affair, consisting of infrared emitters/receivers 39 | * and paper code tracks consisting of alternating black and white sections; 40 | * alternatively, complete disk and PCB emitter/receiver encoder systems can 41 | * be bought, but the interface, regardless of implementation is the same. 42 | * 43 | * +-----+ +-----+ +-----+ 44 | * Channel A | ^ | | | | | 45 | * ---+ ^ +-----+ +-----+ +----- 46 | * ^ ^ 47 | * ^ +-----+ +-----+ +-----+ 48 | * Channel B ^ | | | | | | 49 | * ------+ +-----+ +-----+ +----- 50 | * ^ ^ 51 | * ^ ^ 52 | * 90deg 53 | * 54 | * The interface uses X2 encoding by default which calculates the pulse count 55 | * based on reading the current state after each rising and falling edge of 56 | * channel A. 57 | * 58 | * +-----+ +-----+ +-----+ 59 | * Channel A | | | | | | 60 | * ---+ +-----+ +-----+ +----- 61 | * ^ ^ ^ ^ ^ 62 | * ^ +-----+ ^ +-----+ ^ +-----+ 63 | * Channel B ^ | ^ | ^ | ^ | ^ | | 64 | * ------+ ^ +-----+ ^ +-----+ +-- 65 | * ^ ^ ^ ^ ^ 66 | * ^ ^ ^ ^ ^ 67 | * Pulse count 0 1 2 3 4 5 ... 68 | * 69 | * This interface can also use X4 encoding which calculates the pulse count 70 | * based on reading the current state after each rising and falling edge of 71 | * either channel. 72 | * 73 | * +-----+ +-----+ +-----+ 74 | * Channel A | | | | | | 75 | * ---+ +-----+ +-----+ +----- 76 | * ^ ^ ^ ^ ^ 77 | * ^ +-----+ ^ +-----+ ^ +-----+ 78 | * Channel B ^ | ^ | ^ | ^ | ^ | | 79 | * ------+ ^ +-----+ ^ +-----+ +-- 80 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 81 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 82 | * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 83 | * 84 | * It defaults 85 | * 86 | * An optional index channel can be used which determines when a full 87 | * revolution has occured. 88 | * 89 | * If a 4 pules per revolution encoder was used, with X4 encoding, 90 | * the following would be observed. 91 | * 92 | * +-----+ +-----+ +-----+ 93 | * Channel A | | | | | | 94 | * ---+ +-----+ +-----+ +----- 95 | * ^ ^ ^ ^ ^ 96 | * ^ +-----+ ^ +-----+ ^ +-----+ 97 | * Channel B ^ | ^ | ^ | ^ | ^ | | 98 | * ------+ ^ +-----+ ^ +-----+ +-- 99 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 100 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 101 | * ^ ^ ^ +--+ ^ ^ +--+ ^ 102 | * ^ ^ ^ | | ^ ^ | | ^ 103 | * Index ------------+ +--------+ +----------- 104 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 105 | * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 106 | * Rev. count 0 1 2 107 | * 108 | * Rotational position in degrees can be calculated by: 109 | * 110 | * (pulse count / X * N) * 360 111 | * 112 | * Where X is the encoding type [e.g. X4 encoding => X=4], and N is the number 113 | * of pulses per revolution. 114 | * 115 | * Linear position can be calculated by: 116 | * 117 | * (pulse count / X * N) * (1 / PPI) 118 | * 119 | * Where X is encoding type [e.g. X4 encoding => X=44], N is the number of 120 | * pulses per revolution, and PPI is pulses per inch, or the equivalent for 121 | * any other unit of displacement. PPI can be calculated by taking the 122 | * circumference of the wheel or encoder disk and dividing it by the number 123 | * of pulses per revolution. 124 | */ 125 | 126 | #ifndef QEI_H 127 | #define QEI_H 128 | 129 | /** 130 | * Includes 131 | */ 132 | #include "mbed.h" 133 | 134 | /** 135 | * Defines 136 | */ 137 | #define PREV_MASK 0x1 //Mask for the previous state in determining direction 138 | //of rotation. 139 | #define CURR_MASK 0x2 //Mask for the current state in determining direction 140 | //of rotation. 141 | #define INVALID 0x3 //XORing two states where both bits have changed. 142 | 143 | /** 144 | * Quadrature Encoder Interface. 145 | */ 146 | class QEI { 147 | 148 | public: 149 | 150 | typedef enum Encoding { 151 | X1_ENCODING, 152 | X2_ENCODING, 153 | X4_ENCODING 154 | 155 | } Encoding; 156 | 157 | /** 158 | * Constructor. 159 | * 160 | * Reads the current values on channel A and channel B to determine the 161 | * initial state. 162 | * 163 | * Attaches the encode function to the rise/fall interrupt edges of 164 | * channels A and B to perform X4 encoding. 165 | * 166 | * Attaches the index function to the rise interrupt edge of channel index 167 | * (if it is used) to count revolutions. 168 | * 169 | * @param channelA mbed pin for channel A input. 170 | * @param channelB mbed pin for channel B input. 171 | * @param index mbed pin for optional index channel input, 172 | * (pass NC if not needed). 173 | * @param pulsesPerRev Number of pulses in one revolution. 174 | * @param encoding The encoding to use. Uses X2 encoding by default. X2 175 | * encoding uses interrupts on the rising and falling edges 176 | * of only channel A where as X4 uses them on both 177 | * channels. 178 | */ 179 | QEI(PinName channelA, PinName channelB, PinName index, int pulsesPerRev, Encoding encoding = X2_ENCODING); 180 | 181 | /** 182 | * Reset the encoder. 183 | * 184 | * Sets the pulses and revolutions count to zero. 185 | */ 186 | void reset(void); 187 | 188 | /** 189 | * Read the state of the encoder. 190 | * 191 | * @return The current state of the encoder as a 2-bit number, where: 192 | * bit 1 = The reading from channel B 193 | * bit 2 = The reading from channel A 194 | */ 195 | int getCurrentState(void); 196 | 197 | /** 198 | * Read the number of pulses recorded by the encoder. 199 | * 200 | * @return Number of pulses which have occured. 201 | */ 202 | int getPulses(void); 203 | 204 | /** 205 | * Read the number of revolutions recorded by the encoder on the index channel. 206 | * 207 | * @return Number of revolutions which have occured on the index channel. 208 | */ 209 | int getRevolutions(void); 210 | 211 | /** 212 | * Set the ecoding type of the encoder. 213 | * 214 | * Changes the type of encoding used by the encoder from the default X2_ENCODING. 215 | */ 216 | void setEncoding(Encoding encoding); 217 | 218 | private: 219 | 220 | /** 221 | * Update the pulse count. 222 | * 223 | * Called on every rising/falling edge of channels A/B. 224 | * 225 | * Reads the state of the channels and determines whether a pulse forward 226 | * or backward has occured, updating the count appropriately. 227 | */ 228 | void encode(void); 229 | 230 | /** 231 | * Called on every rising edge of channel index to update revolution 232 | * count by one. 233 | */ 234 | void index(void); 235 | 236 | Encoding encoding_; 237 | 238 | mbed::InterruptIn channelA_; 239 | mbed::InterruptIn channelB_; 240 | mbed::InterruptIn index_; 241 | 242 | int pulsesPerRev_; 243 | int prevState_; 244 | int currState_; 245 | 246 | volatile int pulses_; 247 | volatile int revolutions_; 248 | 249 | }; 250 | 251 | #endif /* QEI_H */ 252 | -------------------------------------------------------------------------------- /src/utility/QEI/QEI.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Aaron Berk 3 | * 4 | * @section LICENSE 5 | * 6 | * Copyright (c) 2010 ARM Limited 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | * @section DESCRIPTION 27 | * 28 | * Quadrature Encoder Interface. 29 | * 30 | * A quadrature encoder consists of two code tracks on a disc which are 90 31 | * degrees out of phase. It can be used to determine how far a wheel has 32 | * rotated, relative to a known starting position. 33 | * 34 | * Only one code track changes at a time leading to a more robust system than 35 | * a single track, because any jitter around any edge won't cause a state 36 | * change as the other track will remain constant. 37 | * 38 | * Encoders can be a homebrew affair, consisting of infrared emitters/receivers 39 | * and paper code tracks consisting of alternating black and white sections; 40 | * alternatively, complete disk and PCB emitter/receiver encoder systems can 41 | * be bought, but the interface, regardless of implementation is the same. 42 | * 43 | * +-----+ +-----+ +-----+ 44 | * Channel A | ^ | | | | | 45 | * ---+ ^ +-----+ +-----+ +----- 46 | * ^ ^ 47 | * ^ +-----+ +-----+ +-----+ 48 | * Channel B ^ | | | | | | 49 | * ------+ +-----+ +-----+ +----- 50 | * ^ ^ 51 | * ^ ^ 52 | * 90deg 53 | * 54 | * The interface uses X2 encoding by default which calculates the pulse count 55 | * based on reading the current state after each rising and falling edge of 56 | * channel A. 57 | * 58 | * +-----+ +-----+ +-----+ 59 | * Channel A | | | | | | 60 | * ---+ +-----+ +-----+ +----- 61 | * ^ ^ ^ ^ ^ 62 | * ^ +-----+ ^ +-----+ ^ +-----+ 63 | * Channel B ^ | ^ | ^ | ^ | ^ | | 64 | * ------+ ^ +-----+ ^ +-----+ +-- 65 | * ^ ^ ^ ^ ^ 66 | * ^ ^ ^ ^ ^ 67 | * Pulse count 0 1 2 3 4 5 ... 68 | * 69 | * This interface can also use X4 encoding which calculates the pulse count 70 | * based on reading the current state after each rising and falling edge of 71 | * either channel. 72 | * 73 | * +-----+ +-----+ +-----+ 74 | * Channel A | | | | | | 75 | * ---+ +-----+ +-----+ +----- 76 | * ^ ^ ^ ^ ^ 77 | * ^ +-----+ ^ +-----+ ^ +-----+ 78 | * Channel B ^ | ^ | ^ | ^ | ^ | | 79 | * ------+ ^ +-----+ ^ +-----+ +-- 80 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 81 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 82 | * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 83 | * 84 | * It defaults 85 | * 86 | * An optional index channel can be used which determines when a full 87 | * revolution has occured. 88 | * 89 | * If a 4 pules per revolution encoder was used, with X4 encoding, 90 | * the following would be observed. 91 | * 92 | * +-----+ +-----+ +-----+ 93 | * Channel A | | | | | | 94 | * ---+ +-----+ +-----+ +----- 95 | * ^ ^ ^ ^ ^ 96 | * ^ +-----+ ^ +-----+ ^ +-----+ 97 | * Channel B ^ | ^ | ^ | ^ | ^ | | 98 | * ------+ ^ +-----+ ^ +-----+ +-- 99 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 100 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 101 | * ^ ^ ^ +--+ ^ ^ +--+ ^ 102 | * ^ ^ ^ | | ^ ^ | | ^ 103 | * Index ------------+ +--------+ +----------- 104 | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 105 | * Pulse count 0 1 2 3 4 5 6 7 8 9 ... 106 | * Rev. count 0 1 2 107 | * 108 | * Rotational position in degrees can be calculated by: 109 | * 110 | * (pulse count / X * N) * 360 111 | * 112 | * Where X is the encoding type [e.g. X4 encoding => X=4], and N is the number 113 | * of pulses per revolution. 114 | * 115 | * Linear position can be calculated by: 116 | * 117 | * (pulse count / X * N) * (1 / PPI) 118 | * 119 | * Where X is encoding type [e.g. X4 encoding => X=44], N is the number of 120 | * pulses per revolution, and PPI is pulses per inch, or the equivalent for 121 | * any other unit of displacement. PPI can be calculated by taking the 122 | * circumference of the wheel or encoder disk and dividing it by the number 123 | * of pulses per revolution. 124 | */ 125 | 126 | /** 127 | * Includes 128 | */ 129 | #include "QEI.h" 130 | 131 | QEI::QEI(PinName channelA, 132 | PinName channelB, 133 | PinName index, 134 | int pulsesPerRev, 135 | Encoding encoding) : channelA_(channelA), channelB_(channelB), 136 | index_(index) { 137 | 138 | pulses_ = 0; 139 | revolutions_ = 0; 140 | pulsesPerRev_ = pulsesPerRev; 141 | encoding_ = encoding; 142 | 143 | //Workout what the current state is. 144 | int chanA = channelA_.read(); 145 | int chanB = channelB_.read(); 146 | 147 | //2-bit state. 148 | currState_ = (chanA << 1) | (chanB); 149 | prevState_ = currState_; 150 | 151 | setEncoding(encoding); 152 | //Index is optional. 153 | if (index != NC) { 154 | index_.rise(mbed::callback(this, &QEI::index)); 155 | } 156 | 157 | } 158 | 159 | void QEI::reset(void) { 160 | 161 | pulses_ = 0; 162 | revolutions_ = 0; 163 | 164 | } 165 | 166 | int QEI::getCurrentState(void) { 167 | 168 | return currState_; 169 | 170 | } 171 | 172 | int QEI::getPulses(void) { 173 | 174 | return pulses_; 175 | 176 | } 177 | 178 | int QEI::getRevolutions(void) { 179 | 180 | return revolutions_; 181 | 182 | } 183 | 184 | void QEI::setEncoding(Encoding encoding) { 185 | //X2 encoding uses interrupts on only channel A. 186 | //X4 encoding uses interrupts on channel A, 187 | //and on channel B. 188 | channelA_.rise(mbed::callback(this, &QEI::encode)); 189 | if(encoding != X1_ENCODING){ 190 | channelA_.fall(mbed::callback(this, &QEI::encode)); 191 | } else { 192 | channelA_.fall(0); 193 | } 194 | 195 | //If we're using X4 encoding, then attach interrupts to channel B too. 196 | if (encoding == X4_ENCODING) { 197 | channelB_.rise(mbed::callback(this, &QEI::encode)); 198 | channelB_.fall(mbed::callback(this, &QEI::encode)); 199 | } 200 | } 201 | 202 | // +-------------+ 203 | // | X1 Encoding | 204 | // +-------------+ 205 | // 206 | // When observing states two patterns will appear: 207 | // 208 | // Counter clockwise rotation: 209 | // 210 | // 10 -> 10 -> 10 -> 10 -> ... 211 | // 212 | // Clockwise rotation: 213 | // 214 | // 11 -> 11 -> 11 -> ... 215 | // 216 | // +-------------+ 217 | // | X2 Encoding | 218 | // +-------------+ 219 | // 220 | // When observing states two patterns will appear: 221 | // 222 | // Counter clockwise rotation: 223 | // 224 | // 10 -> 01 -> 10 -> 01 -> ... 225 | // 226 | // Clockwise rotation: 227 | // 228 | // 11 -> 00 -> 11 -> 00 -> ... 229 | // 230 | // We consider counter clockwise rotation to be "forward" and 231 | // counter clockwise to be "backward". Therefore pulse count will increase 232 | // during counter clockwise rotation and decrease during clockwise rotation. 233 | // 234 | // +-------------+ 235 | // | X4 Encoding | 236 | // +-------------+ 237 | // 238 | // There are four possible states for a quadrature encoder which correspond to 239 | // 2-bit gray code. 240 | // 241 | // A state change is only valid if of only one bit has changed. 242 | // A state change is invalid if both bits have changed. 243 | // 244 | // Clockwise Rotation -> 245 | // 246 | // 00 01 11 10 00 247 | // 248 | // <- Counter Clockwise Rotation 249 | // 250 | // If we observe any valid state changes going from left to right, we have 251 | // moved one pulse clockwise [we will consider this "backward" or "negative"]. 252 | // 253 | // If we observe any valid state changes going from right to left we have 254 | // moved one pulse counter clockwise [we will consider this "forward" or 255 | // "positive"]. 256 | // 257 | // We might enter an invalid state for a number of reasons which are hard to 258 | // predict - if this is the case, it is generally safe to ignore it, update 259 | // the state and carry on, with the error correcting itself shortly after. 260 | void QEI::encode(void) { 261 | 262 | int change = 0; 263 | int chanA = channelA_.read(); 264 | int chanB = channelB_.read(); 265 | 266 | //2-bit state. 267 | currState_ = (chanA << 1) | (chanB); 268 | 269 | if(encoding_ == X1_ENCODING){ 270 | if(currState_ == 0x3){ 271 | pulses_++; 272 | } 273 | if(currState_ == 0x2){ 274 | pulses_--; 275 | } 276 | } else if (encoding_ == X2_ENCODING) { 277 | 278 | //11->00->11->00 is counter clockwise rotation or "forward". 279 | if ((prevState_ == 0x3 && currState_ == 0x0) || 280 | (prevState_ == 0x0 && currState_ == 0x3)) { 281 | 282 | pulses_++; 283 | 284 | } 285 | //10->01->10->01 is clockwise rotation or "backward". 286 | else if ((prevState_ == 0x2 && currState_ == 0x1) || 287 | (prevState_ == 0x1 && currState_ == 0x2)) { 288 | 289 | pulses_--; 290 | 291 | } 292 | 293 | } else if (encoding_ == X4_ENCODING) { 294 | 295 | //Entered a new valid state. 296 | if (((currState_ ^ prevState_) != INVALID) && (currState_ != prevState_)) { 297 | //2 bit state. Right hand bit of prev XOR left hand bit of current 298 | //gives 0 if clockwise rotation and 1 if counter clockwise rotation. 299 | change = (prevState_ & PREV_MASK) ^ ((currState_ & CURR_MASK) >> 1); 300 | 301 | if (change == 0) { 302 | change = -1; 303 | } 304 | 305 | pulses_ -= change; 306 | } 307 | 308 | } 309 | 310 | prevState_ = currState_; 311 | 312 | } 313 | 314 | void QEI::index(void) { 315 | 316 | revolutions_++; 317 | 318 | } 319 | -------------------------------------------------------------------------------- /src/utility/RTC/PCF8563T.cpp: -------------------------------------------------------------------------------- 1 | #include "PCF8563T.h" 2 | 3 | #define PCF8563T_ADDRESS 0x51 4 | #define PCF8563T_STATUS_2_REG 0X01 5 | #define PCF8563T_VL_SECONDS_REG 0X02 6 | #define PCF8563T_MINUTES_REG 0x03 7 | #define PCF8563T_HOURS_REG 0X04 8 | #define PCF8563T_DAYS_REG 0x05 9 | #define PCF8563T_MONTHS_REG 0x07 10 | #define PCF8563T_YEARS_REG 0x08 11 | 12 | // alarm management 13 | #define PCF8563T_MINUTE_ALARM_REG 0x09 14 | #define PCF8563T_MINUTE_ALARM_AE_M_MASK 0x80 15 | #define PCF8563T_MINUTE_ALARM_ON 0x7F 16 | 17 | #define PCF8563T_HOUR_ALARM_REG 0x0A 18 | #define PCF8563T_HOUR_ALARM_AE_H_MASK 0x80 19 | #define PCF8563T_HOUR_ALARM_ON 0x7F 20 | 21 | #define PCF8563T_DAY_ALARM_REG 0x0B 22 | #define PCF8563T_DAY_ALARM_AE_D_MASK 0x80 23 | #define PCF8563T_DAY_ALARM_ON 0x7F 24 | 25 | #define PCF8563T_TIMER_CONTROL_REG 0X0E 26 | #define PCF8563T_TIMER_CONTROL_ON 0x80 27 | #define PCF8563T_TIMER_CONTROL_OFF 0x7F 28 | 29 | #define PCF8563T_STATUS_2_AIE_MASK 0x02 30 | #define PCF8563T_STATUS_2_CLEAR_INT 0xF7 31 | #define PCF8563T_STATUS_2_INT_OFF 0x7d 32 | 33 | /** 34 | * Object constructor 35 | * 36 | */ 37 | PCF8563TClass::PCF8563TClass() 38 | { 39 | } 40 | 41 | /** 42 | * Start the communication with the RTC 43 | * Initialize I2C (Wire1) bus and check if the chip is connected by sending an ACK on the I2C bus. 44 | * @return true if the RTC Controller is on the I2C bus, false if it is not. 45 | * 46 | */ 47 | bool PCF8563TClass::begin() 48 | { 49 | Wire1.begin(); // join i2c bus 50 | 51 | Wire1.beginTransmission(PCF8563T_ADDRESS); 52 | if (!Wire1.endTransmission()) { 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | /** 59 | * Set Year number's value 60 | * Save an unsigned byte with the Year's value 61 | * @param years Year's unsigned byte 62 | */ 63 | void PCF8563TClass::setYear(uint8_t years) { 64 | uint8_t dec = years / 10; 65 | uint8_t unit = years - (dec * 10); 66 | writeByte(PCF8563T_YEARS_REG, ((dec << 4) + unit)); 67 | } 68 | 69 | /** 70 | * Set Month number's value 71 | * Save an unsigned byte with the Month's value 72 | * @param months Month's unsigned byte (0 to 12) 73 | */ 74 | void PCF8563TClass::setMonth(uint8_t months) { 75 | uint8_t offset = 0; 76 | if (months > 9) { 77 | offset = 6; 78 | } 79 | writeByte(PCF8563T_MONTHS_REG, months + offset); 80 | } 81 | 82 | /** 83 | * Set Day number's value 84 | * Save an unsigned byte with the Day's value 85 | * @param days day's unsigned byte 86 | */ 87 | void PCF8563TClass::setDay(uint8_t days) { 88 | uint8_t dec = days / 10; 89 | uint8_t unit = days - (dec * 10); 90 | writeByte(PCF8563T_DAYS_REG, ((dec << 4) + unit)); 91 | } 92 | 93 | /** 94 | * Set Hour(s) number's value 95 | * Save an unsigned byte with the Hour(s) value 96 | * @param hours hour unsigned byte (0 - 23) 97 | */ 98 | void PCF8563TClass::setHours(uint8_t hours) { 99 | uint8_t dec = hours / 10; 100 | uint8_t unit = hours - (dec * 10); 101 | writeByte(PCF8563T_HOURS_REG, ((dec << 4) + unit)); //check formula on datasheet val + 6 * (val / 10) 102 | } 103 | 104 | /** 105 | * Set Minute(s) number's value 106 | * Save an unsigned byte with the Minute(s) value 107 | * @param minutes minute unsigned byte (0-60) 108 | */ 109 | void PCF8563TClass::setMinutes(uint8_t minutes) { 110 | uint8_t dec = minutes / 10; 111 | uint8_t unit = minutes - (dec * 10); 112 | writeByte(PCF8563T_MINUTES_REG, ((dec << 4) + unit)); 113 | } 114 | 115 | /** 116 | * Set Second(s) number's value 117 | * Save an unsigned byte with the Second(s) value 118 | * @param seconds Second(s) unsigned byte (0-60) 119 | */ 120 | void PCF8563TClass::setSeconds(uint8_t seconds) { 121 | uint8_t dec = seconds / 10; 122 | uint8_t unit = seconds - (dec * 10); 123 | writeByte(PCF8563T_VL_SECONDS_REG, ((dec << 4) + unit)); 124 | } 125 | 126 | /** 127 | * Get Year(s) number's value 128 | * Get unsigned byte with the Year(s) value 129 | * @return byte with Year(s) value 130 | */ 131 | uint8_t PCF8563TClass::getYear() { 132 | uint8_t years = readByte(PCF8563T_YEARS_REG); 133 | return (years & 0x0F) + ((years >> 4)*10); 134 | } 135 | 136 | /** 137 | * Get Month(s) month's value 138 | * Get unsigned byte with the month(s) value 139 | * @return byte with Month(s) value 140 | */ 141 | uint8_t PCF8563TClass::getMonth() { 142 | uint8_t months = readByte(PCF8563T_MONTHS_REG) & 0x1F; 143 | if(months > 9) { 144 | return months - 6; 145 | } else { 146 | return months; 147 | } 148 | } 149 | 150 | /** 151 | * Get Day(s) number's value 152 | * Get unsigned byte with the Day(s) value 153 | * @return byte with Day(s) value 154 | */ 155 | uint8_t PCF8563TClass::getDay() { 156 | uint8_t days = readByte(PCF8563T_DAYS_REG) & 0x3F; 157 | return (days & 0x0F) + ((days >> 4)*10); 158 | } 159 | 160 | /** 161 | * Get Hour(s) number's value 162 | * Get unsigned byte with the Hour(s) value 163 | * @return byte with Hour(s) value 164 | */ 165 | uint8_t PCF8563TClass::getHours() { 166 | uint8_t hours = readByte(PCF8563T_HOURS_REG) & 0x3F; 167 | return (hours & 0x0F) + ((hours >> 4)*10); 168 | } 169 | 170 | /** 171 | * Get Minute(s) number's value 172 | * Get unsigned byte with the Minute(s) value 173 | * @return byte with Minute(s) value 174 | */ 175 | uint8_t PCF8563TClass::getMinutes() { 176 | uint8_t minutes = (readByte(PCF8563T_MINUTES_REG)) & 0x7F ; 177 | return (minutes & 0x0F) + ((minutes >> 4)*10); 178 | } 179 | 180 | /** 181 | * Get Second(s) number's value 182 | * Get unsigned byte with the Second(s) value 183 | * @return byte with Second(s) value 184 | */ 185 | uint8_t PCF8563TClass::getSeconds() { 186 | uint8_t seconds = readByte(PCF8563T_VL_SECONDS_REG) & 0x7F; 187 | return (seconds & 0x0F) + ((seconds >> 4)*10); 188 | } 189 | 190 | /** 191 | * Set time Epoch format 192 | * 193 | */ 194 | void PCF8563TClass::setEpoch() { 195 | struct tm time; 196 | time.tm_sec = getSeconds(); 197 | time.tm_min = getMinutes(); 198 | time.tm_hour = getHours(); 199 | time.tm_mday = getDay(); 200 | time.tm_mon = getMonth() - 1; 201 | time.tm_year = getYear() + 100; 202 | time_t seconds; 203 | _rtc_maketime(&time, &seconds, RTC_FULL_LEAP_YEAR_SUPPORT); 204 | set_time(seconds); 205 | } 206 | 207 | /** 208 | * Set time with Epoch format 209 | * 210 | * 211 | * @param seconds number of seconds (time_t type) 212 | */ 213 | void PCF8563TClass::setEpoch(time_t seconds) { 214 | struct tm time; 215 | _rtc_localtime(seconds, &time, RTC_FULL_LEAP_YEAR_SUPPORT); 216 | 217 | setSeconds(time.tm_sec); 218 | setMinutes(time.tm_min); 219 | setHours( time.tm_hour); 220 | setDay(time.tm_mday); 221 | setMonth(time.tm_mon + 1); 222 | setYear((time.tm_year - 100)); 223 | set_time(seconds); 224 | } 225 | 226 | /** 227 | * Set time with Epoch format 228 | * 229 | * Convert the input values to Epoch format 230 | * example: Tue, 06 Jul 2021 11:55:27 GMT -> 1625572527 231 | * 232 | * @param years number of years 233 | * @param mohths number of months 234 | * @param days number of days 235 | * @param hours number of hours 236 | * @param minutes number of minutes 237 | * @param seconds number of seconds 238 | */ 239 | void PCF8563TClass::setEpoch(uint8_t years, uint8_t months, uint8_t days, uint8_t hours, uint8_t minutes, uint8_t seconds) { 240 | struct tm time; 241 | time_t utcsec; 242 | 243 | time.tm_sec = seconds; 244 | time.tm_min = minutes; 245 | time.tm_hour = hours; 246 | time.tm_mday = days; 247 | time.tm_mon = months - 1; 248 | time.tm_year = years + 100; // year since 1900 249 | 250 | _rtc_maketime(&time, &utcsec,RTC_FULL_LEAP_YEAR_SUPPORT); 251 | set_time(utcsec); 252 | } 253 | 254 | /** 255 | * Get epoch number 256 | * Convert real time to difference between actual time and Epoch(Unix time) 257 | * Saved into time_t type 258 | * 259 | * example: 1625572527 -> Tue, 06 Jul 2021 11:55:27 GMT 260 | * 261 | * @return number of seconds after Unix time (time_t type) 262 | */ 263 | time_t PCF8563TClass::getEpoch() { 264 | struct tm time; 265 | time_t seconds; 266 | 267 | time.tm_sec = getSeconds(); 268 | time.tm_min = getMinutes(); 269 | time.tm_hour = getHours(); 270 | time.tm_mday = getDay(); 271 | time.tm_mon = getMonth() - 1; 272 | time.tm_year = getYear() + 100; // year since 1900 273 | 274 | _rtc_maketime(&time, &seconds, RTC_FULL_LEAP_YEAR_SUPPORT); 275 | return seconds; 276 | } 277 | 278 | /** 279 | * Enable alarm 280 | * 281 | */ 282 | void PCF8563TClass::enableAlarm() { 283 | writeByte(PCF8563T_STATUS_2_REG, (readByte(PCF8563T_STATUS_2_REG) & PCF8563T_STATUS_2_CLEAR_INT) | PCF8563T_STATUS_2_AIE_MASK); 284 | } 285 | 286 | /** 287 | * Disable alarm 288 | * 289 | */ 290 | void PCF8563TClass::disableAlarm() { 291 | writeByte(PCF8563T_STATUS_2_REG, (readByte(PCF8563T_STATUS_2_REG) & PCF8563T_STATUS_2_INT_OFF)); 292 | } 293 | 294 | /** 295 | * Clear alarm status 296 | * 297 | */ 298 | void PCF8563TClass::clearAlarm() { 299 | writeByte(PCF8563T_STATUS_2_REG, (readByte(PCF8563T_STATUS_2_REG) & PCF8563T_STATUS_2_CLEAR_INT) | PCF8563T_STATUS_2_AIE_MASK); 300 | } 301 | 302 | /** 303 | * Set alarm's minute 304 | * 305 | * @param minutes minute(s) value for the Alarm (byte type) 306 | */ 307 | void PCF8563TClass::setMinuteAlarm(uint8_t minutes) { 308 | uint8_t dec = minutes / 10; 309 | uint8_t unit = minutes - (dec * 10); 310 | uint8_t min_alarm = PCF8563T_MINUTE_ALARM_ON & ((dec << 4) + unit); 311 | writeByte(PCF8563T_MINUTE_ALARM_REG , min_alarm); 312 | } 313 | 314 | /** 315 | * Disable and clear the minute of the Alarm 316 | * 317 | */ 318 | void PCF8563TClass::disableMinuteAlarm() { 319 | writeByte(PCF8563T_MINUTE_ALARM_REG, readByte(PCF8563T_MINUTE_ALARM_REG) | PCF8563T_MINUTE_ALARM_AE_M_MASK); 320 | } 321 | 322 | /** 323 | * Set Alarm's hour 324 | * 325 | * @param hours hour(s) value for the Alarm (byte type) 326 | */ 327 | void PCF8563TClass::setHourAlarm(uint8_t hours) { 328 | uint8_t dec = hours / 10; 329 | uint8_t unit = hours - (dec * 10); 330 | uint8_t hour_alarm = PCF8563T_HOUR_ALARM_AE_H_MASK & ((dec << 4) + unit); 331 | writeByte(PCF8563T_HOUR_ALARM_REG, hour_alarm); //check formula on datasheet val + 6 * (val / 10) 332 | } 333 | 334 | /** 335 | * Disable and clear the hour of the Alarm 336 | * 337 | */ 338 | void PCF8563TClass::disableHourAlarm() { 339 | writeByte(PCF8563T_HOUR_ALARM_REG, readByte(PCF8563T_HOUR_ALARM_REG) | PCF8563T_HOUR_ALARM_AE_H_MASK); 340 | } 341 | 342 | /** 343 | * Set Alarm's day 344 | * 345 | * @param days day value for the Alarm (byte type) 346 | */ 347 | void PCF8563TClass::setDayAlarm(uint8_t days) { 348 | uint8_t dec = days / 10; 349 | uint8_t unit = days - (dec * 10); 350 | uint8_t day_alarm = PCF8563T_DAY_ALARM_ON & ((dec << 4) + unit); 351 | writeByte(PCF8563T_DAY_ALARM_REG, day_alarm); 352 | } 353 | 354 | /** 355 | * Disable and clear the day of the Alarm 356 | * 357 | */ 358 | void PCF8563TClass::disableDayAlarm() { 359 | writeByte(PCF8563T_DAY_ALARM_REG, readByte(PCF8563T_DAY_ALARM_REG) | PCF8563T_DAY_ALARM_AE_D_MASK ); 360 | } 361 | 362 | void PCF8563TClass::writeByte(uint8_t regAddres, uint8_t data) { 363 | Wire1.beginTransmission(PCF8563T_ADDRESS); 364 | Wire1.write(regAddres); 365 | Wire1.write(data); 366 | Wire1.endTransmission(); 367 | } 368 | 369 | uint8_t PCF8563TClass::readByte(uint8_t regAddres) { 370 | Wire1.beginTransmission(PCF8563T_ADDRESS); 371 | Wire1.write(regAddres); // Day Register 372 | Wire1.endTransmission(); 373 | Wire1.requestFrom(PCF8563T_ADDRESS, 1); 374 | 375 | return Wire1.read(); 376 | } 377 | --------------------------------------------------------------------------------