Skip to content

Commit 409e6b3

Browse files
Xaytonfacchinm
authored andcommitted
Make AddressChanger interactive
1 parent d0c12de commit 409e6b3

File tree

1 file changed

+230
-34
lines changed

1 file changed

+230
-34
lines changed

examples/Utilities/AddressChanger/AddressChanger.ino

+230-34
Original file line numberDiff line numberDiff line change
@@ -8,60 +8,256 @@
88

99
#include "Wire.h"
1010

11-
// Setting new_address to 0 means that the module will get back its original address
12-
const uint8_t new_address = 0;
11+
struct DetectedModulino {
12+
uint8_t addr;
13+
String modulinoType;
14+
String pinstrap;
15+
String defaultAddr;
16+
};
17+
18+
#define MAX_DEVICES 16
19+
DetectedModulino rows[MAX_DEVICES];
20+
int numRows = 0;
1321

14-
uint8_t address;
1522

1623
void setup() {
1724
Wire1.begin();
1825
Serial.begin(115200);
19-
delay(1000);
20-
if (new_address != 0 && (new_address < 8 || new_address > 0x77)) {
21-
Serial.println("Address outside valid range");
22-
while (1);
26+
27+
delay(600);
28+
discoverDevices();
29+
}
30+
31+
bool waitingInput = false;
32+
void loop() {
33+
if (numRows == 0) return;
34+
if (Serial.available() == 0 && waitingInput) return;
35+
36+
if (Serial.available() > 0) {
37+
String hex1 = Serial.readStringUntil(' '); // Read until space (or other delimiter)
38+
String hex2 = Serial.readStringUntil('\n'); // Read until newline
39+
Serial.println("> " + hex1 + " " + hex2); // Print what the user inserted.
40+
41+
int num1 = parseHex(hex1); // Parse the first hex number
42+
int num2 = parseHex(hex2); // Parse the second hex number
43+
if (num1 == -1 || num2 == -1) {
44+
Serial.println("Error: Incomplete or invalid input. Please enter two hexadecimal numbers");
45+
return;
46+
}
47+
48+
bool success = updateI2cAddress(num1, num2);
49+
if (!success) return; // If the update failed, skip discovery and messages, and wait for input again.
50+
51+
discoverDevices();
52+
waitingInput = false;
53+
}
54+
55+
Serial.println("Enter the current address, space, and new address (ex. \"0x20 0x30\" or \"20 2A\"):");
56+
Serial.println(" - Enter \"<addr> 0\" to reset the device at <addr> to its default address.");
57+
Serial.println(" - Enter \"0 0\" to reset all devices to the default address.");
58+
waitingInput = true;
59+
}
60+
61+
// Updates the device at current address to new address. Supports broadcasting and setting default address (0).
62+
// Returns true if the update was successful, false otherwise.
63+
bool updateI2cAddress(int curAddress, int newAddress) {
64+
uint8_t data[40] = { 'C', 'F', newAddress * 2 };
65+
memset(data + 3, 0, sizeof(data) - 3); // Zero the rest of the buffer.
66+
67+
// Validate the current address, it must match a detected device.
68+
if (curAddress != 0 && !findRow(curAddress)) {
69+
Serial.println("Error: current address 0x" + String(curAddress, HEX) + " not found in the devices list\n");
70+
return false;
71+
}
72+
73+
if (curAddress != 0 && isFixedAddrDevice(curAddress)) {
74+
Serial.println("Error: address 0x" + String(curAddress, HEX) + " is a non configurable device\n");
75+
return false;
76+
}
77+
78+
// Validate the new address.
79+
if (newAddress != 0 && (newAddress < 8 || newAddress > 0x77)) {
80+
Serial.println("Error: new address 0x" + String(newAddress, HEX) + " must be from 0x08 to 0x77\n");
81+
return false;
82+
}
83+
84+
if (curAddress == 0) {
85+
Serial.print("Updating all devices (broadcast 0x00) to 0x" + String(newAddress, HEX));
86+
} else {
87+
Serial.print("Updating the device address from 0x" + String(curAddress, HEX) + " to 0x" + String(newAddress, HEX));
88+
}
89+
if (newAddress == 0) Serial.print(" (default address)");
90+
Serial.print("...");
91+
92+
Wire1.beginTransmission(curAddress);
93+
Wire1.write(data, 40);
94+
Wire1.endTransmission();
95+
96+
delay(500);
97+
98+
if (newAddress == 0) {
99+
Serial.println(" done\n");
100+
return true;
101+
} else {
102+
Wire1.requestFrom(newAddress, 1);
103+
if (Wire1.available()) {
104+
Serial.println(" done\n");
105+
return true;
106+
} else {
107+
Serial.println(" error\n");
108+
return false;
109+
}
110+
}
111+
}
112+
113+
// Function to parse hex number (with or without 0x prefix)
114+
int parseHex(String hexStr) {
115+
hexStr.trim();
116+
117+
if (hexStr.length() == 0) {
118+
return -1;
119+
}
120+
121+
if (hexStr.startsWith("0x") || hexStr.startsWith("0X")) {
122+
hexStr = hexStr.substring(2); // Remove the "0x" prefix
23123
}
24-
// Search for devices and wait for user confirmation
25-
for (int i = 8; i < 128; i++) {
26-
Wire1.beginTransmission(i);
27-
auto err = Wire1.endTransmission();
28-
if (err == 0) {
29-
Serial.print("Found device at ");
30-
Serial.println(i);
31-
address = i;
32-
Serial.println("Press 'c' to configure te new address");
124+
125+
// Validate that the remaining string contains only valid hexadecimal characters (0-9, A-F, a-f)
126+
for (int i = 0; i < hexStr.length(); i++) {
127+
if (!isHexDigit(hexStr.charAt(i))) {
128+
return -1;
129+
}
130+
}
131+
132+
return strtol(hexStr.c_str(), NULL, 16);
133+
}
134+
135+
bool isHexDigit(char c) {
136+
return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'));
137+
}
138+
139+
void discoverDevices() {
140+
char buffer[64];
141+
Serial.println("ADDR\tMODULINO\tPINSTRAP\tDEFAULT ADDR"); // Table heading.
142+
143+
numRows = 0;
144+
145+
// Discover all modulino devices connected to the I2C bus.
146+
for (int addr = 8; addr < 128; addr++) {
147+
Wire1.beginTransmission(addr);
148+
if (Wire1.endTransmission() != 0) continue;
149+
150+
if (numRows >= MAX_DEVICES) {
151+
Serial.println("Too many devices connected, maximum supported is" + String(MAX_DEVICES));
152+
return;
153+
}
154+
155+
// Some addresses represent non configurable devices (no MCU on it). Handle them as a special case.
156+
if (isFixedAddrDevice(addr)) {
157+
snprintf(buffer, 64, "0x%02X (cannot change)", addr);
158+
addRow(addr, fixedAddrToName(addr), "-", String(buffer));
159+
160+
continue; // Stop here, do not try to communicate with this device.
33161
}
162+
163+
{
164+
uint8_t pinstrap = 0; // Variable to store the pinstrap (device type)
165+
Wire1.beginTransmission(addr); // Begin I2C transmission to the current address
166+
Wire1.write(0x00); // Send a request to the device (assuming 0x00 is the register for device type)
167+
Wire1.endTransmission(); // End transmission
168+
169+
delay(50); // Delay to allow for the device to respond
170+
171+
Wire1.requestFrom(addr, 1); // Request 1 byte from the device at the current address
172+
if (Wire1.available()) {
173+
pinstrap = Wire1.read(); // Read the device type (pinstrap)
174+
} else {
175+
// If an error happens in the range 0x78 to 0x7F, ignore it.
176+
if (addr >= 0x78) continue;
177+
Serial.println("Failed to read device type at address 0x" + String(addr, HEX));
178+
}
179+
180+
snprintf(buffer, 64, "0x%02X", pinstrap);
181+
auto hexPinstrap = String(buffer);
182+
183+
snprintf(buffer, 64, "0x%02X", pinstrap / 2); // Default address is half pinstrap.
184+
auto defaultAddr = String(buffer);
185+
if (addr != pinstrap / 2) defaultAddr += " *"; // Mark devices with modified address.
186+
187+
addRow(addr, pinstrapToName(pinstrap), hexPinstrap, defaultAddr);
188+
}
189+
}
190+
191+
// Print the results.
192+
for (int i = 0; i < numRows; i++) {
193+
char buffer[16];
194+
snprintf(buffer, 16, "0x%02X", rows[i].addr);
195+
196+
Serial.print(fixedWidth(buffer, 8));
197+
Serial.print(fixedWidth(rows[i].modulinoType, 16));
198+
Serial.print(fixedWidth(rows[i].pinstrap, 16));
199+
Serial.println(fixedWidth(rows[i].defaultAddr, 12));
34200
}
35201
}
36202

203+
void addRow(uint8_t address, String modulinoType, String pinstrap, String defaultAddr) {
204+
if (numRows >= MAX_DEVICES) return;
205+
206+
rows[numRows].addr = address;
207+
rows[numRows].modulinoType = modulinoType;
208+
rows[numRows].pinstrap = pinstrap;
209+
rows[numRows].defaultAddr = defaultAddr;
210+
numRows++; // Increment the row counter
211+
}
212+
213+
bool findRow(uint8_t address) {
214+
for (int i = 0; i < numRows; i++) {
215+
if (rows[i].addr == address) return true;
216+
}
217+
return false;
218+
}
219+
220+
221+
// Function to add padding to the right to ensure each field has a fixed width
222+
String fixedWidth(String str, int width) {
223+
for (int i = str.length(); i < width; i++) str += ' ';
224+
return str;
225+
}
226+
37227
String pinstrapToName(uint8_t pinstrap) {
38228
switch (pinstrap) {
39229
case 0x3C:
40-
return "BUZZER";
230+
return "Buzzer";
41231
case 0x7C:
42-
return "BUTTONS";
232+
return "Buttons";
43233
case 0x76:
44234
case 0x74:
45-
return "ENCODER";
235+
return "Encoder";
46236
case 0x6C:
47-
return "SMARTLEDS";
237+
return "Smartleds";
48238
}
49239
return "UNKNOWN";
50240
}
51241

52-
void loop() {
53-
// put your main code here, to run repeatedly:
54-
if (Serial.available()) {
55-
if (Serial.read() == 'c') {
56-
Serial.print("Assigning new address to ");
57-
Serial.println(address);
58-
uint8_t data[40] = { 'C', 'F', new_address * 2 };
59-
Wire1.beginTransmission(address);
60-
Wire1.write(data, 40);
61-
Wire1.endTransmission();
62-
delay(1000);
63-
Wire1.requestFrom(new_address, 1);
64-
Serial.println("Device type " + pinstrapToName(Wire1.read()) + " at new address " + String(new_address));
65-
}
242+
String fixedAddrToName(uint8_t address) {
243+
switch (address) {
244+
case 0x29:
245+
return "Distance";
246+
case 0x44:
247+
return "Thermo";
248+
case 0x6A:
249+
case 0x6B:
250+
return "Movement";
251+
}
252+
return "UNKNOWN";
253+
}
254+
255+
bool isFixedAddrDevice(uint8_t addr) {
256+
// List of non-configurable devices, recognized by their fixed I2C address.
257+
const uint8_t fixedAddr[] = { 0x29, 0x44, 0x6A, 0x6B };
258+
259+
for (int i = 0; i < sizeof(fixedAddr) / sizeof(fixedAddr[0]); i++) {
260+
if (addr == fixedAddr[i]) return true;
66261
}
262+
return false;
67263
}

0 commit comments

Comments
 (0)