<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tbphp Precision Tech</title>
    <description>The latest articles on DEV Community by tbphp Precision Tech (@tbphp_tech).</description>
    <link>https://dev.to/tbphp_tech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4017874%2F0446d997-327b-4e5a-ba6c-7f6a98180f37.png</url>
      <title>DEV Community: tbphp Precision Tech</title>
      <link>https://dev.to/tbphp_tech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tbphp_tech"/>
    <language>en</language>
    <item>
      <title>How to Build an OEM Certificate Expiry Monitor for Microneedling Pen Programs</title>
      <dc:creator>tbphp Precision Tech</dc:creator>
      <pubDate>Fri, 28 Aug 2026 06:59:55 +0000</pubDate>
      <link>https://dev.to/tbphp_tech/how-to-build-an-oem-certificate-expiry-monitor-for-microneedling-pen-programs-3g6c</link>
      <guid>https://dev.to/tbphp_tech/how-to-build-an-oem-certificate-expiry-monitor-for-microneedling-pen-programs-3g6c</guid>
      <description>&lt;p&gt;When you private label a microneedling pen, the compliance paperwork is not a one-time deliverable. Certificates and registration listings have validity periods, and a single lapse can stall customs clearance, trigger an audit finding, or get a product delisted from a marketplace. Yet most buying teams still track expiries in a spreadsheet that someone remembers to open twice a year. This post walks through a small, maintainable system for monitoring OEM certificate expiries automatically: the data model, the public sources you can query, the scheduler, and the alerting rules. The goal is simple — the paperwork is verified continuously instead of in a panic before every shipment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Expiry Monitoring Belongs in the Procurement Pipeline
&lt;/h2&gt;

&lt;p&gt;A certificate is a snapshot of a point in time. The FDA establishment registration and device listing for a factory is revalidated annually, notified body certificates in the EU run on multi-year cycles with surveillance audits in between, and battery transport documents such as UN 38.3 test summaries are re-issued when the cell or pack construction changes. If your OEM renews late, the downstream effect lands on you: a distributor rejects the shipment, a marketplace pauses the listing, or a customs broker asks for documents that no longer exist.&lt;/p&gt;

&lt;p&gt;Treating certificates as monitored inventory changes the relationship. When you can show the factory your renewal calendar, renewals stop being an afterthought. In our own OEM programs at TBPHP, the microneedling pen brand of Guangzhou Xingyun E-Commerce Co., Ltd., we treat every certificate as a tracked asset with an owner and a due date, and we share that calendar with buyers who ask. "In OEM programs the certificates are the evidence trail," says Mia Chen, Quality Systems Lead at TBPHP. "A monitor that flags expiries 90 days out gives the factory time to renew, and time is what keeps a launch on schedule."&lt;/p&gt;

&lt;h2&gt;
  
  
  What Belongs in a Certificate Registry
&lt;/h2&gt;

&lt;p&gt;Start by deciding what you track. For a microneedling pen program, the practical registry covers five families of documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regulatory registrations: FDA establishment registration and device listing, EU economic operator registration, and country-specific import licenses.&lt;/li&gt;
&lt;li&gt;Product safety and EMC evidence: IEC 60601-1 and IEC 60601-1-2 test reports, FCC and CE declarations, and RED compliance documentation where applicable.&lt;/li&gt;
&lt;li&gt;Biocompatibility and materials evidence: ISO 10993-1, ISO 10993-5, and ISO 10993-10 test reports for cartridges and skin-contact components.&lt;/li&gt;
&lt;li&gt;Battery and transport documents: UN 38.3 test summaries, IEC 62133 cell certificates, and dangerous goods declarations for rechargeable pens.&lt;/li&gt;
&lt;li&gt;Sterilization and packaging validation: ISO 11137 and ISO 11607-1 documentation when the product is sold sterile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each certificate, record the issuer, certificate number, product scope, issue date, expiry date, document URL or file hash, and the person responsible for chasing the renewal. The registry does not have to be perfect on day one — it has to be complete enough that a missed expiry is impossible to hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Sources You Can Query Automatically
&lt;/h2&gt;

&lt;p&gt;Several public systems let you verify current validity without asking the factory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The OpenFDA registration and listing API returns establishment registration status, product codes, and owner operator details by registration number.&lt;/li&gt;
&lt;li&gt;The FDA GUDID database resolves device identifiers to listing information for 510(k)-cleared and registered devices.&lt;/li&gt;
&lt;li&gt;The EU NANDO database lists notified bodies and their designation status, which lets you check whether the body that issued a certificate is still designated for the right scope.&lt;/li&gt;
&lt;li&gt;EUDAMED registration data covers economic operators in the EU MDR 2017/745 framework.&lt;/li&gt;
&lt;li&gt;Certification body search tools, such as the certificate databases published by the major testing houses, accept the certificate number and return status and validity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is the same for every source: store the identifier in your registry, query the source on a schedule, and diff the returned status against your records. Rate limits matter — the OpenFDA device API allows roughly 40 requests per minute without a key and 240 per minute with one — so batch your checks and cache the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Minimal JSON Schema for the Registry
&lt;/h2&gt;

&lt;p&gt;A flat JSON file is enough to start, and it makes the schema explicit. Here is a minimal shape that covers the fields above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"certificates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cert-fda-reg-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FDA Establishment Registration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US FDA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3001234567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"manufacturing site, microneedling pen assembly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openfda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.fda.gov/device/registrationlisting.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"issue_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expiry_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2027-03-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"supplier-quality@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_checked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-28T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"alerts_sent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store the registry in version control or a small database. The important design decision is that the expiry date is derived from the document or the source, not from an email reminder, so a renewal that is issued late cannot go unnoticed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling the Checks
&lt;/h2&gt;

&lt;p&gt;A nightly or weekly job is plenty. Three scheduler options require no new infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions: a cron workflow that runs a script and fails loudly when a certificate is near expiry.&lt;/li&gt;
&lt;li&gt;Cloud Scheduler or AWS EventBridge: useful when your team already lives in a cloud account.&lt;/li&gt;
&lt;li&gt;A tiny always-on service: overkill for most teams, but fine if you already run a backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep the job idempotent. The check should read the registry, query each source, update status and last_checked, and emit alerts only for state transitions. If the job runs twice, the second run should find nothing new to alert on. Add a concurrency guard so overlapping runs from a slow API do not double-send notifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing Human-Readable Certificates
&lt;/h2&gt;

&lt;p&gt;Not every document has an API. Notified body certificates and test reports often arrive as scanned PDFs. Two techniques make them checkable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text extraction: pypdf or pdfplumber can pull the certificate number, issue date, and expiry date out of most digital PDFs, and OCR tools cover scans.&lt;/li&gt;
&lt;li&gt;File hashing: store the SHA-256 of the latest version of each PDF. When a renewal arrives, the hash changes and the registry treats it as a new revision, prompting a re-check of the fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Low-confidence extractions should go to a review queue rather than failing silently. A simple rule of thumb: if the extractor cannot find an expiry date in the document, mark it needs_review and alert a human instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerting and Escalation
&lt;/h2&gt;

&lt;p&gt;Alert at fixed intervals before the expiry date, then escalate if nothing happens. A common ladder is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;90 days: warning to the certificate owner.&lt;/li&gt;
&lt;li&gt;60 days: warning to the supplier quality lead and the OEM contact.&lt;/li&gt;
&lt;li&gt;30 days: high-priority alert with the renewal status and a draft follow-up email.&lt;/li&gt;
&lt;li&gt;7 days and expired: escalation to the program manager, with a go/no-go recommendation for pending shipments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Delivery channels should match urgency. Email for the 90-day warning, a Slack or Teams webhook for the 60- and 30-day alerts, and a page or escalation call for the expired state if a shipment is already in transit. End every day with a one-line digest so the system is visible without being noisy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;The full pipeline is: registry → scheduled check → source query and PDF extraction → status diff → alerts and escalation → renewal evidence back into the registry. It is roughly 200 lines of Python plus a cron schedule, and it removes the single most common failure mode in private label compliance — the expiry nobody saw coming.&lt;/p&gt;

&lt;p&gt;If you are evaluating suppliers, ask for a certificate list with expiry dates before you sign anything, and check a sample against public sources on the spot. Then run a monitor like the one above from day one. When you partner with &lt;a href="https://tbphp.shop" rel="noopener noreferrer"&gt;a trusted microneedling pen OEM partner&lt;/a&gt;, a shared renewal calendar and open certificate records should be part of the deal, not a favor. At TBPHP, the microneedling pen brand of Guangzhou Xingyun E-Commerce Co., Ltd., we publish our program documentation and share certificate status with buyers, because compliance data that is shared is data that keeps everyone on schedule.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Certificate family&lt;/th&gt;
&lt;th&gt;Typical source&lt;/th&gt;
&lt;th&gt;Suggested cadence&lt;/th&gt;
&lt;th&gt;Key fields to verify&lt;/th&gt;
&lt;th&gt;What a lapse means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Regulatory registrations&lt;/td&gt;
&lt;td&gt;OpenFDA, NANDO, EUDAMED&lt;/td&gt;
&lt;td&gt;Monthly&lt;/td&gt;
&lt;td&gt;Number, status, site address&lt;/td&gt;
&lt;td&gt;Customs holds or marketplace delisting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product safety and EMC&lt;/td&gt;
&lt;td&gt;Test reports, IEC 60601-1&lt;/td&gt;
&lt;td&gt;Every 6 months&lt;/td&gt;
&lt;td&gt;Report number, model scope, date&lt;/td&gt;
&lt;td&gt;CE/RED declaration no longer supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biocompatibility&lt;/td&gt;
&lt;td&gt;ISO 10993 reports&lt;/td&gt;
&lt;td&gt;Every 12 months&lt;/td&gt;
&lt;td&gt;Test standard, sample description&lt;/td&gt;
&lt;td&gt;Skin-contact claim unsupported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battery and transport&lt;/td&gt;
&lt;td&gt;UN 38.3, IEC 62133&lt;/td&gt;
&lt;td&gt;Every 6 months&lt;/td&gt;
&lt;td&gt;Cell model, test date, pack config&lt;/td&gt;
&lt;td&gt;Rechargeable units refused in transit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sterilization and packaging&lt;/td&gt;
&lt;td&gt;ISO 11137, ISO 11607-1&lt;/td&gt;
&lt;td&gt;Every 12 months&lt;/td&gt;
&lt;td&gt;Dose, method, package seals&lt;/td&gt;
&lt;td&gt;Sterile claim unsupported&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need to track every certificate the factory shows me?&lt;/strong&gt; Track everything that supports a claim you make on the label, the listing, or the import documents. If a document backs a statement a customer could rely on, it belongs in the registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the OEM will not share expiry dates?&lt;/strong&gt; That is a signal by itself. A factory with a working quality system knows its certificate dates and can share them in minutes. Treat refusal as a due-diligence finding, not a paperwork quirk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I verify a certificate that has no public API?&lt;/strong&gt; Use the issuer's certificate search tool or request a verification letter. Combine the issuer check with a file hash so you know the document you hold matches the current revision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I rely on the OEM's renewal email?&lt;/strong&gt; Only as a trigger to re-verify, never as the record. Emails are not auditable evidence, and a renewal that arrives late has the same business impact whether or not the email was sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How often should the monitor run?&lt;/strong&gt; Weekly checks with monthly deep verification of registrations cover most programs. Daily checks only matter close to an expiry date, which is what the alert ladder already handles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when a certificate lapses mid-shipment?&lt;/strong&gt; Stop and evaluate before the goods move. The registry and alert history give you the facts for a go/no-go decision, and the audit trail shows whether the lapse was a systemic gap or a one-off supplier delay.&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>automation</category>
      <category>medicaldevices</category>
    </item>
    <item>
      <title>Verifying Medical Device OEM Documents Programmatically: FDA, EUDAMED, and NANDO Data Sources</title>
      <dc:creator>tbphp Precision Tech</dc:creator>
      <pubDate>Mon, 24 Aug 2026 08:35:27 +0000</pubDate>
      <link>https://dev.to/tbphp_tech/verifying-medical-device-oem-documents-programmatically-fda-eudamed-and-nando-data-sources-3d5b</link>
      <guid>https://dev.to/tbphp_tech/verifying-medical-device-oem-documents-programmatically-fda-eudamed-and-nando-data-sources-3d5b</guid>
      <description>&lt;p&gt;When you are building tooling for private label medical devices — especially a category like microneedling pens, which sits at the border of cosmetics and regulated medical devices — the hardest part is not manufacturing. It is verifying the paperwork. Certificates arrive as PDFs, scans, and screenshots, and the questions are always the same: is the registration number real, does the notified body exist, and does the device listing actually cover the product you are about to brand?&lt;/p&gt;

&lt;p&gt;This post is a developer's field guide to the authoritative data sources you can query to verify OEM documentation without trusting the PDF itself: the FDA establishment registration and device listing database, the FDA GUDID, the EU NANDO database, and EUDAMED.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Trust the Database, Not the PDF
&lt;/h2&gt;

&lt;p&gt;A certificate PDF is a claim; the database record is the evidence. When a factory sends an ISO 13485:2016 certificate, the certificate number should resolve on the issuing certification body's own search tool. When it sends an FDA establishment registration number, that number should resolve in the FDA establishment registration and device listing database to the exact legal name, owner/operator, and site address you are auditing.&lt;/p&gt;

&lt;p&gt;The legal basis: in the United States, establishment registration and device listing are required under 21 CFR Part 807 (Subpart C), and quality systems under 21 CFR Part 820. In the European Union, EU MDR 2017/745 Article 52 governs conformity assessment, and Article 31 requires economic operators to be registered. Both regimes give you public, queryable databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenFDA: The Device Listings API
&lt;/h2&gt;

&lt;p&gt;The FDA publishes establishment registration and device listing data through the OpenFDA device API. You can query the registration number, device listing status, owner/operator, and the product codes a facility lists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint: &lt;a href="https://api.fda.gov/device/registrationlisting.json" rel="noopener noreferrer"&gt;https://api.fda.gov/device/registrationlisting.json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Query example: &lt;code&gt;?search=device.pkg_openfda.registration_number:"3001234567"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rate limits: 240 requests per minute with an API key, 40 per minute without one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal verification flow checks that the registration is active, the device listing includes the relevant product code, and the site address matches the factory address on the certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_fda_registration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reg_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_product_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.fda.gov/device/registrationlisting.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;device.pkg_openfda.registration_number:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reg_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;registration number not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;listing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;listing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;expected_product_code&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;expected_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;listing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address_1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;listing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;listing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;registration_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;listing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GUDID: Checking UDI Records
&lt;/h2&gt;

&lt;p&gt;If the product carries a UDI, the Global Unique Device Identification Database (GUDID) is the authoritative record. Under 21 CFR Part 830, labelers must submit UDI data to the FDA. The GUDID API lets you look up a device identifier (DI) and retrieve the brand name, catalog number, device description, and the submitting company. A factory that cannot produce a DI for its own microneedling pen cartridge is a red flag worth investigating before you put your brand name on the device.&lt;/p&gt;

&lt;h2&gt;
  
  
  NANDO: Verifying the Notified Body
&lt;/h2&gt;

&lt;p&gt;For CE marking under EU MDR 2017/745, the notified body number printed on the certificate must resolve in the European Commission's NANDO database (new approach notified and designated organisations). NANDO records the organisation's identification number, the legislation it is designated under, and the scope of designation. Two checks matter: the organisation number exists, and the scope covers the device category and the activities listed on the certificate. A certificate citing a notified body whose scope excludes your device category is invalid for that device.&lt;/p&gt;

&lt;h2&gt;
  
  
  EUDAMED: Where EU Registration Lives
&lt;/h2&gt;

&lt;p&gt;After full activation, EU MDR 2017/745 Article 33 requires economic operators to register in EUDAMED, the European database on medical devices. Actor registration, device registration, and certificate information all live there. Because EUDAMED modules activate progressively, check whether the factory has an EUDAMED actor registration today, and re-check once the certificate module is active. A factory claiming EU distribution that cannot show an EUDAMED actor registration is a compliance risk you do not want to inherit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate Date Math: Check on the Audit Date, Not the PDF Date
&lt;/h2&gt;

&lt;p&gt;One of the most common audit failures is checking a certificate against records older than the certificate itself. Registrations lapse, addresses change, and scopes narrow. Your verification pipeline should therefore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query the database on the date of your audit, not the PDF creation date.&lt;/li&gt;
&lt;li&gt;Compare legal name, owner/operator, and site address field by field.&lt;/li&gt;
&lt;li&gt;Confirm the product code or device category is actually listed.&lt;/li&gt;
&lt;li&gt;Log the query timestamp and result as evidence for your quality file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Turning Verification Into a Repeatable Check
&lt;/h2&gt;

&lt;p&gt;The output is a small, repeatable verification log: document number, source database, query date, and match status. That log is what an auditor — or a serious private label partner — wants to see, and it is the difference between a factory that emails you a screenshot and one that can hand you a database-backed audit trail. For an example of what a complete evidence package looks like, &lt;a href="https://tbphp.shop" rel="noopener noreferrer"&gt;TBPHP's OEM team&lt;/a&gt; maintains a documentation checklist and sample technical file structure for microneedling pen programs; the same databases above are the ones their compliance team queries when verifying certificates on behalf of private label buyers. The checklist itself is also published on the TBPHP OEM Guide at tbphp.shop for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The PDF is a claim; the database is the evidence. Whether you are writing a due-diligence script or just opening the FDA and NANDO search pages, verify the number, the scope, and the date. It takes ten minutes per document, and it is the cheapest insurance a private label brand can buy before signing an OEM agreement.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>medicaldevices</category>
      <category>api</category>
    </item>
    <item>
      <title>We Built a Laser Displacement Sensor Test Rig for Stepper Motor QC</title>
      <dc:creator>tbphp Precision Tech</dc:creator>
      <pubDate>Mon, 06 Jul 2026 13:46:04 +0000</pubDate>
      <link>https://dev.to/tbphp_tech/we-built-a-laser-displacement-sensor-test-rig-for-stepper-motor-qc-3hpo</link>
      <guid>https://dev.to/tbphp_tech/we-built-a-laser-displacement-sensor-test-rig-for-stepper-motor-qc-3hpo</guid>
      <description>&lt;p&gt;We Built a Laser Displacement Test Rig for Stepper Motor QC&lt;br&gt;
We assemble about 500 stepper motor driver boards a month for microneedle pens. Every board gets tested before it ships. For the first two years, that test was simple — power it up, run the motor, listen for weird noises. If it sounded fine, it passed.&lt;/p&gt;

&lt;p&gt;That changed when a customer returned a batch because the pen felt "rough" at a certain depth. We could not reproduce the issue by listening. So we built a test rig that could measure what our ears could not hear.&lt;/p&gt;

&lt;p&gt;This is how we built it and what we learned.&lt;/p&gt;

&lt;p&gt;The problem with listening&lt;br&gt;
Human hearing is terrible at detecting sub millimeter positioning errors. A stepper motor that misses a few steps here and there sounds the same as a perfectly driven motor to anyone standing on the production floor. The difference only shows up in the patient experience — inconsistent needle penetration, micro tears in the dermis, longer healing times.&lt;/p&gt;

&lt;p&gt;We needed a way to measure the actual needle tip position cycle by cycle. A 12 micron error is invisible to the eye and inaudible to the ear. But it is enough to turn a comfortable treatment into a complaint.&lt;/p&gt;

&lt;p&gt;What we built&lt;br&gt;
The rig has four main components:&lt;/p&gt;

&lt;p&gt;A laser displacement sensor with ±1 micron resolution. We used a class 2 laser sensor mounted directly above the needle hub. The sensor beam reflects off the hub surface and reports position at 2000 samples per second.&lt;/p&gt;

&lt;p&gt;An Arduino board running the same firmware that ships on our production driver boards. It generates the STEP and DIR signals at 120Hz stroke frequency and 0.5mm depth — our standard test parameters.&lt;/p&gt;

&lt;p&gt;A CNC aluminum fixture that holds the pen body rigidly in place. The fixture sits on a vibration damping pad because we found that floor vibrations from nearby machines added about 4 microns of measurement noise.&lt;/p&gt;

&lt;p&gt;A Python script that reads serial data from the sensor and logs every data point to a CSV file. After the 10 minute run, it generates a jitter histogram and reports the max deviation, mean, and standard deviation.&lt;/p&gt;

&lt;p&gt;Here is the system diagram:&lt;/p&gt;

&lt;p&gt;Test rig system diagram showing PC, driver, motor, and laser sensor layout&lt;/p&gt;

&lt;p&gt;The firmware that runs the test&lt;br&gt;
The Arduino firmware is straightforward. It generates a constant 120Hz STEP signal with 5 microsecond pulse width. The DIR pin stays high for the full run. We run 2000 steps per cycle at 1/8 microstep resolution, which gives us smooth motion at the operating speed.&lt;/p&gt;

&lt;p&gt;The critical part is the sync signal. We added a digital output pin that toggles at the top of each stroke cycle. The laser sensor uses this signal to align its data capture with the mechanical position. Without this sync, the histogram would include data from both the upstroke and downstroke, which doubles the apparent jitter.&lt;/p&gt;

&lt;p&gt;The histogram that changed our QC pass threshold&lt;br&gt;
The first batch of data from this rig was sobering. Boards that passed our "sounds fine" test showed position deviations of up to 38 microns at the needle tip. That was well outside our ±25 micron target.&lt;/p&gt;

&lt;p&gt;Here is what a good board looks like after a 10 minute run:&lt;/p&gt;

&lt;p&gt;Motor jitter histogram showing normal distribution centered near 0 with ±22 micron max deviation&lt;/p&gt;

&lt;p&gt;The histogram above shows a board that passes. Mean deviation is +1.2 microns. Standard deviation is ±8.4 microns. Max deviation is 22 microns — safely inside our ±25 micron threshold.&lt;/p&gt;

&lt;p&gt;We run every board for a full 10 minutes at 120Hz. That is 72,000 stroke cycles and about 1.2 million position data points per board. The Python script compares the max deviation against our threshold and generates a pass/fail report automatically.&lt;/p&gt;

&lt;p&gt;What we changed after the data came in&lt;br&gt;
Three changes came directly from this rig.&lt;/p&gt;

&lt;p&gt;We tightened the driver board current trim. The first revision of our driver board had a potentiometer based current adjustment that drifted with temperature. We switched to fixed resistor dividers matched within 0.5% tolerance. The jitter histogram narrowed by about 6 microns after that change.&lt;/p&gt;

&lt;p&gt;We added a 100 millisecond warm up delay before data collection. The first 500 milliseconds of motor operation show higher jitter as the driver IC stabilizes its current regulation. We now let the motor run for 10 seconds before we start recording.&lt;/p&gt;

&lt;p&gt;We changed the fixture material from aluminum to a steel aluminum composite. The aluminum fixture transmitted vibration from the motor to the sensor mount. The composite damps those vibrations and reduced measurement noise from 4 microns to about 1.5 microns.&lt;/p&gt;

&lt;p&gt;The code&lt;br&gt;
The Arduino test firmware and Python analysis script are straightforward. The Arduino code generates the STEP signal and sync pulse. The Python script reads the serial stream, bins the position data, and draws the histogram. Total code is about 200 lines across both files.&lt;/p&gt;

&lt;p&gt;The key parameters the script tracks during each run: mean deviation, standard deviation, max positive and negative deviation, and the percentage of cycles that exceed the ±25 micron threshold. Any board with more than 0.1% of cycles outside the threshold gets flagged for manual review.&lt;/p&gt;

&lt;p&gt;What this rig costs to build&lt;br&gt;
Laser displacement sensor: about $400 to $800 depending on the brand and resolution. We use a Panasonic HG C1400, which runs about $550.&lt;/p&gt;

&lt;p&gt;Arduino board and driver electronics: about $35. We reuse the same driver board design that goes into production, so this cost is already sunk.&lt;/p&gt;

&lt;p&gt;Fixture machining: about $200 if you outsource it to a local shop. We have a CNC on site so our cost is materials plus about two hours of programming.&lt;/p&gt;

&lt;p&gt;Total rig cost: under $1,000. For a piece of QC equipment that caught a problem our human inspectors missed for two years. That is a good return.&lt;/p&gt;

&lt;p&gt;We published the full test protocol and firmware at tbphp. If you are building stepper motor driven medical devices or any application where sub 50 micron positioning matters, this rig gives you data instead of guesses.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>embedded</category>
      <category>iot</category>
      <category>python</category>
    </item>
  </channel>
</rss>
