Skip to content

add Arduino LED and Wi-Fi SDKs #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

maartin0
Copy link

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

@ericlewis
Copy link

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.

@maartin0
Copy link
Author

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?

@kubamracek
Copy link
Collaborator

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.)

@maartin0
Copy link
Author

I've just written the actions now but there's a dependency issue:

Some options:

  1. 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
  2. Manually build the latest version of GLIBC in the action (although this would cause the action to take a lot longer)
  3. Don't use the espressif/idf docker image and manually install esp-idf in the action (similar to above)
  4. Create a custom docker image with IDF v5.3 and ubuntu 24.04

@maartin0
Copy link
Author

Not sure what you think is best, but I've done (1) which compiles successfully (example run)

@eric-humane
Copy link

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.

@maartin0
Copy link
Author

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?

@maartin0 maartin0 marked this pull request as draft March 25, 2025 13:33
@maartin0 maartin0 force-pushed the arduino-pr branch 3 times, most recently from 68bf7ae to 5582710 Compare March 26, 2025 21:46
@maartin0
Copy link
Author

maartin0 commented Mar 26, 2025

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)

@maartin0 maartin0 marked this pull request as ready for review March 26, 2025 21:55
@kubamracek
Copy link
Collaborator

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.

@maartin0
Copy link
Author

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.

@ericlewis
Copy link

ericlewis commented Mar 31, 2025

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.

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.

@maartin0
Copy link
Author

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

@eric-humane
Copy link

eric-humane commented Apr 1, 2025

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

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

@maartin0 maartin0 force-pushed the arduino-pr branch 2 times, most recently from 3daba1d to 1355e7d Compare April 11, 2025 10:53
@maartin0
Copy link
Author

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

Adds two esp32 example projects using the espressif/arduino-esp32 library to demonstrate Wi-Fi scanning and a simple LED blink example
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for Arduino libraries?
4 participants