Skip to content

Commit e91fb71

Browse files
committed
DRAFT firmata
1 parent a13a5a2 commit e91fb71

File tree

2 files changed

+335
-0
lines changed

2 files changed

+335
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
2+
3+
#include "HardwareGPIO_FIR.h"
4+
#include "Stream.h" // or <Stream.h> if needed
5+
#include <map>
6+
#include <vector>
7+
#include <cstdint>
8+
9+
namespace arduino {
10+
11+
12+
Stream* firmataStream = nullptr;
13+
std::map<pin_size_t, PinMode> pinModes;
14+
std::map<pin_size_t, PinStatus> pinStates;
15+
std::map<pin_size_t, int> analogValues;
16+
std::vector<uint8_t> rxBuffer;
17+
18+
// Firmata protocol constants
19+
constexpr uint8_t DIGITAL_MESSAGE = 0x90;
20+
constexpr uint8_t ANALOG_MESSAGE = 0xE0;
21+
constexpr uint8_t SET_PIN_MODE = 0xF4;
22+
constexpr uint8_t REPORT_DIGITAL = 0xD0;
23+
constexpr uint8_t REPORT_ANALOG = 0xC0;
24+
25+
HardwareGPIO_FIRMATA::~HardwareGPIO_FIRMATA() {
26+
end();
27+
}
28+
29+
bool HardwareGPIO_FIRMATA::begin(Stream &stream) {
30+
firmataStream = &stream;
31+
is_open = true;
32+
rxBuffer.clear();
33+
return true;
34+
}
35+
36+
void HardwareGPIO_FIRMATA::end() {
37+
is_open = false;
38+
firmataStream = nullptr;
39+
pinModes.clear();
40+
pinStates.clear();
41+
analogValues.clear();
42+
rxBuffer.clear();
43+
}
44+
45+
void HardwareGPIO_FIRMATA::pinMode(pin_size_t pinNumber, PinMode pinMode) {
46+
pinModes[pinNumber] = pinMode;
47+
if (firmataStream) {
48+
firmataStream->write(SET_PIN_MODE);
49+
firmataStream->write(pinNumber);
50+
firmataStream->write(pinMode);
51+
}
52+
}
53+
54+
void HardwareGPIO_FIRMATA::digitalWrite(pin_size_t pinNumber, PinStatus status) {
55+
pinStates[pinNumber] = status;
56+
if (firmataStream) {
57+
uint8_t port = pinNumber / 8;
58+
uint8_t portValue = 0;
59+
for (int i = 0; i < 8; ++i) {
60+
pin_size_t p = port * 8 + i;
61+
if (pinStates[p] == HIGH) {
62+
portValue |= (1 << i);
63+
}
64+
}
65+
firmataStream->write(DIGITAL_MESSAGE | port);
66+
firmataStream->write(portValue & 0x7F);
67+
firmataStream->write((portValue >> 7) & 0x7F);
68+
}
69+
}
70+
71+
PinStatus HardwareGPIO_FIRMATA::digitalRead(pin_size_t pinNumber) {
72+
// Request digital report for the port
73+
if (firmataStream) {
74+
uint8_t port = pinNumber / 8;
75+
firmataStream->write(REPORT_DIGITAL | port);
76+
firmataStream->write(1); // enable reporting
77+
}
78+
// Try to parse digital message from rxBuffer
79+
for (size_t i = 0; i + 2 < rxBuffer.size(); ++i) {
80+
uint8_t msg = rxBuffer[i];
81+
if ((msg & 0xF0) == DIGITAL_MESSAGE) {
82+
uint8_t port = msg & 0x0F;
83+
uint8_t lsb = rxBuffer[i+1];
84+
uint8_t msb = rxBuffer[i+2];
85+
uint16_t portValue = lsb | (msb << 7);
86+
pin_size_t p = port * 8;
87+
for (int j = 0; j < 8; ++j) {
88+
pinStates[p + j] = (portValue & (1 << j)) ? HIGH : LOW;
89+
}
90+
// Remove processed message
91+
rxBuffer.erase(rxBuffer.begin(), rxBuffer.begin() + i + 3);
92+
break;
93+
}
94+
}
95+
auto it = pinStates.find(pinNumber);
96+
if (it != pinStates.end()) {
97+
return it->second;
98+
}
99+
return LOW;
100+
}
101+
102+
int HardwareGPIO_FIRMATA::analogRead(pin_size_t pinNumber) {
103+
// Request analog report for the pin
104+
if (firmataStream) {
105+
firmataStream->write(REPORT_ANALOG | (pinNumber & 0x0F));
106+
firmataStream->write(1); // enable reporting
107+
}
108+
// Try to parse analog message from rxBuffer
109+
for (size_t i = 0; i + 2 < rxBuffer.size(); ++i) {
110+
uint8_t msg = rxBuffer[i];
111+
if ((msg & 0xF0) == ANALOG_MESSAGE) {
112+
uint8_t pin = msg & 0x0F;
113+
uint8_t lsb = rxBuffer[i+1];
114+
uint8_t msb = rxBuffer[i+2];
115+
int value = lsb | (msb << 7);
116+
analogValues[pin] = value;
117+
// Remove processed message
118+
rxBuffer.erase(rxBuffer.begin(), rxBuffer.begin() + i + 3);
119+
break;
120+
}
121+
}
122+
auto it = analogValues.find(pinNumber);
123+
if (it != analogValues.end()) {
124+
return it->second;
125+
}
126+
return 0;
127+
}
128+
129+
void HardwareGPIO_FIRMATA::analogReference(uint8_t mode) {
130+
// Not supported by Firmata, do nothing
131+
}
132+
133+
void HardwareGPIO_FIRMATA::analogWrite(pin_size_t pinNumber, int value) {
134+
if (firmataStream) {
135+
firmataStream->write(ANALOG_MESSAGE | (pinNumber & 0x0F));
136+
firmataStream->write(value & 0x7F);
137+
firmataStream->write((value >> 7) & 0x7F);
138+
}
139+
}
140+
141+
void HardwareGPIO_FIRMATA::analogWriteFrequency(pin_size_t pinNumber, uint32_t frequency) {
142+
// Not supported by Firmata, do nothing
143+
}
144+
145+
void HardwareGPIO_FIRMATA::tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
146+
// Not supported by Firmata, do nothing
147+
}
148+
149+
void HardwareGPIO_FIRMATA::noTone(uint8_t _pin) {
150+
// Not supported by Firmata, do nothing
151+
}
152+
153+
unsigned long HardwareGPIO_FIRMATA::pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) {
154+
// Not supported by Firmata, return 0
155+
return 0;
156+
}
157+
158+
unsigned long HardwareGPIO_FIRMATA::pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout) {
159+
// Not supported by Firmata, return 0
160+
return 0;
161+
}
162+
163+
void HardwareGPIO_FIRMATA::analogWriteResolution(uint8_t bits) {
164+
// Firmata supports 8-bit resolution, do nothing
165+
}
166+
167+
} // namespace arduino
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#pragma once
2+
/*
3+
HardwareGPIO_FIR.h
4+
Copyright (c) 2025 Phil Schatzmann. All right reserved.
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
This library is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
Lesser General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public
17+
License along with this library; if not, write to the Free Software
18+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
#ifdef USE_FIRMATA
21+
// Undefine DEPRECATED macro from libftdi1 to avoid conflict with Arduino API
22+
#ifdef DEPRECATED
23+
#undef DEPRECATED
24+
#endif
25+
#include "HardwareGPIO.h"
26+
#include <map>
27+
#include <thread>
28+
#include <atomic>
29+
#include <mutex>
30+
#include <chrono>
31+
32+
namespace arduino {
33+
34+
/**
35+
* @class HardwareGPIO_FIRMATA
36+
*/
37+
class HardwareGPIO_FIRMATA : public HardwareGPIO {
38+
public:
39+
/**
40+
* @brief Constructor for HardwareGPIO_FIRMATA.
41+
*/
42+
HardwareGPIO_FIRMA() = default;
43+
44+
/**
45+
* @brief Destructor for HardwareGPIO_FTDI.
46+
*/
47+
~HardwareGPIO_FIRMA();
48+
49+
/**
50+
* @brief Initialize the GPIO hardware interface for FTDI FT2232HL.
51+
* @param vendor_id USB vendor ID (default: 0x0403)
52+
* @param product_id USB product ID (default: 0x6010 for FT2232HL)
53+
* @param description Device description string (optional)
54+
* @param serial Device serial number (optional)
55+
* @return true if initialization successful, false otherwise
56+
*/
57+
bool begin(Stream &stream);
58+
59+
/**
60+
* @brief Close the FTDI connection and cleanup resources.
61+
*/
62+
void end();
63+
64+
/**
65+
* @brief Set the mode of a GPIO pin (INPUT, OUTPUT, etc).
66+
* @param pinNumber Pin number (0-15 for FT2232HL)
67+
* @param pinMode Pin mode (INPUT, OUTPUT, INPUT_PULLUP)
68+
*/
69+
void pinMode(pin_size_t pinNumber, PinMode pinMode) override;
70+
71+
/**
72+
* @brief Write a digital value to a GPIO pin.
73+
* @param pinNumber Pin number (0-15)
74+
* @param status Pin status (HIGH or LOW)
75+
*/
76+
void digitalWrite(pin_size_t pinNumber, PinStatus status) override;
77+
78+
/**
79+
* @brief Read a digital value from a GPIO pin.
80+
* @param pinNumber Pin number (0-15)
81+
* @return Pin status (HIGH or LOW)
82+
*/
83+
PinStatus digitalRead(pin_size_t pinNumber) override;
84+
85+
/**
86+
* @brief Read an analog value from a pin (not supported by FT2232HL).
87+
* @param pinNumber Pin number
88+
* @return Always returns 0 (no ADC on FT2232HL)
89+
*/
90+
int analogRead(pin_size_t pinNumber) override;
91+
92+
/**
93+
* @brief Set the analog reference mode (not supported by FT2232HL).
94+
* @param mode Reference mode (ignored)
95+
*/
96+
void analogReference(uint8_t mode) override;
97+
98+
/**
99+
* @brief Write an analog value (PWM) to a pin using software PWM.
100+
* @param pinNumber Pin number (0-15)
101+
* @param value PWM duty cycle (0-255, where 0=0% and 255=100%)
102+
*/
103+
void analogWrite(pin_size_t pinNumber, int value) override;
104+
105+
/**
106+
* @brief Set the PWM frequency for analogWrite() on a specific pin.
107+
* @param pinNumber Pin number (0-15)
108+
* @param frequency PWM frequency in Hz (default: 1000Hz)
109+
*/
110+
void analogWriteFrequency(pin_size_t pinNumber, uint32_t frequency);
111+
112+
/**
113+
* @brief Generate a tone on a pin (not supported by FT2232HL).
114+
* @param _pin Pin number
115+
* @param frequency Frequency in Hz (ignored)
116+
* @param duration Duration in ms (ignored)
117+
*/
118+
void tone(uint8_t _pin, unsigned int frequency,
119+
unsigned long duration = 0) override;
120+
121+
/**
122+
* @brief Stop tone generation on a pin (not supported by FT2232HL).
123+
* @param _pin Pin number (ignored)
124+
*/
125+
void noTone(uint8_t _pin) override;
126+
127+
/**
128+
* @brief Measure pulse duration on a pin
129+
* @param pin Pin number
130+
* @param state Pin state to measure
131+
* @param timeout Timeout in microseconds
132+
* @return Always returns 0 (not implemented)
133+
*/
134+
unsigned long pulseIn(uint8_t pin, uint8_t state,
135+
unsigned long timeout = 1000000L) override;
136+
137+
/**
138+
* @brief Measure long pulse duration on a pin
139+
* @param pin Pin number
140+
* @param state Pin state to measure
141+
* @param timeout Timeout in microseconds
142+
* @return Always returns 0 (not implemented)
143+
*/
144+
unsigned long pulseInLong(uint8_t pin, uint8_t state,
145+
unsigned long timeout = 1000000L) override;
146+
147+
/**
148+
* @brief Set the resolution for analogWrite() operations.
149+
* @param bits The resolution in bits (8-bit by default for FTDI)
150+
* @note FT2232HL supports 8-bit PWM resolution (0-255)
151+
*/
152+
void analogWriteResolution(uint8_t bits) override;
153+
154+
/**
155+
* @brief Boolean conversion operator.
156+
* @return true if the FTDI interface is open and initialized, false otherwise.
157+
*/
158+
operator bool() { return is_open && ftdi_context != nullptr; }
159+
160+
161+
protected:
162+
bool is_open = false;
163+
164+
};
165+
166+
} // namespace arduino
167+
168+
#endif // USE_FTDI

0 commit comments

Comments
 (0)