add Arduino LED and Wi-Fi SDKs
Adds two esp32 example projects using the espressif/arduino-esp32 library to demonstrate Wi-Fi scanning and a simple LED blink example
I've also mentioned this in the READMEs: note that these examples require ESP-IDF v5.3.2 since the Arduino releases are paired with specific IDF versions
Resolves #42
Blink works but I’ve been unable to get WiFi to work. I’m using a seeed studio C6, so perhaps there are some differences.
Blink works but I’ve been unable to get WiFi to work. I’m using a seeed studio C6, so perhaps there are some differences.
Hmm, what errors are you getting? Is it compiling but just not outputting anything?
The diff looks good, thanks!
Could I ask for a new GH workflow to be added so that the code is built in CI? (Just building is good enough, no need to try to run it.)
I've just written the actions now but there's a dependency issue:
- Swift 6.0.0+ depends on GLIBC_2.38
- The latest arduino-esp32 release depends on IDF v5.3
- The espressif/idf:v5.3.2 docker image is based on Ubuntu 22.04
- Ubuntu 22.04 bundles GLIBC_2.35
Some options:
- Use the latest arduino-esp32 pre-release which is based on IDF v5.4 and update the Arduino version when the stable release comes out
- Manually build the latest version of GLIBC in the action (although this would cause the action to take a lot longer)
- Don't use the
espressif/idfdocker image and manually install esp-idf in the action (similar to above) - Create a custom docker image with IDF v5.3 and ubuntu 24.04
Not sure what you think is best, but I've done (1) which compiles successfully (example run)
Blink works but I’ve been unable to get WiFi to work. I’m using a seeed studio C6, so perhaps there are some differences.
Hmm, what errors are you getting? Is it compiling but just not outputting anything?
I didn't realize you were using the hardware serial 😆 also fun is that Arduino's String collides with swift's string.
I sorta fixed that with this:
public typealias ArduinoString = String
extension ArduinoString: CustomStringConvertible {
public var description: Swift.String {
return Swift.String(cString: self.c_str())
}
}
extension ArduinoString: Equatable {
public static func == (lhs: ArduinoString, rhs: ArduinoString) -> Bool {
lhs.equals(rhs)
}
public static func == <RHS: StringProtocol>(lhs: ArduinoString, rhs: RHS) -> Bool {
lhs.equals(String(Swift.String(rhs)))
}
public static func == <LHS: StringProtocol>(lhs: LHS, rhs: ArduinoString) -> Bool {
rhs.equals(String(Swift.String(lhs)))
}
}
extension ArduinoString: Comparable {
public static func < (lhs: ArduinoString, rhs: ArduinoString) -> Bool {
lhs.compareTo(rhs) < 0
}
public static func < <RHS: StringProtocol>(lhs: ArduinoString, rhs: RHS) -> Bool {
lhs.compareTo(String(Swift.String(rhs))) < 0
}
public static func < <LHS: StringProtocol>(lhs: LHS, rhs: ArduinoString) -> Bool {
lhs < rhs.description
}
}
extension ArduinoString: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(description)
}
}
extension ArduinoString: ExpressibleByStringLiteral {
public init(stringLiteral value: Swift.String) {
let cString = Array(value.utf8CString)
self = ArduinoString(cString)
}
}
// MARK: - Operators (+, +=)
extension ArduinoString {
public static func + (lhs: ArduinoString, rhs: ArduinoString) -> ArduinoString {
var newString = lhs
_ = newString.concat(rhs)
return newString
}
/// ArduinoString + SomeString (e.g. String, Substring, etc.)
public static func + <RHS: StringProtocol>(lhs: ArduinoString, rhs: RHS) -> ArduinoString {
ArduinoString(lhs.description + Swift.String(rhs))
}
/// SomeString + ArduinoString
public static func + <LHS: StringProtocol>(lhs: LHS, rhs: ArduinoString) -> ArduinoString {
ArduinoString(Swift.String(lhs) + rhs.description)
}
/// ArduinoString += ArduinoString
public static func += (lhs: inout ArduinoString, rhs: ArduinoString) {
lhs = lhs + rhs
}
/// ArduinoString += SomeString
public static func += <RHS: StringProtocol>(lhs: inout ArduinoString, rhs: RHS) {
lhs = lhs + rhs
}
}
extension Swift.String {
public static func += (lhs: inout Swift.String, rhs: ArduinoString) {
lhs.append(contentsOf: rhs.description)
}
}
extension ArduinoString {
// Access the underlying Swift.String's StringProtocol properties
public var utf8: Swift.String.UTF8View { Swift.String(cString: self.c_str()).utf8 }
public var utf16: Swift.String.UTF16View { Swift.String(cString: self.c_str()).utf16 }
public var unicodeScalars: Swift.String.UnicodeScalarView { Swift.String(cString: self.c_str()).unicodeScalars }
// Common StringProtocol methods
public func hasPrefix(_ prefix: Swift.String) -> Bool {
return Swift.String(cString: self.c_str()).hasPrefix(prefix)
}
public func hasSuffix(_ suffix: Swift.String) -> Bool {
return Swift.String(cString: self.c_str()).hasSuffix(suffix)
}
public func contains(_ other: Swift.Character) -> Bool {
return Swift.String(cString: self.c_str()).contains(other)
}
// Index-based access
public subscript(position: Swift.String.Index) -> Character {
return Swift.String(cString: self.c_str())[position]
}
public subscript(bounds: Range<Swift.String.Index>) -> Swift.Substring {
return Swift.String(cString: self.c_str())[bounds]
}
public var startIndex: Swift.String.Index {
return Swift.String(cString: self.c_str()).startIndex
}
public var endIndex: Swift.String.Index {
return Swift.String(cString: self.c_str()).endIndex
}
public func index(after i: Swift.String.Index) -> Swift.String.Index {
return Swift.String(cString: self.c_str()).index(after: i)
}
public func index(before i: Swift.String.Index) -> Swift.String.Index {
return Swift.String(cString: self.c_str()).index(before: i)
}
}
it's not quite StringProtocol conformance, but does allow for the example to use the usb monitor.
That's very neat - I was using Swift 6.0.3 not 6.1 so didn't realise print(_:separator:terminator:) was now available on embedded; do you want me to add you as a collaborator on my fork so you can commit that?
I had to slightly modify that CustomStringConvertible implementation since String#c_str isn't available without an annotation in the C++ library but that compiles and works quite nicely (run)
Can you elaborate on why we need the ArduinoString extensions at all? I.e. where are those extensions used from? I don't immediately see that in the code.
Yes they're not used for anything in the example; as @eric-humane suggested it brings ArduinoString closer to conforming to StringProtocol but I guess you could just convert them to Swift.String and use the existing functionality.
Do you think it would be neater to not include them to keep the example more minimal? The only one that's actually needed is the CustomStringConvertible extension.
Yes they're not used for anything in the example; as @eric-humane suggested it brings
ArduinoStringcloser to conforming toStringProtocolbut I guess you could just convert them toSwift.Stringand use the existing functionality.Do you think it would be neater to not include them to keep the example more minimal? The only one that's actually needed is the
CustomStringConvertibleextension.
That’s a common misconception about CustomStringConvertible actually. String concat won’t work etc. the other issue is it’s sort of “invisible” and hard to understand why it’s occurring. The better thing to do would be to use SWIFT_NAME, probably. But then you’re still having to convert these to swift strings for swift functions and it’s not really clear when or why you have to do that.
option: conform to string protocol optimal option: swift rename the arduino string class Most optimal option (?): Swift.String supports most of the arduino api string api (it’s much smaller than the option one) and strip out the arduino string class.
Either way, it’s a tough problem and really hard to debug. Even writing wrappers you run into this a bit.
Edit: I’m also not suggesting adding my code above. But just demonstrating how I fixed the problem.
Edit 2: or make arduino lib a proper module (import Arduino), probably best. But I was just going for quick and dirty here. I haven’t spent much time reading all the docs on this stuff here, just happened to have a C6 laying around.
That’s a common misconception about CustomStringConvertible actually. String concat won’t work etc. the other issue is it’s sort of “invisible” and hard to understand why it’s occurring. The better thing to do would be to use SWIFT_NAME, probably. But then you’re still having to convert these to swift strings for swift functions and it’s not really clear when or why you have to do that.
Interesting - I've only used string formatting in the example which works fine without the other extensions. I can see why WiFi.RSSI(i) + "dBm" might not work for example, but wouldn't Swift.String(WiFi.RSSI(i)) + "dBm", "\(WiFi.RSSI(i))dBm" and "\(WiFi.RSSI(i))" + "dBm" still work?
Edit: I’m also not suggesting adding my code above.
Sorry, should have probably waited for your reply. I'll strip out the extensions then
That’s a common misconception about CustomStringConvertible actually. String concat won’t work etc. the other issue is it’s sort of “invisible” and hard to understand why it’s occurring. The better thing to do would be to use SWIFT_NAME, probably. But then you’re still having to convert these to swift strings for swift functions and it’s not really clear when or why you have to do that.
Interesting - I've only used string formatting in the example which works fine without the other extensions. I can see why
WiFi.RSSI(i) + "dBm"might not work for example, but wouldn'tSwift.String(WiFi.RSSI(i)) + "dBm","\(WiFi.RSSI(i))dBm"and"\(WiFi.RSSI(i))" + "dBm"still work?Edit: I’m also not suggesting adding my code above.
Sorry, should have probably waited for your reply. I'll strip out the extensions then
it would, it just sucks to have to do that 😆 that said if we keep this really concise i.e. no extra extensions etc, we could land it
I just realised there was a full arduino-esp32 release 2 weeks ago based on ESP-IDF v5.4 so I've rebased and updated it to use that version
Hi, just wondering if there were any updates on this?
Do you mind rebasing this so we can more easily review it?
FWIW my main concern here is maintaining it. An example like this might be better in a repo we link to in the community examples section.
FWIW my main concern here is maintaining it. An example like this might be better in a repo we link to in the community examples section.
Fair enough, it's up to you; I'm happy to move this to it's own repo if you think that would be better?
Hi @rauhul, I've renamed and stripped the other examples from my fork (maartin0/arduino-swift-esp32-examples) so it's more or less a standalone repository now. Not sure if you think it's useful to link in the community examples?