2021. november 30., kedd

USB-RS232 Interface


As implementation of my plan to continue to work on my measurement control solution, first I done the design of the USB-RS232 interface. It could be, that this is not the last version of it, but the design is done for now.

It is based on the CH340G chip what I used for my ESP8266 programmer with great success, also have it in various chinese USB-Serial cables, Arduinos, 3D printer boards and almost never had a single problem with it. To achieve the proper signal level I added an MAX232 to it.

Probably this solution is not optimal as it is lack of all of the RS232 signals, but I think it is good enough for now (I don't want to put much more effort into it for now).

The schematic:


The PCB design:

As you may realize from the schematic and the PCB, it has a "dual" USB connection. It means the USB signals are exists both of an micro USB connector and a pin header. This solution give the versatility to use the device with USB connector or soldered in cable.
According to this concept I designed two different enclosure.
With USB connector:


And with cable connectivity:


You can realize that the board color is gray on the exploded views above. The reason is that I wasn't able to import color 3D model to the OpenSCAD what I use for 3D modeling.
The original 3D view looks like this:


Currently I'm not planning to order the board yet because I want to panelize it with the next version of my GPIB hardware what I'm designing right now.

All of the design files you can find here:

Also as I planed reorganization of my measurement control things, all of the connecting repositories will appear here:


2021. november 27., szombat

Measurement Control - reboot

Preface

In the recent years I put substantial amount of work in controlling my various instruments from computer. The goal is to achieve some added values, like logging, better visibility, more functions, etc.
Looks like I a bit failed with this, as none of the things reached the usability state.
Even I put aside the thing months ago. Now I feel the need to restart the project and achieve some "working" result.

Plans

Reorganize the source repositories

This is needed as I learned many things in the recent years from my work on git (branching strategies, release management, etc.)

Create an USB-RS232 interface

I've a few devices using RS232 interface instead of (or on the side of) GPIB. Yes you can buy USB-Serial interfaces for nuts. Why you need another? Most of the things you can buy has no proper signal levels. They use TTL levels instead of RS232 levels. Most of the professional instruments require proper levels. The interfaces with proper signal levels are not cheap or you risk buying something on Aliexpress what states it correct, when not.

Redesign USB-GPIB hardware

My ATMEGA32U4 based interface working perfectly. In the recent years I realized a few things regarding to it, what feels like some adjustments are needed:
  • Create a physical design what can be mounted a 3D printed enclosure
  • Abandon the unnecessary connectors added for future improvements like display, additional device control, etc.
  • Add serial debugging capability (according to AR488)
  • Maintain bootloader compatibility with Arduino Leonardo
  • Also keep the advantage of using a complete 8bit bus of the MCU for data transfer (require modified bootloader
  • Maintain software compatibility with AR488 (https://github.com/Twilight-Logic/AR488)
  • Add activity LEDs (the original Arduino Leonardo has those)
  • Proper GPIB interfacing with TI chips (in a different HW version)
Maintaining AR488 compatibility doesn't mean I intend to drop the development of my own firmware. I still not a fan of the 3000+ lines of code in a single source file and the AR488 still doesn't support GPIB subaddressing. I've several instruments (VXI mainframes, power supply mainframe), what require subaddressing. On the other side it has device mode, TI GPIB chips support, what is missing from mine and support from software like sigrok.

Proper bootloader for USB-GPIB

I created a bootloader for the the v2.0 of my board. This was a result of an incident caused by reusing the USB activity LEDs of the Arduino Leonardo, killing my GPIB communication. The issue was resolved then, but the result is poorly documented (https://pakahuszar.blogspot.com/2020/04/gp-ib-5-v20-board.html), hard to reproduce and killing the activity LEDs instead of relocating them.
Now I'm trying to create a maintainable buildable, and properly installable solution. Also want to allocate a proper PID/VID from pid.codes

Power supply control software

My virtual instrument software is able to do many things but I realized it is too much for the first iteration.
There are many things already achieved with it, but for the reboot I want to have more realistic goal. So using the plugable driver support from it, I want to just create a power supply control software for my many supplies (even the HP 66000A mainframe is useless without software control as I don't have keyboard for it). I don't want to add higher functions yet.

Further...

Sure, I don't want to stop here, I want to reboot the virtual instrument software, my curve tracer, etc.
But those are cloudy right now, and if I lose my focus on it, this project tsunami will be shelved. Again.

2021. november 11., csütörtök

Terraform, Cloudformation and Cognito

Some things what more of my professional side than the hobby, electronics, maker things.

I'm working with IaC tools since... don't even remember. I started with AWS CloudFormation around 2014 (not sure, it is cloudy. 😆).


When I learned about Terraform, I had a fear, that I'll not get the full functionality of AWS. The CloudFormation is a native tool. Something developed by a 3rd party should fall behind the native tool of the provider.

I started using Terraform as the customer required it. So switching from Cloudformation to Terraform wasn't my decision. The fear I described above just become reality when the customer didn't wanted to upgrade the AWS provider in their deploy agents, so it wasn't the fault of HashiCorp or the Terraform community. Also, writing Terraform code is much more convenient, give you more capabilities on processing, converting the required information.

A few weeks ago, after three years exclusive Terraform work, I got a project what use CloudFormation again.

There was a - I guessed - super easy task. Publish the secret of a Cognito User Pool Client into an SSM Parameter Store value as Secret String.

In Terraform this would look like this:

// Create Cognito User Pool
resource "aws_cognito_user_pool" "pool" {
  name = "pool"
}

// Create Cognito User Pool Client
resource "aws_cognito_user_pool_client" "client" {
  name = "client"
  user_pool_id = aws_cognito_user_pool.pool.id
  generate_secret     = true
}

// Publish to SSM Parameter Store
resource "aws_ssm_parameter" "secret" {
  name        = "/production/cognito/clientSecret"
  type        = "SecureString"
  value       = ${aws_cognito_user_pool_client.client.client_secret}
}

And we are done.

Try the same in CloudFormation.

Can the AWS::Cognito::UserPoolClient object export the secret? Nope. Ref: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpoolclient.html

Can the AWS::SSM::Parameter object create SecureString? Nope. Ref: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-parameter.html

Here my previous theory on the "CloudFormation should be ahead of Terraform" fall.

I tried several things finally this was working (One small comment: the import cfnresponse is in a separate line. It is not because of my lazyness. It is because otherwise the infrastructure doesn't realize, the cfnresponse.py should be added to the zip as it is not a part of the pip repo):

Resources:
  # Create Cognito User Pool
  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: pool

  # Create Cognito User Pool Client
  UserPoolClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      AccessTokenValidity: 15
      AllowedOAuthFlowsUserPoolClient: false
      ClientName: client
      GenerateSecret: true

  # You need a lambda to
  #     - read from the cognito user pool client
  #     - write to the parameter store
  #     - report back to the CloudFormation when finished
  CognitoSecretExporterLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: CognitoSecretExporter
      Runtime: python3.9
      Role: !GetAtt CognitoSecretExporterExecutionRole.Arn
      Handler: index.handler
      Timeout: 30
      Environment:
        Variables:
          CognitoUserPoolId: !Ref UserPool
          CognitoUserPoolClientId: !Ref UserPoolClient
          ssmParameterName: /production/cognito/ClientSecret
      Code:
        ZipFile: |
          import boto3, os
          import cfnresponse

          def handler(event, context):
              CognitoUserPoolId = os.environ['CognitoUserPoolId']
              CognitoUserPoolClientId = os.environ['CognitoUserPoolClientId']
              ssmParameterName = os.environ['ssmParameterName']

              # Read Cognito
              cognito = boto3.client('cognito-idp')
              response = cognito.describe_user_pool_client(
                  UserPoolId = CognitoUserPoolId,
                  ClientId = CognitoUserPoolClientId
              )
              cognitoClientSecret = response['UserPoolClient']['ClientSecret']
              print(cognitoClientSecret)
              ssm = boto3.client('ssm')

              # Write to parameter store
              response = ssm.put_parameter(
                  Name = ssmParameterName,
                  Description = '[CF] Cognito Client Secret used by the WebApp',
                  Value = cognitoClientSecret,
                  Type = 'SecureString',
                  KeyId = 'alias/aws/ssm',
                  Overwrite = True,
                  Tier='Standard'
              )

              # Report success to CloudFormation
              responseData = {}
              responseData['Data'] = 120
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) 

  # Trigger the lambda from the CloudFormation stack
  CognitoSecretExporterInvoke:
    Type: AWS::CloudFormation::CustomResource
    DependsOn: CognitoSecretExporterLambda
    Version: "1.0"
    Properties:
      ServiceToken: !GetAtt CognitoSecretExporterLambda.Arn 

  # IAM Role for the lambda above
  CognitoSecretExporterExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: CognitoSecretExporterExecutionRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              - lambda.amazonaws.com
          Action:
            - sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: root
      PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Sid: EnablePutLogEvents
              Effect: Allow
              Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource:
                - '*'
            - Sid: ReadCognito
              Effect: Allow
              Action:
                - cognito-idp:DescribeUserPoolClient
              Resource:
                - !GetAtt UserPool.Arn
            - Sid: ParamStore
              Effect: Allow
              Action:
                - ssm:DeleteParameter
                - ssm:PutParameter
                - ssm:GetParameter
              Resource: 
                - !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/production/cognito/ClientSecret