Skip to content

Commit 37ee89a

Browse files
committed
Make AddressChanger interactive
1 parent d0c12de commit 37ee89a

File tree

1 file changed

+256
-58
lines changed

1 file changed

+256
-58
lines changed

examples/Utilities/AddressChanger/AddressChanger.ino

+256-58
Original file line numberDiff line numberDiff line change
@@ -6,62 +6,260 @@
66
* SPDX-License-Identifier: MPL-2.0
77
*/
88

9-
#include "Wire.h"
9+
#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;
13-
14-
uint8_t address;
15-
16-
void setup() {
17-
Wire1.begin();
18-
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);
23-
}
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");
33-
}
34-
}
35-
}
36-
37-
String pinstrapToName(uint8_t pinstrap) {
38-
switch (pinstrap) {
39-
case 0x3C:
40-
return "BUZZER";
41-
case 0x7C:
42-
return "BUTTONS";
43-
case 0x76:
44-
case 0x74:
45-
return "ENCODER";
46-
case 0x6C:
47-
return "SMARTLEDS";
48-
}
49-
return "UNKNOWN";
50-
}
51-
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-
}
66-
}
67-
}
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;
21+
22+
23+
void setup() {
24+
Wire1.begin();
25+
Serial.begin(115200);
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+
}
107+
else {
108+
Serial.println(" error\n");
109+
return false;
110+
}
111+
}
112+
}
113+
114+
// Function to parse hex number (with or without 0x prefix)
115+
int parseHex(String hexStr) {
116+
hexStr.trim();
117+
118+
if (hexStr.length() == 0) {
119+
return -1;
120+
}
121+
122+
if (hexStr.startsWith("0x") || hexStr.startsWith("0X")) {
123+
hexStr = hexStr.substring(2); // Remove the "0x" prefix
124+
}
125+
126+
// Validate that the remaining string contains only valid hexadecimal characters (0-9, A-F, a-f)
127+
for (int i = 0; i < hexStr.length(); i++) {
128+
if (!isHexDigit(hexStr.charAt(i))) {
129+
return -1;
130+
}
131+
}
132+
133+
return strtol(hexStr.c_str(), NULL, 16);
134+
}
135+
136+
bool isHexDigit(char c) {
137+
return ( (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') );
138+
}
139+
140+
void discoverDevices() {
141+
char buffer[64];
142+
Serial.println("ADDR\tMODULINO\tPINSTRAP\tDEFAULT ADDR"); // Table heading.
143+
144+
numRows = 0;
145+
146+
// Discover all modulino devices connected to the I2C bus.
147+
for (int addr = 8; addr < 128; addr++) {
148+
Wire1.beginTransmission(addr);
149+
if (Wire1.endTransmission() != 0) continue;
150+
151+
if (numRows >= MAX_DEVICES) {
152+
Serial.println("Too many devices connected, maximum supported is" + String(MAX_DEVICES));
153+
return;
154+
}
155+
156+
// Some addresses represent non configurable devices (no MCU on it). Handle them as a special case.
157+
if (isFixedAddrDevice(addr)) {
158+
snprintf(buffer, 64, "0x%02X (cannot change)", addr);
159+
addRow(addr, fixedAddrToName(addr), "-", String(buffer));
160+
161+
continue; // Stop here, do not try to communicate with this device.
162+
}
163+
164+
{
165+
uint8_t pinstrap = 0; // Variable to store the pinstrap (device type)
166+
Wire1.beginTransmission(addr); // Begin I2C transmission to the current address
167+
Wire1.write(0x00); // Send a request to the device (assuming 0x00 is the register for device type)
168+
Wire1.endTransmission(); // End transmission
169+
170+
delay(50); // Delay to allow for the device to respond
171+
172+
Wire1.requestFrom(addr, 1); // Request 1 byte from the device at the current address
173+
if (Wire1.available()) {
174+
pinstrap = Wire1.read(); // Read the device type (pinstrap)
175+
} else {
176+
// If an error happens in the range 0x78 to 0x7F, ignore it.
177+
if (addr >= 0x78) continue;
178+
Serial.println("Failed to read device type at address 0x" + String(addr, HEX));
179+
}
180+
181+
snprintf(buffer, 64, "0x%02X", pinstrap);
182+
auto hexPinstrap = String(buffer);
183+
184+
snprintf(buffer, 64, "0x%02X", pinstrap / 2); // Default address is half pinstrap.
185+
auto defaultAddr = String(buffer);
186+
if (addr != pinstrap / 2) defaultAddr += " *"; // Mark devices with modified address.
187+
188+
addRow(addr, pinstrapToName(pinstrap), hexPinstrap, defaultAddr);
189+
}
190+
}
191+
192+
// Print the results.
193+
for (int i = 0; i < numRows; i++) {
194+
char buffer[16];
195+
snprintf(buffer, 16, "0x%02X", rows[i].addr);
196+
197+
Serial.print(fixedWidth(buffer, 8));
198+
Serial.print(fixedWidth(rows[i].modulinoType, 16));
199+
Serial.print(fixedWidth(rows[i].pinstrap, 16));
200+
Serial.println(fixedWidth(rows[i].defaultAddr, 12));
201+
}
202+
}
203+
204+
void addRow(uint8_t address, String modulinoType, String pinstrap, String defaultAddr) {
205+
if (numRows >= MAX_DEVICES) return;
206+
207+
rows[numRows].addr = address;
208+
rows[numRows].modulinoType = modulinoType;
209+
rows[numRows].pinstrap = pinstrap;
210+
rows[numRows].defaultAddr = defaultAddr;
211+
numRows++; // Increment the row counter
212+
}
213+
214+
bool findRow(uint8_t address) {
215+
for (int i = 0; i < numRows; i++) {
216+
if (rows[i].addr == address) return true;
217+
}
218+
return false;
219+
}
220+
221+
222+
// Function to add padding to the right to ensure each field has a fixed width
223+
String fixedWidth(String str, int width) {
224+
for (int i = str.length(); i < width; i++) str += ' ';
225+
return str;
226+
}
227+
228+
String pinstrapToName(uint8_t pinstrap) {
229+
switch (pinstrap) {
230+
case 0x3C:
231+
return "Buzzer";
232+
case 0x7C:
233+
return "Buttons";
234+
case 0x76:
235+
case 0x74:
236+
return "Encoder";
237+
case 0x6C:
238+
return "Smartleds";
239+
}
240+
return "UNKNOWN";
241+
}
242+
243+
String fixedAddrToName(uint8_t address) {
244+
switch (address) {
245+
case 0x29:
246+
return "Distance";
247+
case 0x44:
248+
return "Thermo";
249+
case 0x6A:
250+
case 0x6B:
251+
return "Movement";
252+
}
253+
return "UNKNOWN";
254+
}
255+
256+
bool isFixedAddrDevice(uint8_t addr) {
257+
// List of non-configurable devices, recognized by their fixed I2C address.
258+
const uint8_t fixedAddr[] = {0x29, 0x44, 0x6A, 0x6B};
259+
260+
for (int i = 0; i < sizeof(fixedAddr) / sizeof(fixedAddr[0]); i++) {
261+
if (addr == fixedAddr[i]) return true;
262+
}
263+
return false;
264+
}
265+

0 commit comments

Comments
 (0)